Windows-Server-2003/shell/shell32/fstree.cpp

1946 lines
76 KiB
C++

#include "shellprv.h"
#include "filefldr.h"
#include <shellp.h>
#include <shguidp.h>
#include "idlcomm.h"
#include "pidl.h"
#include "views.h"
#include "ids.h"
#include "shitemid.h"
#include "datautil.h"
#include "prop.h"
#include "basefvcb.h"
#include "brutil.h"
#include "enumuicommand.h"
#include "enumidlist.h"
#include "wia.h"
#include "shimgvw.h"
#include "cdburn.h"
#include "foldertypes.h"
#include "htmlhelp.h"
#include "buytasks.h"
#include <crypto\md5.h> // for MD5DIGESTLEN
const SHOP_INFO c_BuySampleMusic = { L"BuyURL", L"http://go.microsoft.com/fwlink/?LinkId=730&clcid={SUB_CLCID}", FALSE};
const SHOP_INFO c_BuyMusic = { L"MusicBuyURL", L"http://go.microsoft.com/fwlink/?LinkId=493&clcid={SUB_CLCID}", TRUE};
const SHOP_INFO c_BuySamplePictures = { L"BuyURL", L"http://go.microsoft.com/fwlink/?LinkId=625&clcid={SUB_CLCID}", TRUE};
class CFSFolderViewCB : public CBaseShellFolderViewCB
{
public:
CFSFolderViewCB(CFSFolder *pfsf);
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
STDMETHODIMP SetSite(IUnknown* pUnkSite);
private:
~CFSFolderViewCB();
HRESULT OnSize(DWORD pv, UINT cx, UINT cy);
HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane);
HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax);
HRESULT OnWindowCreated(DWORD pv, HWND wP);
HRESULT OnInsertDeleteItem(int iMul, LPCITEMIDLIST wP);
HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP);
HRESULT OnUpdateStatusBar(DWORD pv, BOOL wP);
HRESULT OnRefresh(DWORD pv, BOOL fPreRefresh);
HRESULT OnSelectAll(DWORD pv);
HRESULT OnGetWorkingDir(DWORD pv, UINT wP, LPTSTR lP);
HRESULT OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST* rgpidl);
HRESULT OnGetViewData(DWORD pv, UINT uViewMode, SFVM_VIEW_DATA* pvi);
HRESULT OnGetWebViewTemplate(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_TEMPLATE_DATA* pvit);
HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
HRESULT OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme);
HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE*lP);
HRESULT OnGetCustomViewInfo(DWORD pv, SFVM_CUSTOMVIEWINFO_DATA* pData);
HRESULT OnSupportsIdentity(DWORD pv);
HRESULT OnQueryReuseExtView(DWORD pv, BOOL *pfReuseAllowed);
HRESULT OnGetNotify(DWORD pv, LPITEMIDLIST*wP, LONG*lP);
HRESULT OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings);
BOOL _CollectDefaultFolderState();
PERCEIVED _GetFolderPerceivedType(LPCIDFOLDER pidf);
HRESULT _GetStringForFolderType(int iType, LPWSTR pszFolderType, UINT cchBuf);
BOOL _IsBarricadedFolder();
UINT _cItems;
FSSELCHANGEINFO _fssci;
CFSFolder* _pfsf;
BOOL _fStatusInitialized;
TRIBIT _fHasWIADevices;
IPreview3 * _pPreview;
HRESULT _GetPreview3(IPreview3** ppPreview3);
HRESULT _GetShoppingBrowsePidl(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, const SHOP_INFO *pShopInfo, LPITEMIDLIST *ppidl);
HRESULT _GetShoppingURL(const SHOP_INFO *pShopInfo, LPTSTR pszURL, DWORD cchURL);
HRESULT _DataObjectFromItemsOrFolder(IShellItemArray *psiItemArray, IDataObject **ppdto);
public:
// webview task implementations:
static HRESULT _HasWiaDevices(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _HasItems(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanBuyPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanPlayMusic(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanPlayVideos(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _CanSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
static HRESULT _OnCommonDocumentsHelp(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnPlayMusic(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnPlayVideos(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnShopForMusicOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnShopForPicturesOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnGetFromCamera(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnSlideShow(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _OnSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
static HRESULT _CanPlay(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState, int fDATAOBJCB);
static HRESULT _OnPlay(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, int fDATAOBJCB);
};
#define FS_EVENTS (SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_NETSHARE | SHCNE_NETUNSHARE)
CFSFolderViewCB::CFSFolderViewCB(CFSFolder *pfsf) : CBaseShellFolderViewCB(pfsf->_pidl, FS_EVENTS), _pfsf(pfsf)
{
_pfsf->AddRef();
ZeroMemory(&_fssci, sizeof(_fssci));
// _fssci.szDrive[0] == '\0' means "unknown" / "not available"
_fssci.cbFree = -1; // this field uses -1 to mean
// "unknown" / "not available"
_pPreview = NULL;
ASSERT(!_fStatusInitialized);
}
CFSFolderViewCB::~CFSFolderViewCB()
{
if (_pPreview)
{
IUnknown_SetSite(_pPreview, NULL);
_pPreview->Release();
}
_pfsf->Release();
}
STDMETHODIMP CFSFolderViewCB::SetSite(IUnknown* punkSite)
{
if (_pPreview)
{
IUnknown_SetSite(_pPreview, punkSite);
}
return CBaseShellFolderViewCB::SetSite(punkSite);
}
HRESULT CFSFolderViewCB::OnSize(DWORD pv, UINT cx, UINT cy)
{
ResizeStatus(_punkSite, cx);
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
{
if (PANE_ZONE == dwPaneID)
*pdwPane = 2;
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax)
{
TCHAR szName[MAX_PATH];
if (SUCCEEDED(DisplayNameOf(_pfsf, pidlItem, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName))))
{
_pfsf->GetMaxLength(szName, (int *)pcchMax);
}
return S_OK;
}
HRESULT CFSFolderViewCB::OnWindowCreated(DWORD pv, HWND wP)
{
if (SUCCEEDED(_pfsf->_GetPath(_fssci.szDrive, ARRAYSIZE(_fssci.szDrive))))
{
_fssci.cbFree = -1; // not known yet
if (!_fStatusInitialized)
{
InitializeStatus(_punkSite);
_fStatusInitialized = TRUE;
}
return S_OK;
}
return E_FAIL;
}
HRESULT CFSFolderViewCB::OnInsertDeleteItem(int iMul, LPCITEMIDLIST wP)
{
ViewInsertDeleteItem(_pfsf, &_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)
{
_pfsf->_bUpdateExtendedCols = TRUE;
}
return S_OK;
}
HRESULT CFSFolderViewCB::OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP)
{
ViewSelChange(_pfsf, lP, &_fssci);
return S_OK;
}
HRESULT CFSFolderViewCB::OnUpdateStatusBar(DWORD pv, BOOL wP)
{
if (!_fStatusInitialized)
{
InitializeStatus(_punkSite);
_fStatusInitialized = TRUE;
}
// if initializing, force refresh of disk free space
if (wP)
_fssci.cbFree = -1;
return ViewUpdateStatusBar(_punkSite, _pidl, &_fssci);
}
HRESULT CFSFolderViewCB::OnRefresh(DWORD pv, BOOL fPreRefresh)
{
// pre refresh...
if (fPreRefresh)
{
_fHasWIADevices = TRIBIT_UNDEFINED; // so we re-query
}
else
{
_fssci.cHiddenFiles = _pfsf->_cHiddenFiles;
_fssci.cbSize = _pfsf->_cbSize;
}
return S_OK;
}
HRESULT CFSFolderViewCB::OnSelectAll(DWORD pv)
{
HRESULT hr = S_OK;
if (_fssci.cHiddenFiles > 0)
{
if (ShellMessageBox(HINST_THISDLL, _hwndMain,
MAKEINTRESOURCE(IDS_SELECTALLBUTHIDDEN),
MAKEINTRESOURCE(IDS_SELECTALL), MB_OKCANCEL | MB_SETFOREGROUND | MB_ICONWARNING,
_fssci.cHiddenFiles) == IDCANCEL)
{
hr = S_FALSE;
}
}
return hr;
}
HRESULT CFSFolderViewCB::OnGetWorkingDir(DWORD pv, UINT wP, LPTSTR lP)
{
return _pfsf->_GetPath(lP, MAX_PATH); // assumed buffer size! possible overflow.
}
HRESULT CFSFolderViewCB::_HasWiaDevices(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
if (TRIBIT_UNDEFINED == pThis->_fHasWIADevices && fOkToBeSlow)
{
pThis->_fHasWIADevices = TRIBIT_FALSE;
// strings stolen from stiregi.h
// REGSTR_PATH_SOFT_STI, REGSTR_VAL_WIA_PRESEN
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\StillImage"),
TEXT("WIADevicePresent"), NULL, NULL, NULL))
{
IWiaDevMgr* pwia;
if (SUCCEEDED(CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_NO_FAILURE_LOG, IID_PPV_ARG(IWiaDevMgr, &pwia))))
{
IEnumWIA_DEV_INFO* penum;
if (S_OK == pwia->EnumDeviceInfo(0, &penum))
{
ULONG cItems;
if ((S_OK == penum->GetCount(&cItems)) &&
cItems > 0)
{
pThis->_fHasWIADevices = TRIBIT_TRUE;
}
penum->Release();
}
pwia->Release();
}
}
}
*puisState = (TRIBIT_TRUE == pThis->_fHasWIADevices) ? UIS_ENABLED : UIS_HIDDEN;
return TRIBIT_UNDEFINED == pThis->_fHasWIADevices ? E_PENDING : S_OK;
}
HRESULT CFSFolderViewCB::_HasItems(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
*puisState = UIS_ENABLED;
if (!psiItemArray)
{
// empty folders don't want this task
*puisState = UIS_DISABLED;
IFolderView* pfv;
IDataObject *pdo;
if (pThis->_punkSite && SUCCEEDED(pThis->_punkSite->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
{
if (SUCCEEDED(pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IDataObject, &pdo))))
{
*puisState = UIS_ENABLED;
pdo->Release();
}
pfv->Release();
}
}
return S_OK;
}
// Image options
#define IMAGEOPTION_CANROTATE 0x00000001
#define IMAGEOPTION_CANWALLPAPER 0x00000002
HRESULT CFSFolderViewCB::_CanWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
*puisState = UIS_DISABLED;
IDataObject *pdo;
if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo))))
{
LPITEMIDLIST pidl;
if (SUCCEEDED(PidlFromDataObject(pdo, &pidl))) // could get this dircetly from ShellItemArray
{
IAssociationArray *paa;
if (SUCCEEDED(SHGetUIObjectOf(pidl, NULL, IID_PPV_ARG(IAssociationArray, &paa))))
{
DWORD dwFlags, cb = sizeof(dwFlags);
if (SUCCEEDED(paa->QueryDword(ASSOCELEM_MASK_QUERYNORMAL, AQN_NAMED_VALUE, L"ImageOptionFlags", &dwFlags)) &&
(dwFlags & IMAGEOPTION_CANWALLPAPER))
{
*puisState = UIS_ENABLED;
}
paa->Release();
}
ILFree(pidl);
}
pdo->Release();
}
return S_OK;
}
enum
{
DATAOBJCB_IMAGE = 0x1,
DATAOBJCB_MUSIC = 0x2,
DATAOBJCB_VIDEO = 0x4,
DATAOBJCB_ONLYCHECKEXISTENCE = 0x80000000
};
class CDataObjectCallback : public INamespaceWalkCB
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// INamespaceWalkCB
STDMETHODIMP FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl);
STDMETHODIMP EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl);
STDMETHODIMP LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl);
STDMETHODIMP InitializeProgressDialog(LPWSTR *ppszTitle, LPWSTR *ppszCancel);
CDataObjectCallback(DWORD dwFlags);
BOOL Found();
private:
DWORD _dwFlags;
BOOL _fAlreadyFound;
};
STDMETHODIMP_(ULONG) CDataObjectCallback::AddRef()
{
return 3;
}
STDMETHODIMP_(ULONG) CDataObjectCallback::Release()
{
return 2;
}
CDataObjectCallback::CDataObjectCallback(DWORD dwFlags)
{
_dwFlags = dwFlags;
_fAlreadyFound = FALSE;
}
BOOL CDataObjectCallback::Found()
{
return _fAlreadyFound;
}
STDMETHODIMP CDataObjectCallback::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CDataObjectCallback, INamespaceWalkCB),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP CDataObjectCallback::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl)
{
// a slight misuse of the walker -- we bail out early if we know we've already found
// what we're looking for
if ((_dwFlags & DATAOBJCB_ONLYCHECKEXISTENCE) && _fAlreadyFound)
return E_FAIL;
PERCEIVED gen = GetPerceivedType(psf, pidl);
if ((_dwFlags & DATAOBJCB_IMAGE) && (gen == GEN_IMAGE) ||
(_dwFlags & DATAOBJCB_MUSIC) && (gen == GEN_AUDIO) ||
(_dwFlags & DATAOBJCB_VIDEO) && (gen == GEN_VIDEO))
{
if (_dwFlags & DATAOBJCB_ONLYCHECKEXISTENCE)
{
_fAlreadyFound = TRUE;
}
return S_OK;
}
return S_FALSE;
}
STDMETHODIMP CDataObjectCallback::EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
{
if ((_dwFlags & DATAOBJCB_ONLYCHECKEXISTENCE) && _fAlreadyFound)
return E_FAIL;
return S_OK;
}
STDMETHODIMP CDataObjectCallback::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
{
return S_OK;
}
STDMETHODIMP CDataObjectCallback::InitializeProgressDialog(LPWSTR *ppszTitle, LPWSTR *ppszCancel)
{
*ppszCancel = NULL; // use default
TCHAR szMsg[128];
LoadString(HINST_THISDLL, IDS_WALK_PROGRESS_TITLE, szMsg, ARRAYSIZE(szMsg));
return SHStrDup(szMsg, ppszTitle);
}
HRESULT InvokeVerbsOnItems(HWND hwndOwner, const LPCSTR rgszVerbs[], UINT cVerbs, LPITEMIDLIST *ppidls, UINT cItems)
{
IContextMenu *pcm;
HRESULT hr = SHGetUIObjectFromFullPIDL(ppidls[0], NULL, IID_PPV_ARG(IContextMenu, &pcm));
if (SUCCEEDED(hr))
{
ITEMIDLIST id = {0};
IDataObject *pdtobj;
hr = SHCreateFileDataObject(&id, cItems, (LPCITEMIDLIST *)ppidls, NULL, &pdtobj);
if (SUCCEEDED(hr))
{
IShellExtInit *psei;
hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei));
if (SUCCEEDED(hr))
{
psei->Initialize(NULL, pdtobj, NULL);
psei->Release();
}
pdtobj->Release();
}
hr = SHInvokeCommandsOnContextMenu(hwndOwner, NULL, pcm, 0, rgszVerbs, cVerbs);
pcm->Release();
}
return hr;
}
HRESULT PlayFromUnk(IUnknown *punk, HWND hwndOwner, int fDATAOBJCB)
{
INamespaceWalk *pnsw;
HRESULT hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
if (SUCCEEDED(hr))
{
CDataObjectCallback cb(fDATAOBJCB);
hr = pnsw->Walk(punk, NSWF_NONE_IMPLIES_ALL | NSWF_ONE_IMPLIES_ALL | NSWF_SHOW_PROGRESS | NSWF_FLAG_VIEWORDER, 10, &cb);
if (SUCCEEDED(hr))
{
UINT cItems;
LPITEMIDLIST *ppidls;
hr = pnsw->GetIDArrayResult(&cItems, &ppidls);
if (SUCCEEDED(hr))
{
if (cItems)
{
const LPCSTR c_rgszVerbs[] = { "Play", "Open" };
hr = InvokeVerbsOnItems(hwndOwner, c_rgszVerbs, ARRAYSIZE(c_rgszVerbs), ppidls, cItems);
}
else
{
ShellMessageBox(
HINST_THISDLL,
hwndOwner,
MAKEINTRESOURCE(IDS_PLAYABLEFILENOTFOUND),
NULL,
MB_OK | MB_ICONERROR);
hr = S_FALSE;
}
FreeIDListArray(ppidls, cItems);
}
}
pnsw->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_OnCommonDocumentsHelp(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
SHELLEXECUTEINFO sei = { 0 };
sei.cbSize = sizeof(sei);
sei.fMask = 0;
sei.hwnd = ((CFSFolderViewCB*)(void*)pv)->_hwndMain;
sei.nShow = SW_SHOWNORMAL;
sei.lpFile = L"hcp://services/subsite?node=TopLevelBucket_2/Networking_and_the_Web&topic=MS-ITS%3A%25HELP_LOCATION%25%5Cfilefold.chm%3A%3A/using_shared_documents_folder.htm&select=TopLevelBucket_2/Networking_and_the_Web/Sharing_files__printers__and_other_resources";
return ShellExecuteEx(&sei) ? S_OK : E_FAIL;
}
HRESULT CFSFolderViewCB::_CanOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
// TODO: Use fOkToBeSlow (with a return of E_PENDING) to allow walk to
// occur on a background task thread (for performance reasons). However,
// it doesn't work at present because it's completely specialized for WIA
// stuff, and it will not be trivial to adapt to the general case. Thus,
// we make assumptions as best we can in determining the state for now.
*puisState = UIS_DISABLED;
if (SHRestricted(REST_NOONLINEPRINTSWIZARD))
{
// bail out early with UIS_HIDDEN, we dont show the verb
return S_OK;
}
IDataObject *pdo = NULL;
HRESULT hr = psiItemArray ? psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)) : S_OK;
if (SUCCEEDED(hr))
{
if (pThis->_fssci.nItems > 0) // Files selected. Determine if any images...
{
INamespaceWalk *pnsw;
hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
if (SUCCEEDED(hr))
{
CDataObjectCallback cb(DATAOBJCB_IMAGE | DATAOBJCB_ONLYCHECKEXISTENCE);
pnsw->Walk(psiItemArray ? pdo : pThis->_punkSite, NSWF_NONE_IMPLIES_ALL | NSWF_DONT_ACCUMULATE_RESULT, 0, &cb);
if (cb.Found())
{
*puisState = UIS_ENABLED;
}
pnsw->Release();
}
}
else
{
*puisState = UIS_ENABLED; // No files selected. Assume image files exist.
hr = S_OK; // Note we "assume" for the TODO perf reason above.
}
ATOMICRELEASE(pdo);
}
return hr;
}
HRESULT CFSFolderViewCB::_CanPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
HRESULT hr;
// TODO: Use fOkToBeSlow (with a return of E_PENDING) to allow walk to
// occur on a background task thread (for performance reasons). However,
// it doesn't work at present because it's completely specialized for WIA
// stuff, and it will not be trivial to adapt to the general case. Thus,
// we make assumptions as best we can in determining the state for now.
if (psiItemArray)
{
*puisState = UIS_DISABLED;
IDataObject *pdo;
hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo));
if (SUCCEEDED(hr))
{
INamespaceWalk *pnsw;
hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
if (SUCCEEDED(hr))
{
CDataObjectCallback cb(DATAOBJCB_IMAGE | DATAOBJCB_ONLYCHECKEXISTENCE);
pnsw->Walk(pdo, NSWF_DONT_ACCUMULATE_RESULT, 0, &cb);
if (cb.Found())
{
*puisState = UIS_ENABLED;
}
pnsw->Release();
}
pdo->Release();
}
}
else
{
*puisState = UIS_ENABLED; // No files selected. Assume image files exist.
hr = S_OK; // Note we "assume" for the TODO perf reason above.
}
return hr;
}
HRESULT CFSFolderViewCB::_CanBuyPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
*puisState = UIS_DISABLED;
// If there is a BuyURL in the desktop.ini, then we'll show the buy pictures task.
WCHAR szIniPath[MAX_PATH];
if (pThis->_pfsf->_CheckDefaultIni(NULL, szIniPath, ARRAYSIZE(szIniPath)) && PathFileExistsAndAttributes(szIniPath, NULL))
{
WCHAR szURLArguments[MAX_PATH];
if (GetPrivateProfileString(L".ShellClassInfo", c_BuySamplePictures.szURLKey, L"", szURLArguments, ARRAYSIZE(szURLArguments), szIniPath))
{
// Note:
// String validation does not occur here (by design). This is
// simply an "existance" check. Validation will occur only if
// the user actually clicks on this task and we need to execute.
// Yes - there's something.
*puisState = UIS_ENABLED;
}
}
return S_OK;
}
HRESULT CFSFolderViewCB::_CanPlayMusic(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
return _CanPlay(pv, psiItemArray, fOkToBeSlow, puisState, DATAOBJCB_MUSIC | DATAOBJCB_VIDEO);
}
HRESULT CFSFolderViewCB::_CanPlayVideos(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
return _CanPlay(pv, psiItemArray, fOkToBeSlow, puisState, DATAOBJCB_VIDEO);
}
HRESULT CFSFolderViewCB::_CanPlay(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState, int fDATAOBJCB)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
*puisState = UIS_DISABLED;
// TODO: Use fOkToBeSlow (with a return of E_PENDING) to allow walk to
// occur on a background task thread (for performance reasons). However,
// it doesn't work at present because it's completely specialized for WIA
// stuff, and it will not be trivial to adapt to the general case. Thus,
// we make assumptions as best we can in determining the state for now.
IDataObject *pdo = NULL;
HRESULT hr = psiItemArray ? psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)) : S_OK;
if (SUCCEEDED(hr))
{
RIPMSG(!psiItemArray || pdo, "CFSFolderViewCB::_CanPlay - BindToHandler returned S_OK but NULL pdo");
RIPMSG(psiItemArray || pThis->_punkSite, "CFSFolderViewCB::_CanPlay - no _punkSite!");
if (pThis->_fssci.cFiles > 0)
{
if (pThis->_fssci.nItems > 0) // Files selected. Determine if any playable...
{
INamespaceWalk *pnsw;
hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
if (SUCCEEDED(hr))
{
CDataObjectCallback cb(fDATAOBJCB | DATAOBJCB_ONLYCHECKEXISTENCE);
pnsw->Walk(psiItemArray ? pdo : pThis->_punkSite, NSWF_DONT_ACCUMULATE_RESULT, 4, &cb);
if (cb.Found())
{
*puisState = UIS_ENABLED;
}
pnsw->Release();
}
}
else
*puisState = UIS_ENABLED; // No files selected. Assume playable files exist.
} // Note we "assume" for the TODO perf reason above.
ATOMICRELEASE(pdo);
}
return hr;
}
HRESULT CFSFolderViewCB::_OnPlayMusic(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
return _OnPlay(pv, psiItemArray, pbc, DATAOBJCB_MUSIC | DATAOBJCB_VIDEO);
}
HRESULT CFSFolderViewCB::_OnPlayVideos(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
return _OnPlay(pv, psiItemArray, pbc, DATAOBJCB_VIDEO);
}
HRESULT CFSFolderViewCB::_OnPlay(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, int fDATAOBJCB)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
HRESULT hr;
if (psiItemArray)
{
IDataObject *pdo;
hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo));
if (SUCCEEDED(hr))
{
hr = PlayFromUnk(pdo, pThis->_hwndMain, fDATAOBJCB);
pdo->Release();
}
}
else
{
hr = PlayFromUnk(pThis->_punkSite, pThis->_hwndMain, fDATAOBJCB);
}
return hr;
}
HRESULT CFSFolderViewCB::_GetShoppingURL(const SHOP_INFO *pShopInfo, LPTSTR pszURL, DWORD cchURL)
{
HRESULT hr = URLSubstitution(pShopInfo->szURLPrefix, pszURL, cchURL, URLSUB_CLCID);
if (SUCCEEDED(hr))
{
WCHAR szIniPath[MAX_PATH];
// If we can't just use the fwlink with no arguments, then assume failure.
hr = pShopInfo->bUseDefault ? S_OK : E_FAIL;
if (_pfsf->_CheckDefaultIni(NULL, szIniPath, ARRAYSIZE(szIniPath)) && PathFileExistsAndAttributes(szIniPath, NULL))
{
WCHAR szURLArguments[MAX_PATH];
if (GetPrivateProfileString(L".ShellClassInfo", pShopInfo->szURLKey, L"", szURLArguments, ARRAYSIZE(szURLArguments), szIniPath))
{
// Note:
// All URL's are read from hard-coded strings in the code
// base, and are of the form:
//
// http://go.microsoft.com/fwlink/?LinkId=730&clcid={SUB_CLCID}
//
// The desktop.ini simply offers an avenue to add additional
// arguments onto the end of the URL to refine the redirect.
// We do not validate these arguments here, because it is
// assumed the fwlink service is robust enough to handle bad
// input. If it wasn't, than anyone could type a bad fwlink
// URL in their address bar and wreck havoc on the fwlink
// service.
StringCchCat(pszURL, cchURL, L"&");
StringCchCat(pszURL, cchURL, szURLArguments);
// Got some arguments - we're definitely ok.
hr = S_OK;
}
}
}
return hr;
}
HRESULT CFSFolderViewCB::_GetShoppingBrowsePidl(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, const SHOP_INFO *pShopInfo, LPITEMIDLIST *ppidl)
{
WCHAR wszShoppingURL[MAX_URL_STRING];
HRESULT hr = _GetShoppingURL(pShopInfo, wszShoppingURL, ARRAYSIZE(wszShoppingURL));
if (SUCCEEDED(hr))
{
IShellFolder *psfDesktop;
hr = SHGetDesktopFolder(&psfDesktop);
if (SUCCEEDED(hr))
{
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszShoppingURL, NULL, ppidl, NULL);
psfDesktop->Release();
}
}
return hr;
}
HRESULT CFSFolderViewCB::_OnShopForMusicOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
LPITEMIDLIST pidl;
// See if there is a sample music BuyURL
// (do this check first, because the regular music buy URL should always succeed)
HRESULT hr = pThis->_GetShoppingBrowsePidl(pv, psiItemArray, pbc, &c_BuySampleMusic, &pidl);
if (SUCCEEDED(hr))
{
hr = pThis->_BrowseObject(pidl, SBSP_NEWBROWSER);
ILFree(pidl);
}
else
{
// Nope - look for the regular music buy URL
hr = pThis->_GetShoppingBrowsePidl(pv, psiItemArray, pbc, &c_BuyMusic, &pidl);
if (SUCCEEDED(hr))
{
hr = pThis->_BrowseObject(pidl, SBSP_NEWBROWSER);
ILFree(pidl);
}
}
return hr;
}
HRESULT CFSFolderViewCB::_OnShopForPicturesOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
WCHAR wszShoppingURL[MAX_URL_STRING];
HRESULT hr = pThis->_GetShoppingURL(&c_BuySamplePictures, wszShoppingURL, ARRAYSIZE(wszShoppingURL));
if (SUCCEEDED(hr))
{
HINSTANCE hinstRet = ShellExecute(NULL, NULL, wszShoppingURL, NULL, NULL, SW_SHOWNORMAL);
hr = ((UINT_PTR)hinstRet) <= 32 ? E_FAIL : S_OK;
}
return hr;
}
HRESULT CFSFolderViewCB::_DataObjectFromItemsOrFolder(IShellItemArray *psiItemArray, IDataObject **ppdto)
{
*ppdto = NULL;
HRESULT hr;
if (psiItemArray)
{
// Something selected -- work with selected items.
hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, ppdto));
}
else
{
// Nothing selected -- imply folder selected.
hr = SHGetUIObjectOf(_pidl, NULL, IID_PPV_ARG(IDataObject, ppdto));
}
return hr;
}
HRESULT CFSFolderViewCB::_CanSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
*puisState = UIS_DISABLED;
IDataObject *pdo;
HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo);
if (SUCCEEDED(hr))
{
// todo: use fOkToBeSlow to get off the UI thread -- right now it wont work because
// its specialized just for the WIA stuff and things that have global state
ICDBurn *pcdb;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ICDBurn, &pcdb))))
{
// media player will get invoked, so we only worry about if the system has a
// recordable drive at all -- whether the shell burning is enabled or not doesnt matter
BOOL fHasRecorder;
if (SUCCEEDED(pcdb->HasRecordableDrive(&fHasRecorder)) && fHasRecorder)
{
IUnknown *punk;
// if this probe works, we can get something thats good to go and itll burn cds.
if (SUCCEEDED(CDBurn_GetExtensionObject(CDBE_TYPE_MUSIC, pdo, IID_PPV_ARG(IUnknown, &punk))))
{
*puisState = UIS_ENABLED;
punk->Release();
}
}
pcdb->Release();
}
pdo->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_OnSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
IDataObject *pdo;
HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo);
if (SUCCEEDED(hr))
{
IDropTarget *pdt;
hr = CDBurn_GetExtensionObject(CDBE_TYPE_MUSIC, pdo, IID_PPV_ARG(IDropTarget, &pdt));
if (SUCCEEDED(hr))
{
hr = SHSimulateDrop(pdt, pdo, 0, NULL, NULL);
pdt->Release();
}
pdo->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_CanSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
{
*puisState = UIS_DISABLED;
WCHAR szDrive[4];
if (SUCCEEDED(CDBurn_GetRecorderDriveLetter(szDrive, ARRAYSIZE(szDrive))))
{
// if this succeeds, shell cd burning is enabled.
*puisState = UIS_ENABLED;
}
return S_OK;
}
HRESULT CFSFolderViewCB::_OnSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
IDataObject *pdo;
HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo);
if (SUCCEEDED(hr))
{
WCHAR szDrive[4];
hr = CDBurn_GetRecorderDriveLetter(szDrive, ARRAYSIZE(szDrive));
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidl;
hr = SHILCreateFromPath(szDrive, &pidl, NULL);
if (SUCCEEDED(hr))
{
IDropTarget *pdt;
hr = SHGetUIObjectOf(pidl, NULL, IID_PPV_ARG(IDropTarget, &pdt));
if (SUCCEEDED(hr))
{
hr = SHSimulateDropWithSite(pdt, pdo, 0, NULL, NULL, pThis->_punkSite);
pdt->Release();
}
ILFree(pidl);
}
}
pdo->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_OnGetFromCamera(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc)
{
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_DOENVSUBST;
sei.hwnd = ((CFSFolderViewCB*)(void*)pv)->_hwndMain;
sei.lpFile = TEXT("%SystemRoot%\\System32\\wiaacmgr.exe");
sei.lpParameters = TEXT("/SelectDevice");
sei.nShow = SW_SHOWNORMAL;
return ShellExecuteEx(&sei) ? S_OK : E_FAIL;
}
HRESULT CFSFolderViewCB::_GetPreview3(IPreview3** ppPreview3)
{
HRESULT hr = E_FAIL;
*ppPreview3 = NULL;
if (!_pPreview)
{
hr = CoCreateInstance(CLSID_Preview, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPreview3, &_pPreview));
if (SUCCEEDED(hr))
{
IUnknown_SetSite(_pPreview, _punkSite);
}
}
if (_pPreview)
{
*ppPreview3 = _pPreview;
_pPreview->AddRef();
hr = S_OK;
}
return hr;
}
HRESULT CFSFolderViewCB::_OnSlideShow(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
IPreview3* pPreview3;
HRESULT hr = pThis->_GetPreview3(&pPreview3);
if (SUCCEEDED(hr))
{
hr = pPreview3->SlideShow();
pPreview3->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_OnWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
HRESULT hr = E_FAIL;
IDataObject *pdo;
if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo))))
{
IPreview3* pPreview3;
if (SUCCEEDED(pThis->_GetPreview3(&pPreview3)))
{
TCHAR szPath[MAX_PATH];
if (SUCCEEDED(PathFromDataObject(pdo, szPath, ARRAYSIZE(szPath))))
{
hr = pPreview3->SetWallpaper(szPath);
}
pPreview3->Release();
}
pdo->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_OnOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
IDataObject *pdo;
HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo);
if (SUCCEEDED(hr))
{
hr = SHSimulateDropOnClsid(CLSID_InternetPrintOrdering, pThis->_punkSite, pdo);
pdo->Release();
}
return hr;
}
HRESULT CFSFolderViewCB::_OnPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
{
CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv;
IDataObject *pdo;
HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo);
if (SUCCEEDED(hr))
{
hr = SHSimulateDropOnClsid(CLSID_PrintPhotosDropTarget, pThis->_punkSite, pdo);
pdo->Release();
}
return hr;
}
const WVTASKITEM c_CommonDocumentsSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_COMMONDOCUMENTS, IDS_HEADER_COMMONDOCUMENTS_TT);
const WVTASKITEM c_CommonDocumentsSpecialTaskList[] =
{
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_COMMONDOCUMENTSHELP, IDS_TASK_COMMONDOCUMENTSHELP_TT, IDI_TASK_HELP, NULL, CFSFolderViewCB::_OnCommonDocumentsHelp),
};
const LPCTSTR c_DocumentsOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
const WVTASKITEM c_MusicSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MUSIC, IDS_HEADER_MUSIC_TT);
const WVTASKITEM c_MusicSpecialTaskList[] =
{
WVTI_ENTRY_ALL_TITLE(UICID_PlayMusic, L"shell32.dll", IDS_TASK_PLAYALL, IDS_TASK_PLAYALL, IDS_TASK_PLAY, IDS_TASK_PLAY, IDS_TASK_PLAY_TT, IDI_TASK_PLAY_MUSIC, CFSFolderViewCB::_CanPlayMusic, CFSFolderViewCB::_OnPlayMusic),
WVTI_ENTRY_ALL(UICID_ShopForMusicOnline, L"shell32.dll", IDS_TASK_SHOPFORMUSICONLINE, IDS_TASK_SHOPFORMUSICONLINE_TT, IDI_TASK_BUY_MUSIC, NULL, CFSFolderViewCB::_OnShopForMusicOnline),
WVTI_ENTRY_ALL_TITLE(GUID_NULL, L"shell32.dll", IDS_TASK_COPYTOAUDIOCDALL, IDS_TASK_COPYTOAUDIOCD, IDS_TASK_COPYTOAUDIOCD, IDS_TASK_COPYTOAUDIOCD, IDS_TASK_COPYTOAUDIOCD_TT, IDI_TASK_SENDTOAUDIOCD, CFSFolderViewCB::_CanSendToAudioCD, CFSFolderViewCB::_OnSendToAudioCD),
};
const LPCTSTR c_MusicOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_MYMUSIC), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
const LPCTSTR c_MyMusicOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_COMMON_MUSIC), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
const WVTASKITEM c_PicturesSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_PICTURES, IDS_HEADER_PICTURES_TT);
const WVTASKITEM c_PicturesSpecialTaskList[] =
{
WVTI_ENTRY_ALL(UICID_GetFromCamera, L"shell32.dll", IDS_TASK_GETFROMCAMERA, IDS_TASK_GETFROMCAMERA_TT, IDI_TASK_GETFROMCAMERA, CFSFolderViewCB::_HasWiaDevices, CFSFolderViewCB::_OnGetFromCamera),
WVTI_ENTRY_ALL(UICID_SlideShow, L"shell32.dll", IDS_TASK_SLIDESHOW, IDS_TASK_SLIDESHOW_TT, IDI_TASK_SLIDESHOW, CFSFolderViewCB::_HasItems, CFSFolderViewCB::_OnSlideShow),
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_ORDERPRINTS, IDS_TASK_ORDERPRINTS_TT, IDI_TASK_ORDERPRINTS, CFSFolderViewCB::_CanOrderPrints, CFSFolderViewCB::_OnOrderPrints),
WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_PRINT_PICTURE_FOLDER, IDS_TASK_PRINT_PICTURE, IDS_TASK_PRINT_PICTURE_FOLDER, IDS_TASK_PRINT_PICTURES, IDS_TASK_PRINT_PICTURES_TT, IDI_TASK_PRINTPICTURES, CFSFolderViewCB::_CanPrintPictures, CFSFolderViewCB::_OnPrintPictures),
WVTI_ENTRY_FILE(UICID_SetAsWallpaper,L"shell32.dll",IDS_TASK_SETASWALLPAPER, IDS_TASK_SETASWALLPAPER_TT, IDI_TASK_SETASWALLPAPER,CFSFolderViewCB::_CanWallpaper, CFSFolderViewCB::_OnWallpaper),
WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_COPYTOCDALL, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD_TT, IDI_TASK_SENDTOCD, CFSFolderViewCB::_CanSendToCD, CFSFolderViewCB::_OnSendToCD),
// Note: temporarily using IDI_ORDERPRINTS for the following task:
WVTI_ENTRY_ALL(UICID_ShopForPicturesOnline, L"shell32.dll", IDS_TASK_SHOPFORPICTURESONLINE, IDS_TASK_SHOPFORPICTURESONLINE_TT, IDI_TASK_ORDERPRINTS, CFSFolderViewCB::_CanBuyPictures, CFSFolderViewCB::_OnShopForPicturesOnline),
};
const LPCTSTR c_PicturesOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_MYPICTURES), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
const LPCTSTR c_MyPicturesOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_COMMON_PICTURES), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
const WVTASKITEM c_VideosSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_VIDEOS, IDS_HEADER_VIDEOS_TT);
const WVTASKITEM c_VideosSpecialTaskList[] =
{
WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_PLAYALL, IDS_TASK_PLAYALL, IDS_TASK_PLAY, IDS_TASK_PLAY, IDS_TASK_PLAY_VIDEOS_TT, IDI_TASK_PLAY_MUSIC, CFSFolderViewCB::_CanPlayVideos, CFSFolderViewCB::_OnPlayVideos),
WVTI_ENTRY_ALL(UICID_GetFromCamera, L"shell32.dll", IDS_TASK_GETFROMCAMERA, IDS_TASK_GETFROMCAMERA_TT, IDI_TASK_GETFROMCAMERA, CFSFolderViewCB::_HasWiaDevices, CFSFolderViewCB::_OnGetFromCamera),
WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_COPYTOCDALL, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD_TT, IDI_TASK_SENDTOCD, CFSFolderViewCB::_CanSendToCD, CFSFolderViewCB::_OnSendToCD)
};
const LPCTSTR c_VideosOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_MYVIDEO), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
const LPCTSTR c_MyVideosOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_COMMON_VIDEO), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
typedef struct {
const WVTASKITEM *pwvIntroText;
const WVTASKITEM *pwvSpecialHeader;
const WVTASKITEM *pwvSpecialTaskList;
UINT cSpecialTaskList;
const WVTASKITEM *pwvFolderHeader;
const WVTASKITEM *pwvFolderTaskList;
UINT cFolderTaskList;
const LPCTSTR *pdwOtherPlacesList;
UINT cOtherPlacesList;
LPCWSTR pszThemeInfo;
} WVCONTENT_DATA;
#define WVCONTENT_DEFVIEWDEFAULT(op) { NULL, NULL, NULL, 0, NULL, NULL, 0, (op), ARRAYSIZE(op), NULL }
#define WVCONTENT_FOLDER(fh, ft, op) { NULL, NULL, NULL, 0, &(fh), (ft), ARRAYSIZE(ft), (op), ARRAYSIZE(op), NULL }
#define WVCONTENT_SPECIAL(sh, st, op, th) { NULL, &(sh), (st), ARRAYSIZE(st), NULL, NULL, 0, (op), ARRAYSIZE(op), (th) }
const WVCONTENT_DATA c_wvContent[] =
{
WVCONTENT_DEFVIEWDEFAULT(c_DocumentsOtherPlaces), // FVCBFT_DOCUMENTS
WVCONTENT_DEFVIEWDEFAULT(c_DocumentsOtherPlaces), // FVCBFT_MYDOCUMENTS
WVCONTENT_SPECIAL(c_PicturesSpecialTaskHeader, c_PicturesSpecialTaskList, c_PicturesOtherPlaces, L"picture"),// FVCBFT_PICTURES
WVCONTENT_SPECIAL(c_PicturesSpecialTaskHeader, c_PicturesSpecialTaskList, c_MyPicturesOtherPlaces, L"picture"),// FVCBFT_MYPICTURES
WVCONTENT_SPECIAL(c_PicturesSpecialTaskHeader, c_PicturesSpecialTaskList, c_PicturesOtherPlaces, L"picture"),// FVCBFT_PHOTOALBUM
WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MusicOtherPlaces, L"music"), // FVCBFT_MUSIC
WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MyMusicOtherPlaces, L"music"), // FVCBFT_MYMUSIC
WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MusicOtherPlaces, L"music"), // FVCBFT_MUSICARTIST
WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MusicOtherPlaces, L"music"), // FVCBFT_MUSICALBUM
WVCONTENT_SPECIAL(c_VideosSpecialTaskHeader, c_VideosSpecialTaskList, c_VideosOtherPlaces, L"video"), // FVCBFT_VIDEOS
WVCONTENT_SPECIAL(c_VideosSpecialTaskHeader, c_VideosSpecialTaskList, c_MyVideosOtherPlaces, L"video"), // FVCBFT_MYVIDEOS
WVCONTENT_SPECIAL(c_VideosSpecialTaskHeader, c_VideosSpecialTaskList, c_VideosOtherPlaces, L"video"), // FVCBFT_VIDEOALBUM
WVCONTENT_DEFVIEWDEFAULT(c_DocumentsOtherPlaces),// stub, it should not be used as legacy htts wont have DUI view. // FVCBFT_USELEGACYHTT
WVCONTENT_SPECIAL(c_CommonDocumentsSpecialTaskHeader, c_CommonDocumentsSpecialTaskList, c_DocumentsOtherPlaces, NULL), // FVCBFT_COMMONDOCUMENTS
};
// This structure describes what a Folder Type can control:
//
typedef struct {
BOOL fIncludeThumbstrip;
FOLDERVIEWMODE fvmFew;
FOLDERVIEWMODE fvmMid;
FOLDERVIEWMODE fvmMany;
const SHCOLUMNID* pscidSort;
int iSortDirection;
} FVCBFOLDERTYPEDATA;
// Here are all the Folder Types we know about:
const FVCBFOLDERTYPEDATA c_rgFolderType[] =
{ // flmstrip // <25 // 25..49 //50... //sort by //sort dir
{ FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_DOCUMENTS
{ FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_MYDOCUMENTS
{ TRUE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_PICTURES
{ TRUE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_MYPICTURES
{ TRUE, FVM_THUMBSTRIP,FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_PHOTOALBUM
{ FALSE, FVM_TILE, FVM_TILE, FVM_DETAILS, &SCID_NAME, 1}, // FVCBFT_MUSIC
{ FALSE, FVM_THUMBNAIL, FVM_TILE, FVM_LIST, &SCID_NAME, 1}, // FVCBFT_MYMUSIC
{ FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, -1}, // FVCBFT_MUSICARTIST
{ FALSE, FVM_TILE, FVM_TILE, FVM_DETAILS, &SCID_NAME, 1}, // FVCBFT_MUSICALBUM, SCID_MUSIC_Track is the same as SCID_NAME
{ FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_VIDEOS
{ FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_MYVIDEOS
{ FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_VIDEOALBUM
{ FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_USELEGACYHTT, only for listview state to look like FVCBFT_DOCUMENTS
{ FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_COMMONDOCUMENTS
};
// This is used to sniff the Folder Type based on folder location:
typedef struct {
UINT csidl;
FVCBFOLDERTYPE ft;
DWORD dwFlags;
} FVCBDATA;
#define FVCBDF_SUBFOLDERS_ONLY 1
#define FVCBDF_THISFOLDER_ONLY 2
const FVCBDATA c_rgFolderState[] =
{
{CSIDL_COMMON_PICTURES, FVCBFT_PHOTOALBUM, FVCBDF_SUBFOLDERS_ONLY},
{CSIDL_MYPICTURES, FVCBFT_PHOTOALBUM, FVCBDF_SUBFOLDERS_ONLY},
{CSIDL_COMMON_PICTURES, FVCBFT_PICTURES, FVCBDF_THISFOLDER_ONLY},
{CSIDL_MYPICTURES, FVCBFT_MYPICTURES, FVCBDF_THISFOLDER_ONLY},
{CSIDL_COMMON_MUSIC, FVCBFT_MUSIC, FVCBDF_THISFOLDER_ONLY},
{CSIDL_MYMUSIC, FVCBFT_MYMUSIC, FVCBDF_THISFOLDER_ONLY},
{CSIDL_MYMUSIC, FVCBFT_MUSICARTIST, FVCBDF_SUBFOLDERS_ONLY},
{CSIDL_COMMON_VIDEO, FVCBFT_VIDEOS, 0},
{CSIDL_MYVIDEO, FVCBFT_MYVIDEOS, 0},
{CSIDL_COMMON_DOCUMENTS,FVCBFT_COMMONDOCUMENTS, FVCBDF_THISFOLDER_ONLY},
{CSIDL_PERSONAL, FVCBFT_MYDOCUMENTS, FVCBDF_THISFOLDER_ONLY},
};
// these are special folders that used to be web view folders. we override the "support legacy" for this list:
const UINT c_rgFolderStateNoLegacy[] =
{
CSIDL_WINDOWS,
CSIDL_SYSTEM,
CSIDL_PROGRAM_FILES,
};
// This is used to map desktop.ini's folder type into our Folder Type
const struct {
LPCWSTR pszType;
FVCBFOLDERTYPE ft;
} c_rgPropBagFolderType[] =
{
{STR_TYPE_DOCUMENTS, FVCBFT_DOCUMENTS},
{STR_TYPE_MYDOCUMENTS, FVCBFT_MYDOCUMENTS},
{STR_TYPE_PICTURES, FVCBFT_PICTURES},
{STR_TYPE_MYPICTURES, FVCBFT_MYPICTURES},
{STR_TYPE_PHOTOALBUM, FVCBFT_PHOTOALBUM},
{STR_TYPE_MUSIC, FVCBFT_MUSIC},
{STR_TYPE_MYMUSIC, FVCBFT_MYMUSIC},
{STR_TYPE_MUSICARTIST, FVCBFT_MUSICARTIST},
{STR_TYPE_MUSICALBUM, FVCBFT_MUSICALBUM},
{STR_TYPE_VIDEOS, FVCBFT_VIDEOS},
{STR_TYPE_MYVIDEOS, FVCBFT_MYVIDEOS},
{STR_TYPE_VIDEOALBUM, FVCBFT_VIDEOALBUM},
{STR_TYPE_USELEGACYHTT, FVCBFT_USELEGACYHTT},
{STR_TYPE_COMMONDOCUMENTS, FVCBFT_COMMONDOCUMENTS},
};
const struct
{
PERCEIVED gen;
FVCBFOLDERTYPE ft;
}
c_rgSniffType[] =
{
{GEN_AUDIO, FVCBFT_MUSIC},
{GEN_IMAGE, FVCBFT_PHOTOALBUM},
{GEN_VIDEO, FVCBFT_VIDEOS},
};
HRESULT _GetFolderTypeForString(LPCWSTR pszFolderType, FVCBFOLDERTYPE *piType)
{
HRESULT hr = E_FAIL;
for (int i = 0; i < ARRAYSIZE(c_rgPropBagFolderType); i++)
{
if (!StrCmpI(c_rgPropBagFolderType[i].pszType, pszFolderType))
{
*piType = c_rgPropBagFolderType[i].ft;
hr = S_OK;
break;
}
}
return hr;
}
HRESULT CFSFolderViewCB::_GetStringForFolderType(int iType, LPWSTR pszFolderType, UINT cchBuf)
{
HRESULT hr = E_FAIL;
for (int i = 0; i < ARRAYSIZE(c_rgPropBagFolderType); i++)
{
if (c_rgPropBagFolderType[i].ft == iType)
{
hr = StringCchCopy(pszFolderType, cchBuf, c_rgPropBagFolderType[i].pszType);
break;
}
}
return hr;
}
extern HRESULT GetTemplateInfoFromHandle(HANDLE h, UCHAR * pKey, DWORD *pdwSize);
FVCBFOLDERTYPE _GetFolderType(LPCWSTR pszPath, LPCITEMIDLIST pidl, BOOL fIsSystemFolder)
{
// Assume we don't find a match
FVCBFOLDERTYPE nFolderType = FVCBFT_NOTSPECIFIED;
WCHAR szFolderType[MAX_PATH];
szFolderType[0] = 0;
// peruser is first
if (FVCBFT_NOTSPECIFIED == nFolderType)
{
IPropertyBag *ppb;
if (SUCCEEDED(SHGetViewStatePropertyBag(pidl, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
{
SHPropertyBag_ReadStr(ppb, L"FolderType", szFolderType, ARRAYSIZE(szFolderType));
if (szFolderType[0])
_GetFolderTypeForString(szFolderType, &nFolderType);
ppb->Release();
}
}
// next, alluser
if ((FVCBFT_NOTSPECIFIED == nFolderType) && fIsSystemFolder)
{
GetFolderString(pszPath, NULL, szFolderType, ARRAYSIZE(szFolderType), TEXT("FolderType"));
if (szFolderType[0])
{
_GetFolderTypeForString(szFolderType, &nFolderType);
}
}
// Check the location of this folder is next
//
if (FVCBFT_NOTSPECIFIED == nFolderType)
{
for (int i = 0; i < ARRAYSIZE(c_rgFolderState); i++)
{
if (FVCBDF_THISFOLDER_ONLY & c_rgFolderState[i].dwFlags)
{
if (PathIsOneOf(pszPath, &(c_rgFolderState[i].csidl), 1))
{
nFolderType = c_rgFolderState[i].ft;
break;
}
}
else if (FVCBDF_SUBFOLDERS_ONLY & c_rgFolderState[i].dwFlags)
{
if (PathIsDirectChildOf(MAKEINTRESOURCE(c_rgFolderState[i].csidl), pszPath))
{
nFolderType = c_rgFolderState[i].ft;
break;
}
}
else if (PathIsEqualOrSubFolder(MAKEINTRESOURCE(c_rgFolderState[i].csidl), pszPath))
{
nFolderType = c_rgFolderState[i].ft;
break;
}
}
}
// Upgrade old webviews to their DUI equivalents, if we can
if (FVCBFT_NOTSPECIFIED == nFolderType && fIsSystemFolder && SHRestricted(REST_ALLOWLEGACYWEBVIEW))
{
// Don't check for legacy webview on our special folders
if (!PathIsOneOf(pszPath, c_rgFolderStateNoLegacy, ARRAYSIZE(c_rgFolderStateNoLegacy)))
{
SFVM_WEBVIEW_TEMPLATE_DATA wvData;
if (SUCCEEDED(DefaultGetWebViewTemplateFromPath(pszPath, &wvData)))
{
if (StrStrI(wvData.szWebView, L"ImgView.htt"))
{
nFolderType = FVCBFT_PHOTOALBUM;
}
else if (StrStrI(wvData.szWebView, L"classic.htt") ||
StrStrI(wvData.szWebView, L"default.htt") ||
StrStrI(wvData.szWebView, L"standard.htt"))
{
// map all of these to "documents", since DUI should take care
// of what the old templates did automatically
nFolderType = FVCBFT_DOCUMENTS;
}
else if (StrStrI(wvData.szWebView, L"folder.htt"))
{
LPTSTR pszFilePrefix = StrStrI(wvData.szWebView, L"file://");
HANDLE hfile = CreateFileWrapW(
pszFilePrefix && (&pszFilePrefix[6] < &wvData.szWebView[MAX_PATH - 1]) ? &pszFilePrefix[7] : wvData.szWebView,
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (INVALID_HANDLE_VALUE != hfile)
{
DWORD dwSize;
UCHAR pKey[MD5DIGESTLEN];
if (SUCCEEDED(GetTemplateInfoFromHandle(hfile, pKey, &dwSize)))
{
static const struct {
UCHAR pKey[MD5DIGESTLEN];
FVCBFOLDERTYPE nFolderType;
} c_paLegacyKeyMap[] = {
{ { 0xf6, 0xad, 0x42, 0xbd, 0xfa, 0x92, 0xb6, 0x61, 0x08, 0x13, 0xd3, 0x71, 0x32, 0x18, 0x85, 0xc7 }, FVCBFT_DOCUMENTS }, // Win98 Gold Program Files
{ { 0x80, 0xea, 0xcb, 0xc7, 0x85, 0x1e, 0xbb, 0x99, 0x12, 0x7b, 0x9d, 0xc7, 0x80, 0xa6, 0x55, 0x2f }, FVCBFT_DOCUMENTS }, // Win98 Gold System
//{ { 0x80, 0xea, 0xcb, 0xc7, 0x85, 0x1e, 0xbb, 0x99, 0x12, 0x7b, 0x9d, 0xc7, 0x80, 0xa6, 0x55, 0x2f }, FVCBFT_DOCUMENTS }, // Win98 Gold Windows
{ { 0x13, 0x0b, 0xe7, 0xaa, 0x42, 0x6f, 0x9c, 0x2e, 0xab, 0x6b, 0x90, 0x77, 0xce, 0x2d, 0xd1, 0x04 }, FVCBFT_DOCUMENTS }, // Win98 Gold - folder.htt
//{ { 0xf6, 0xad, 0x42, 0xbd, 0xfa, 0x92, 0xb6, 0x61, 0x08, 0x13, 0xd3, 0x71, 0x32, 0x18, 0x85, 0xc7 }, FVCBFT_DOCUMENTS }, // Win98 SE Program Files
{ { 0xc4, 0xab, 0x8f, 0x60, 0xf8, 0xfc, 0x5d, 0x07, 0x9e, 0x16, 0xd8, 0xea, 0x12, 0x2c, 0xad, 0x5c }, FVCBFT_DOCUMENTS }, // Win98 SE System
//{ { 0xc4, 0xab, 0x8f, 0x60, 0xf8, 0xfc, 0x5d, 0x07, 0x9e, 0x16, 0xd8, 0xea, 0x12, 0x2c, 0xad, 0x5c }, FVCBFT_DOCUMENTS }, // Win98 SE Windows
//{ { 0x13, 0x0b, 0xe7, 0xaa, 0x42, 0x6f, 0x9c, 0x2e, 0xab, 0x6b, 0x90, 0x77, 0xce, 0x2d, 0xd1, 0x04 }, FVCBFT_DOCUMENTS }, // Win98 SE - folder.htt
{ { 0xef, 0xd0, 0x3e, 0x9e, 0xd8, 0x5e, 0xf3, 0xc5, 0x7e, 0x40, 0xbd, 0x8e, 0x52, 0xbc, 0x9c, 0x67 }, FVCBFT_DOCUMENTS }, // WinME Program Files
{ { 0x49, 0xdb, 0x25, 0x79, 0x7a, 0x5c, 0xb2, 0x8a, 0xe2, 0x57, 0x59, 0xde, 0x2b, 0xd2, 0xa6, 0x70 }, FVCBFT_DOCUMENTS }, // WinME System
//{ { 0x49, 0xdb, 0x25, 0x79, 0x7a, 0x5c, 0xb2, 0x8a, 0xe2, 0x57, 0x59, 0xde, 0x2b, 0xd2, 0xa6, 0x70 }, FVCBFT_DOCUMENTS }, // WinME Windows
{ { 0x2b, 0xcd, 0xc3, 0x11, 0x72, 0x28, 0x34, 0x46, 0xfa, 0x88, 0x31, 0x34, 0xfc, 0xee, 0x7a, 0x3b }, FVCBFT_DOCUMENTS }, // WinME - classic.htt
{ { 0x68, 0x20, 0xa0, 0xa1, 0x6c, 0xba, 0xbf, 0x67, 0x80, 0xfe, 0x1e, 0x70, 0xdf, 0xcb, 0xd6, 0x34 }, FVCBFT_DOCUMENTS }, // WinME - folder.htt
{ { 0x5e, 0x18, 0xaf, 0x48, 0xb1, 0x9f, 0xb8, 0x12, 0x58, 0x64, 0x4a, 0xa2, 0xf5, 0x12, 0x0f, 0x01 }, FVCBFT_PHOTOALBUM }, // WinME - imgview.htt
{ { 0x33, 0x94, 0x21, 0x3b, 0x17, 0x31, 0x2b, 0xeb, 0xac, 0x93, 0x84, 0x13, 0xb8, 0x1f, 0x95, 0x24 }, FVCBFT_DOCUMENTS }, // WinME - standard.htt
{ { 0x47, 0x03, 0x19, 0xf8, 0x0c, 0x20, 0xc4, 0x4f, 0x10, 0xfd, 0x63, 0xf1, 0x2d, 0x2d, 0x0a, 0xcb }, FVCBFT_DOCUMENTS }, // WinME - starter.htt
{ { 0x60, 0x7d, 0xea, 0xa5, 0xaf, 0x5e, 0xbb, 0x9b, 0x10, 0x18, 0xf9, 0x59, 0x9e, 0x43, 0x89, 0x62 }, FVCBFT_DOCUMENTS }, // Win2k Program Files
{ { 0x1c, 0xa6, 0x22, 0xd4, 0x4a, 0x31, 0x57, 0x93, 0xa7, 0x26, 0x68, 0x3c, 0x87, 0x95, 0x8c, 0xce }, FVCBFT_DOCUMENTS }, // Win2k System32
//{ { 0x1c, 0xa6, 0x22, 0xd4, 0x4a, 0x31, 0x57, 0x93, 0xa7, 0x26, 0x68, 0x3c, 0x87, 0x95, 0x8c, 0xce }, FVCBFT_DOCUMENTS }, // Win2k Windows (WinNT)
{ { 0x03, 0x43, 0x48, 0xed, 0xe4, 0x9f, 0xd6, 0xc0, 0x58, 0xf7, 0x72, 0x3f, 0x1b, 0xd0, 0xa7, 0x10 }, FVCBFT_DOCUMENTS }, // Win2k - classic.htt
{ { 0xa8, 0x84, 0xf9, 0x37, 0x84, 0x10, 0xde, 0x7c, 0x0b, 0x34, 0x90, 0x37, 0x23, 0x9e, 0x54, 0x35 }, FVCBFT_DOCUMENTS }, // Win2k - folder.htt
{ { 0x75, 0x1f, 0xcf, 0xca, 0xdd, 0xc7, 0x1d, 0xc7, 0xe1, 0xaf, 0x0c, 0x3e, 0x1e, 0xae, 0x18, 0x51 }, FVCBFT_PHOTOALBUM }, // Win2k - imgview.htt
{ { 0xcc, 0x3f, 0x15, 0xce, 0x4b, 0xfa, 0x36, 0xdf, 0x9b, 0xd8, 0x24, 0x82, 0x3a, 0x9c, 0x0b, 0xa7 }, FVCBFT_DOCUMENTS }, // Win2k - standard.htt
{ { 0x6c, 0xd1, 0xbf, 0xcf, 0xf9, 0x24, 0x24, 0x24, 0x22, 0xfa, 0x1a, 0x8d, 0xd2, 0x1a, 0x41, 0x73 }, FVCBFT_DOCUMENTS }, // Win2k - starter.htt
};
static const size_t c_nLegacyKeys = ARRAYSIZE(c_paLegacyKeyMap);
for (size_t i = 0; i < c_nLegacyKeys; i++)
{
if (0 == memcmp(pKey, c_paLegacyKeyMap[i].pKey, sizeof(UCHAR) * MD5DIGESTLEN))
{
// It's a known legacy folder.htt.
nFolderType = c_paLegacyKeyMap[i].nFolderType;
break;
}
}
}
CloseHandle(hfile);
}
// If we can't say it's a known legacy folder.htt...
if (FVCBFT_NOTSPECIFIED == nFolderType)
{
// ...don't map it to a DUI folder type (preserve customizations).
nFolderType = FVCBFT_USELEGACYHTT;
}
}
else
{
nFolderType = FVCBFT_USELEGACYHTT;
}
}
}
}
return nFolderType;
}
BOOL CFSFolderViewCB::_IsBarricadedFolder()
{
BOOL bResult = FALSE;
TCHAR szPath[MAX_PATH];
if (SUCCEEDED(_pfsf->_GetPath(szPath, ARRAYSIZE(szPath))))
{
const UINT uiFolders[] = {CSIDL_PROGRAM_FILES, CSIDL_WINDOWS, CSIDL_SYSTEM};
if (PathIsOneOf(szPath, uiFolders, ARRAYSIZE(uiFolders)))
bResult = TRUE;
else
{
TCHAR szSystemDrive[4];
ExpandEnvironmentStrings(TEXT("%SystemDrive%\\"), szSystemDrive, ARRAYSIZE(szSystemDrive));
if (!lstrcmpi(szPath, szSystemDrive))
bResult = TRUE;
}
}
return bResult;
}
static const struct { FVCBFOLDERTYPE type; PCWSTR pszClass; PERCEIVED gen;} c_rgDirectoryClasses[] =
{
{FVCBFT_PICTURES, L"Directory.Image", GEN_IMAGE},
{FVCBFT_MYPICTURES, L"Directory.Image", GEN_IMAGE},
{FVCBFT_PHOTOALBUM, L"Directory.Image", GEN_IMAGE},
{FVCBFT_MUSIC, L"Directory.Audio", GEN_AUDIO},
{FVCBFT_MYMUSIC, L"Directory.Audio", GEN_AUDIO},
{FVCBFT_MUSICARTIST, L"Directory.Audio", GEN_AUDIO},
{FVCBFT_MUSICALBUM, L"Directory.Audio", GEN_AUDIO},
{FVCBFT_VIDEOS, L"Directory.Video", GEN_VIDEO},
{FVCBFT_MYVIDEOS, L"Directory.Video", GEN_VIDEO},
{FVCBFT_VIDEOALBUM, L"Directory.Video", GEN_VIDEO},
};
LPCWSTR _GetDirectoryClass(LPCWSTR pszPath, LPCITEMIDLIST pidl, BOOL fIsSystemFolder)
{
FVCBFOLDERTYPE type = _GetFolderType(pszPath, pidl, fIsSystemFolder);
if (type != FVCBFT_NOTSPECIFIED)
{
for (int i = 0; i < ARRAYSIZE(c_rgDirectoryClasses); i++)
{
if (c_rgDirectoryClasses[i].type == type)
return c_rgDirectoryClasses[i].pszClass;
}
}
return NULL;
}
PERCEIVED CFSFolderViewCB::_GetFolderPerceivedType(LPCIDFOLDER pidf)
{
PERCEIVED gen = GEN_FOLDER;
WCHAR szPath[MAX_PATH];
if (SUCCEEDED(_pfsf->_GetPathForItem(pidf, szPath, ARRAYSIZE(szPath))))
{
LPITEMIDLIST pidl = ILCombine(_pfsf->_GetIDList(), (LPCITEMIDLIST)pidf);
if (pidl)
{
FVCBFOLDERTYPE type = _GetFolderType(szPath, pidl, CFSFolder::_IsSystemFolder(pidf));
if (type != -1)
{
for (int i = 0; i < ARRAYSIZE(c_rgDirectoryClasses); i++)
{
if (c_rgDirectoryClasses[i].type == type)
{
gen = c_rgDirectoryClasses[i].gen;
break;
}
}
}
ILFree(pidl);
}
}
return gen;
}
HRESULT CFSFolderViewCB::OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST* rgpidl)
{
// Remember the count of items
_cItems = celt;
FVCBFOLDERTYPE nFolderType = FVCBFT_NOTSPECIFIED;
WCHAR szHere[MAX_PATH];
if (SUCCEEDED(_pfsf->_GetPath(szHere, ARRAYSIZE(szHere))))
{
nFolderType = _GetFolderType(szHere, _pfsf->_GetIDList(), _pfsf->_CheckDefaultIni(NULL, NULL, 0));
}
if (FVCBFT_NOTSPECIFIED == nFolderType)
{
if (_IsBarricadedFolder())
{
nFolderType = FVCBFT_DOCUMENTS;
}
}
// Our location didn't do the trick, so look at the enumerated contents
if (FVCBFT_NOTSPECIFIED == nFolderType && celt > 0)
{
DWORD dwExtCount[ARRAYSIZE(c_rgSniffType)] = {0};
// look at each pidl -> what type is it
//
// But don't look at too many pidls or we really slow down folder
// creation time. If we can't figure it out in the first 100, give up.
//
DWORD dwTotalCount = 0;
for (UINT n = 0; n < celt && dwTotalCount < 100; n++)
{
LPCIDFOLDER pidf = CFSFolder_IsValidID(rgpidl[n]);
ASSERT(pidf);
CFileSysItemString fsi(pidf);
PERCEIVED gen = fsi.PerceivedType();
if (gen == GEN_FOLDER)
{
gen = _GetFolderPerceivedType(pidf);
}
for (int i = 0; i < ARRAYSIZE(c_rgSniffType); i++)
{
if (c_rgSniffType[i].gen == gen)
{
dwExtCount[i]++;
break;
}
}
if (gen != GEN_FOLDER)
dwTotalCount++;
}
// if we found files we determine the overall folder type
if (dwTotalCount > 0)
{
DWORD dwSixtyPercent = MulDiv(dwTotalCount, 3, 5);
for (int i = 0; i < ARRAYSIZE(c_rgSniffType); i++)
{
if (dwExtCount[i] >= dwSixtyPercent)
{
nFolderType = c_rgSniffType[i].ft;
break;
}
}
}
}
// if at this point we've already decided on a folder type, then it either came from sniffing
// or the folder location and we can safely persist that out.
// if celt != 0 then we've sniffed it and we dont want to sniff again, so persist that out.
// otherwise we're in a random folder with 0 elements and we'll sniff it next time.
BOOL fCommit = (FVCBFT_NOTSPECIFIED != nFolderType) || (celt != 0);
// Last resort, assume we're a document folder:
if (FVCBFT_NOTSPECIFIED == nFolderType)
{
nFolderType = FVCBFT_DOCUMENTS;
}
// store what we found out back into the bag.
IPropertyBag *ppb;
if (fCommit && SUCCEEDED(SHGetViewStatePropertyBag(_pfsf->_GetIDList(), VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
{
WCHAR szFolderType[MAX_PATH];
if (SUCCEEDED(_GetStringForFolderType(nFolderType, szFolderType, ARRAYSIZE(szFolderType))))
{
SHPropertyBag_WriteStr(ppb, PROPSTR_FOLDERTYPE, szFolderType);
}
ppb->Release();
}
_pfsf->_nFolderType = nFolderType;
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetViewData(DWORD pv, UINT uViewMode, SFVM_VIEW_DATA* pvi)
{
// Normally whatever defview wants is good for us
pvi->dwOptions = SFVMQVI_NORMAL;
// If our sniff type likes THUMBSTRIP, then override defview
//
if (FVM_THUMBSTRIP == uViewMode)
{
if (c_rgFolderType[_pfsf->_nFolderType].fIncludeThumbstrip)
{
pvi->dwOptions = SFVMQVI_INCLUDE;
}
}
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetWebViewTemplate(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_TEMPLATE_DATA* pvit)
{
HRESULT hr = E_FAIL;
if (FVCBFT_USELEGACYHTT == _pfsf->_nFolderType)
{
TCHAR szHere[MAX_PATH];
if (SUCCEEDED(_pfsf->_GetPath(szHere, ARRAYSIZE(szHere))) && _pfsf->_CheckDefaultIni(NULL, NULL, 0))
{
hr = DefaultGetWebViewTemplateFromPath(szHere, pvit);
}
}
return hr;
}
// Note: defview provides this implementation, this is only for testing
// so the WIA guys can override defview's behavior (and as a way for us
// to force DUI in the presence of HTML content)
//
HRESULT CFSFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
{
HRESULT hr = E_FAIL;
if (FVCBFT_USELEGACYHTT != _pfsf->_nFolderType)
{
ZeroMemory(pData, sizeof(*pData));
pData->dwLayout = SFVMWVL_NORMAL | SFVMWVL_FILES;
if (FVM_THUMBSTRIP == uViewMode)
{
pData->dwLayout = SFVMWVL_PREVIEW | SFVMWVL_FILES;
// duiview will do a release on this pointer when the control is destroyed
_GetPreview3((IPreview3 **)&pData->punkPreview);
}
// RAID 242382
// If we have an image folder, we want to unconditionally hide DefView's
// default "Print this file" folder task since we will supply a context
// appropriate "Print pictures" special task.
//
// RAID 359567
// If we have a music folder, we want to unconditionally hide DefView's
// default "Publish this file" folder task. Not sure the rationale
// behind this, but perhaps they don't want us to be seen as a Napster.
//
// Note:
// This is a HACK added for Whistler, which should be removed in Blackcomb.
//
switch (_pfsf->_nFolderType)
{
case FVCBFT_PICTURES:
case FVCBFT_MYPICTURES:
case FVCBFT_PHOTOALBUM:
case FVCBFT_VIDEOS:
case FVCBFT_MYVIDEOS:
case FVCBFT_VIDEOALBUM:
pData->dwLayout |= SFVMWVL_NOPRINT;
break;
case FVCBFT_MUSIC:
case FVCBFT_MYMUSIC:
case FVCBFT_MUSICARTIST:
case FVCBFT_MUSICALBUM:
pData->dwLayout |= SFVMWVL_NOPUBLISH;
break;
}
hr = S_OK;
}
return hr;
}
HRESULT CFSFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
{
ZeroMemory(pData, sizeof(*pData));
// Check if the folder we are currently over is one of the blockaded folders.
if (_IsBarricadedFolder())
{
pData->dwFlags = SFVMWVF_BARRICADE;
}
if (c_wvContent[_pfsf->_nFolderType].pwvIntroText)
Create_IUIElement(c_wvContent[_pfsf->_nFolderType].pwvIntroText, &(pData->pIntroText));
if (c_wvContent[_pfsf->_nFolderType].pwvSpecialHeader && c_wvContent[_pfsf->_nFolderType].pwvSpecialTaskList)
Create_IUIElement(c_wvContent[_pfsf->_nFolderType].pwvSpecialHeader, &(pData->pSpecialTaskHeader));
if (c_wvContent[_pfsf->_nFolderType].pwvFolderHeader && c_wvContent[_pfsf->_nFolderType].pwvFolderTaskList)
Create_IUIElement(c_wvContent[_pfsf->_nFolderType].pwvFolderHeader, &(pData->pFolderTaskHeader));
if (c_wvContent[_pfsf->_nFolderType].pdwOtherPlacesList)
CreateIEnumIDListOnCSIDLs(_pfsf->_pidl, (LPCTSTR *)c_wvContent[_pfsf->_nFolderType].pdwOtherPlacesList, c_wvContent[_pfsf->_nFolderType].cOtherPlacesList, &(pData->penumOtherPlaces));
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
{
ZeroMemory(pTasks, sizeof(*pTasks));
if (c_wvContent[_pfsf->_nFolderType].pwvSpecialHeader && c_wvContent[_pfsf->_nFolderType].pwvSpecialTaskList)
{
Create_IEnumUICommand((IUnknown*)(void*)this, c_wvContent[_pfsf->_nFolderType].pwvSpecialTaskList, c_wvContent[_pfsf->_nFolderType].cSpecialTaskList, &pTasks->penumSpecialTasks);
}
if (c_wvContent[_pfsf->_nFolderType].pwvFolderHeader && c_wvContent[_pfsf->_nFolderType].pwvFolderTaskList)
{
Create_IEnumUICommand((IUnknown*)(void*)this, c_wvContent[_pfsf->_nFolderType].pwvFolderTaskList, c_wvContent[_pfsf->_nFolderType].cFolderTaskList, &pTasks->penumFolderTasks);
}
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme)
{
ZeroMemory(pTheme, sizeof(*pTheme));
pTheme->pszThemeID = c_wvContent[_pfsf->_nFolderType].pszThemeInfo;
return S_OK;
}
HRESULT CFSFolderViewCB::OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pfvm)
{
HRESULT hr = E_FAIL;
IPropertyBag* pPB;
if (SUCCEEDED(SHGetViewStatePropertyBag(_pfsf->_GetIDList(), VS_BAGSTR_EXPLORER, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB))))
{
SHELLVIEWID vidDefault;
if (SUCCEEDED(SHPropertyBag_ReadGUID(pPB, L"ExtShellFolderViews\\Default", &vidDefault)))
{
hr = ViewModeFromSVID(&vidDefault, pfvm);
}
pPB->Release();
}
if (FAILED(hr))
{
if (IsOS(OS_SERVERADMINUI))
*pfvm = FVM_DETAILS; // Server Admin always gets DETAILS
else if (_cItems < DEFVIEW_FVM_FEW_CUTOFF)
*pfvm = c_rgFolderType[_pfsf->_nFolderType].fvmFew;
else if (_cItems < DEFVIEW_FVM_MANY_CUTOFF)
*pfvm = c_rgFolderType[_pfsf->_nFolderType].fvmMid;
else
*pfvm = c_rgFolderType[_pfsf->_nFolderType].fvmMany;
hr = S_OK;
}
return hr;
}
HRESULT CFSFolderViewCB::OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings)
{
HRESULT hr = OnDefViewMode(pv, &pSettings->fvm);
if (SUCCEEDED(hr))
{
pSettings->fGroupView = (_cItems >= 100) && !IsEqualSCID(SCID_NAME, *c_rgFolderType[_pfsf->_nFolderType].pscidSort);
pSettings->iSortDirection = c_rgFolderType[_pfsf->_nFolderType].iSortDirection;
if (pSettings->fvm == FVM_THUMBNAIL || pSettings->fvm == FVM_THUMBSTRIP || pSettings->fvm == FVM_TILE)
pSettings->fFlags = FWF_AUTOARRANGE;
if (FAILED(_pfsf->_MapSCIDToColumn(c_rgFolderType[_pfsf->_nFolderType].pscidSort, &pSettings->uSortCol)))
pSettings->uSortCol = 0;
}
return hr;
}
HRESULT CFSFolderViewCB::OnGetCustomViewInfo(DWORD pv, SFVM_CUSTOMVIEWINFO_DATA* pData)
{
HRESULT hr = E_FAIL;
TCHAR szIniFile[MAX_PATH];
if (_pfsf->_CheckDefaultIni(NULL, szIniFile, ARRAYSIZE(szIniFile)))
{
if (PathFileExistsAndAttributes(szIniFile, NULL))
{
// Read the custom colors
//
const LPCTSTR c_szCustomColors[CRID_COLORCOUNT] =
{
TEXT("IconArea_TextBackground"),
TEXT("IconArea_Text")
};
for (int i = 0; i < CRID_COLORCOUNT; i++)
{
pData->crCustomColors[i] = GetPrivateProfileInt(TEXT("{BE098140-A513-11D0-A3A4-00C04FD706EC}"), c_szCustomColors[i], CLR_MYINVALID, szIniFile);
}
// Read the background image
TCHAR szTemp1[MAX_PATH];
if (0 < GetPrivateProfileString(TEXT("{BE098140-A513-11D0-A3A4-00C04FD706EC}") /* VID_FolderState */, TEXT("IconArea_Image"), TEXT(""), szTemp1, ARRAYSIZE(szTemp1), szIniFile))
{
TCHAR szTemp2[MAX_PATH];
SHExpandEnvironmentStrings(szTemp1, szTemp2, ARRAYSIZE(szTemp2)); // expand the env vars if any
if (SUCCEEDED(_pfsf->_GetPath(szTemp1, ARRAYSIZE(szTemp1))))
{
if (PathCombine(szTemp2, szTemp1, szTemp2))
{
if (FAILED(StringCchCopy(pData->szIconAreaImage, ARRAYSIZE(pData->szIconAreaImage), szTemp2)))
{
pData->szIconAreaImage[0] = NULL;
}
}
}
}
// Success if we have any real data
hr = (*(pData->szIconAreaImage) ||
pData->crCustomColors[0]!=CLR_MYINVALID ||
pData->crCustomColors[1]!=CLR_MYINVALID)
? S_OK : E_FAIL;
}
}
return hr;
}
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 hr = SHCoCreateInstance(NULL, c_rgFilePages[i], NULL, IID_PPV_ARG(IShellPropSheetExt, &pspse));
if (SUCCEEDED(hr))
{
pspse->AddPages(ppagedata->pfn, ppagedata->lParam);
pspse->Release();
}
}
return S_OK;
}
HRESULT CFSFolderViewCB::OnGetNotify(DWORD pv, LPITEMIDLIST*wP, LONG*lP)
{
if (IsExplorerModeBrowser(_punkSite))
_lEvents |= SHCNE_FREESPACE; // need free space info here too
return E_FAIL; // return failure to let base guy do the rest
}
STDMETHODIMP CFSFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax);
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_ENUMERATEDITEMS, OnEnumeratedItems);
HANDLE_MSG(0, SFVM_GETVIEWDATA, OnGetViewData);
HANDLE_MSG(0, SFVM_GETWEBVIEW_TEMPLATE, OnGetWebViewTemplate);
HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
HANDLE_MSG(0, SFVM_GETWEBVIEWTHEME, OnGetWebViewTheme);
HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode);
HANDLE_MSG(0, SFVM_GETCUSTOMVIEWINFO, OnGetCustomViewInfo);
HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
HANDLE_MSG(0, SFVM_SIZE, OnSize);
HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
HANDLE_MSG(0, SFVM_GETNOTIFY, OnGetNotify);
HANDLE_MSG(0, SFVM_GETDEFERREDVIEWSETTINGS, OnGetDeferredViewSettings);
default:
return E_FAIL;
}
return S_OK;
}
STDAPI CFSFolderCallback_Create(CFSFolder *pfsf, IShellFolderViewCB **ppsfvcb)
{
*ppsfvcb = new CFSFolderViewCB(pfsf);
return *ppsfvcb ? S_OK : E_OUTOFMEMORY;
}