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

934 lines
29 KiB
C++

#include "shellprv.h"
#include "ids.h"
#include "util.h"
#include "datautil.h"
#include "foldertypes.h"
#include "basefvcb.h"
#define PROPSTR_LOGO L"Logo"
typedef struct
{
UINT uIDFriendly;
LPCTSTR pszFolderType;
DWORD dwFlags;
} WEBVIEWTEMPLATEINFO;
#define WVTI_SHOWIFOLDTEMPLATE 0x00000001
// documents must be first.
const WEBVIEWTEMPLATEINFO c_wvtiList[] =
{
{ IDS_CUSTOMIZE_USELEGACYHTT, STR_TYPE_USELEGACYHTT, WVTI_SHOWIFOLDTEMPLATE },
{ IDS_CUSTOMIZE_DOCUMENTS, STR_TYPE_DOCUMENTS, 0 },
{ IDS_CUSTOMIZE_PICTURES, STR_TYPE_PICTURES, 0 },
{ IDS_CUSTOMIZE_PHOTOALBUM, STR_TYPE_PHOTOALBUM, 0 },
{ IDS_CUSTOMIZE_MUSIC, STR_TYPE_MUSIC, 0 },
{ IDS_CUSTOMIZE_MUSICARTIST, STR_TYPE_MUSICARTIST, 0 },
{ IDS_CUSTOMIZE_MUSICALBUM, STR_TYPE_MUSICALBUM, 0 },
{ IDS_CUSTOMIZE_VIDEOS, STR_TYPE_VIDEOS, 0 },
// note: are these gonna happen?
// { IDS_CUSTOMIZE_VIDEOALBUM, STR_TYPE_VIDEOALBUM, 0 },
// { IDS_CUSTOMIZE_BOOKS, STR_TYPE_BOOKS, 0 }
};
typedef enum
{
FOLDERCUST_MODE_GENERATING,
FOLDERCUST_MODE_ICON,
FOLDERCUST_MODE_BITMAP
} FOLDERCUSTMODE;
class CFolderCustomize : public IShellExtInit,
public IShellPropSheetExt
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IShellExtInit
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
// IShellPropSheetExt
STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
STDMETHODIMP ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
{ return S_OK; };
CFolderCustomize();
private:
~CFolderCustomize();
static UINT CALLBACK _PrshtCallback(HWND hwnd, UINT uMsg, PROPSHEETPAGE *ppsp);
void _SetRecurseBox(HWND hwnd);
void _HideIconSection(HWND hwnd);
void _InitDialog(HWND hwnd);
BOOL _HandleWMCommand(HWND hwndDlg, WORD wNotify, WORD wID, HWND hwndCtrl);
void _EnableApply(HWND hwnd);
static DWORD WINAPI _ExtractThreadProc(void *pv);
HRESULT _ExtractOnSeparateThread(IPropertyBag *ppb, HWND hwndDlg);
HRESULT _CreateThumbnailBitmap(HWND hwndDlg);
HRESULT _CreateFolderIcon(HWND hwndDlg);
void _SetThumbnail(HWND hwnd);
void _FreeDlgItems(HWND hwndDlg);
void _SetPreviewToNewState(HWND hwndDlg, FOLDERCUSTMODE fcMode, HBITMAP hbitmap, HICON hicon);
BOOL _ShouldEnableChangeOfIcon();
void _ChangeFolderIcon(HWND hwndDlg);
HRESULT _ProcessIconChange(LPCTSTR pszPickIconDialogCaption, HWND hwndDlg);
void _DirTouch(LPITEMIDLIST pidl);
void _DeleteCustomizationInBag(IPropertyBag *ppb);
BOOL _NotifyAboutWebView(HWND hwnd);
static BOOL CALLBACK _RefreshView(HWND hwnd, LPCITEMIDLIST pidl, LPARAM lParam);
void _RefreshWindows(BOOL fTurnOnWebView, BOOL fApplyToChildren);
HRESULT _ApplyChangesToBag(HWND hwndDlg, IPropertyBag *ppb);
HRESULT _ApplyChanges(HWND hwndDlg);
void _UpdateViewState(HWND hwndDlg, IPropertyBag *ppb, int iIndex);
void _FillTemplateComboBox(HWND hwndTemplates);
int _GetTemplateIndexFromType(LPCTSTR pszType);
static BOOL_PTR CALLBACK _DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LONG _cRef;
LPITEMIDLIST _pidl;
IPropertyBag *_ppb;
// used for background thread extraction
HWND _hwnd;
IPropertyBag *_ppbBackground;
// cached info
HBITMAP _hbmDefault;
HBITMAP _hbmLogo;
TCHAR _szCachedLogoFile[MAX_PATH];
BOOL _fUsingThumb;
ICustomIconManager *_pIconManager;
TCHAR _szLogoFile[MAX_PATH];
TCHAR _szIconPath[MAX_PATH];
int _iIconIndex;
HRESULT _hrFromIconChange;
};
CFolderCustomize::CFolderCustomize() : _cRef(1), _hrFromIconChange(E_FAIL)
{
}
CFolderCustomize::~CFolderCustomize()
{
ILFree(_pidl);
if (_ppb)
_ppb->Release();
if (_pIconManager)
_pIconManager->Release();
if (_ppbBackground)
_ppbBackground->Release();
if (_hbmDefault)
DeleteObject(_hbmDefault);
if (_hbmLogo)
DeleteObject(_hbmLogo);
}
STDAPI CFolderCustomize_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
{
// aggregation checking is handled in class factory
HRESULT hr = E_OUTOFMEMORY;
CFolderCustomize* pfc = new CFolderCustomize();
if (pfc)
{
hr = pfc->QueryInterface(riid, ppvOut);
pfc->Release();
}
return hr;
}
HRESULT CFolderCustomize::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CFolderCustomize, IShellExtInit),
QITABENT(CFolderCustomize, IShellPropSheetExt),
{ 0 }
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CFolderCustomize::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CFolderCustomize::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CFolderCustomize::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
{
HRESULT hr;
if (!pidlFolder)
{
hr = PidlFromDataObject(pdtobj, &_pidl);
}
else
{
hr = Pidl_Set(&_pidl, pidlFolder) ? S_OK : E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = SHGetViewStatePropertyBag(_pidl, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &_ppb));
}
return hr;
}
// from defview.cpp
BOOL IsCustomizable(LPCITEMIDLIST pidlFolder);
UINT CALLBACK CFolderCustomize::_PrshtCallback(HWND hwnd, UINT uMsg, PROPSHEETPAGE *ppsp)
{
if (uMsg == PSPCB_RELEASE)
{
((CFolderCustomize *)ppsp->lParam)->Release();
}
return 1;
}
STDMETHODIMP CFolderCustomize::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
{
HRESULT hr = E_FAIL;
if (IsCustomizable(_pidl))
{
PROPSHEETPAGE psp = {0};
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_USECALLBACK;
psp.hInstance = HINST_THISDLL;
psp.pszTemplate = MAKEINTRESOURCE(DLG_FOLDER_CUSTOMIZE);
psp.pfnDlgProc = _DlgProc;
psp.pfnCallback = _PrshtCallback;
psp.lParam = (LPARAM)this;
HPROPSHEETPAGE hpsp = CreatePropertySheetPage(&psp);
if (hpsp)
{
AddRef(); // HPROPSHEETPAGE holds ref, released on _PrshtCallback
if (!pfnAddPage(hpsp, lParam))
{
DestroyPropertySheetPage(hpsp);
}
else
{
hr = S_OK;
}
}
}
return hr;
}
#define IDH_FOLDER_TEMPLATES 10005
#define IDH_FOLDER_RECURSE 10006
#define IDH_FOLDER_PICKBROWSE 10007
#define IDH_FOLDER_DEFAULT 10008
#define IDH_FOLDER_CHANGEICON 10009
const static DWORD aPrshtHelpIDs[] =
{
IDC_FOLDER_TEMPLATES, IDH_FOLDER_TEMPLATES,
IDC_FOLDER_RECURSE, IDH_FOLDER_RECURSE,
IDC_FOLDER_PICKBROWSE, IDH_FOLDER_PICKBROWSE,
IDC_FOLDER_DEFAULT, IDH_FOLDER_DEFAULT,
IDC_FOLDER_CHANGEICON, IDH_FOLDER_CHANGEICON,
IDC_FOLDER_PREVIEW_ICON, NO_HELP,
IDC_FOLDER_PREVIEW_BITMAP, NO_HELP,
IDC_FOLDER_ICON, NO_HELP,
IDC_FOLDER_CHANGEICONTEXT, NO_HELP,
IDC_FOLDER_CHANGEICONGROUP, NO_HELP,
IDC_NO_HELP_1, NO_HELP,
IDC_NO_HELP_2, NO_HELP,
0, 0
};
BOOL_PTR CALLBACK CFolderCustomize::_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL fRet = FALSE;
CFolderCustomize *pfc = (CFolderCustomize*)GetWindowLongPtr(hwnd, DWLP_USER);
switch (uMsg)
{
case WM_INITDIALOG:
pfc = (CFolderCustomize*)((PROPSHEETPAGE *)lParam)->lParam;
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pfc);
pfc->_InitDialog(hwnd);
break;
case WM_HELP:
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, L"filefold.hlp", HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aPrshtHelpIDs);
break;
case WM_CONTEXTMENU:
WinHelp((HWND)wParam, L"filefold.hlp", HELP_CONTEXTMENU, (ULONG_PTR)(void *)aPrshtHelpIDs);
break;
case WM_COMMAND:
fRet = pfc->_HandleWMCommand(hwnd, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
break;
case WM_NOTIFY:
if (((LPNMHDR)lParam)->code == PSN_APPLY)
{
pfc->_ApplyChanges(hwnd);
}
fRet = TRUE;
break;
case WM_DESTROY:
pfc->_FreeDlgItems(hwnd);
break;
}
return fRet;
}
void CFolderCustomize::_FreeDlgItems(HWND hwndDlg)
{
HICON hicon = (HICON)SendDlgItemMessage(hwndDlg, IDC_FOLDER_PREVIEW_ICON, STM_SETICON, NULL, NULL);
if (hicon)
DestroyIcon(hicon);
HBITMAP hbitmap = (HBITMAP)SendDlgItemMessage(hwndDlg, IDC_FOLDER_PREVIEW_BITMAP, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, NULL);
if (hbitmap)
DeleteObject(hbitmap);
ReplaceDlgIcon(hwndDlg, IDC_FOLDER_ICON, NULL);
}
void CFolderCustomize::_SetPreviewToNewState(HWND hwndDlg, FOLDERCUSTMODE fcMode, HBITMAP hbitmap, HICON hicon)
{
// if fcMode == FOLDERCUST_MODE_ICON, we need hicon and not hbitmap
// if fcMode == FOLDERCUST_MODE_BITMAP, we need hbitmap and not hicon.
// otherwise we dont want either.
ASSERT((fcMode != FOLDERCUST_MODE_ICON) || (hicon && !hbitmap));
ASSERT((fcMode != FOLDERCUST_MODE_BITMAP) || (!hicon && hbitmap));
ASSERT((fcMode != FOLDERCUST_MODE_GENERATING) || (!hicon && !hbitmap));
switch (fcMode)
{
case FOLDERCUST_MODE_GENERATING:
{
TCHAR szText[100];
LoadString(HINST_THISDLL, IDS_CUSTOMIZE_GENERATING, szText, ARRAYSIZE(szText));
SetWindowText(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_TEXT), szText);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_TEXT), SW_SHOW);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_ICON), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_BITMAP), SW_HIDE);
}
break;
case FOLDERCUST_MODE_ICON:
{
HICON hiconOld = (HICON)SendDlgItemMessage(hwndDlg, IDC_FOLDER_PREVIEW_ICON, STM_SETICON, NULL, NULL);
if (hiconOld)
DestroyIcon(hiconOld);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_TEXT), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_ICON), SW_SHOW);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_BITMAP), SW_HIDE);
SendDlgItemMessage(hwndDlg, IDC_FOLDER_PREVIEW_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon);
}
break;
case FOLDERCUST_MODE_BITMAP:
{
HBITMAP hbitmapOld = (HBITMAP)SendDlgItemMessage(hwndDlg, IDC_FOLDER_PREVIEW_BITMAP, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, NULL);
if (hbitmapOld)
DeleteObject(hbitmapOld);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_TEXT), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_ICON), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_PREVIEW_BITMAP), SW_SHOW);
SendDlgItemMessage(hwndDlg, IDC_FOLDER_PREVIEW_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbitmap);
}
break;
}
}
HRESULT CFolderCustomize::_CreateFolderIcon(HWND hwndDlg)
{
IExtractIcon *peic;
HRESULT hr = SHGetUIObjectFromFullPIDL(_pidl, NULL, IID_PPV_ARG(IExtractIcon, &peic));
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH];
INT iIndex;
UINT wFlags;
hr = peic->GetIconLocation(0, szPath, ARRAYSIZE(szPath), &iIndex, &wFlags);
if (SUCCEEDED(hr))
{
UINT nIconSize = MAKELONG(32, 32); // 32 for both large and small
HICON hiconLarge;
hr = peic->Extract(szPath, iIndex, NULL, &hiconLarge, nIconSize);
if (SUCCEEDED(hr))
{
ReplaceDlgIcon(hwndDlg, IDC_FOLDER_ICON, hiconLarge);
}
}
peic->Release();
}
return hr;
}
DWORD WINAPI CFolderCustomize::_ExtractThreadProc(void *pv)
{
CFolderCustomize *pfc = (CFolderCustomize*)pv;
pfc->_SetPreviewToNewState(pfc->_hwnd, FOLDERCUST_MODE_GENERATING, NULL, NULL);
IExtractImage *pei;
HRESULT hr = SHGetUIObjectFromFullPIDL(pfc->_pidl, NULL, IID_PPV_ARG(IExtractImage, &pei));
if (SUCCEEDED(hr))
{
hr = SHLoadFromPropertyBag(pei, pfc->_ppbBackground);
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH];
SIZE sz = {96, 96};
DWORD dwFlags = IEIFLAG_QUALITY;
hr = pei->GetLocation(szPath, ARRAYSIZE(szPath), NULL, &sz, 24, &dwFlags);
if (SUCCEEDED(hr))
{
HBITMAP hbitmap;
hr = pei->Extract(&hbitmap);
if (SUCCEEDED(hr))
{
pfc->_SetPreviewToNewState(pfc->_hwnd, FOLDERCUST_MODE_BITMAP, hbitmap, NULL);
TCHAR szLogo[MAX_PATH];
if (SUCCEEDED(SHPropertyBag_ReadStr(pfc->_ppbBackground, PROPSTR_LOGO, szLogo, ARRAYSIZE(szLogo))))
{
HBITMAP *phbm = szLogo[0] ? &pfc->_hbmLogo : &pfc->_hbmDefault;
if (*phbm)
DeleteObject(*phbm);
*phbm = (HBITMAP)CopyImage(hbitmap, IMAGE_BITMAP, 0, 0, 0);
if (szLogo[0])
{
lstrcpyn(pfc->_szCachedLogoFile, szLogo, ARRAYSIZE(pfc->_szCachedLogoFile));
}
}
}
}
}
pei->Release();
}
if (FAILED(hr))
{
// IExtractImage on a folder without any jpegs inside will fail.
// in that case we need IExtractIcon.
IExtractIcon *peic;
hr = SHGetUIObjectFromFullPIDL(pfc->_pidl, NULL, IID_PPV_ARG(IExtractIcon, &peic));
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH];
INT iIndex;
UINT wFlags;
hr = peic->GetIconLocation(0, szPath, ARRAYSIZE(szPath), &iIndex, &wFlags);
if (SUCCEEDED(hr))
{
UINT nIconSize = MAKELONG(96, 96); // 96 for both large and small
HICON hiconLarge;
hr = peic->Extract(szPath, iIndex, NULL, &hiconLarge, nIconSize);
if (SUCCEEDED(hr))
{
pfc->_SetPreviewToNewState(pfc->_hwnd, FOLDERCUST_MODE_ICON, NULL, hiconLarge);
}
}
peic->Release();
}
}
pfc->Release(); // this thread holds a ref
return 0;
}
HRESULT CFolderCustomize::_ExtractOnSeparateThread(IPropertyBag *ppb, HWND hwndDlg)
{
HRESULT hr = E_OUTOFMEMORY;
IUnknown_Set((IUnknown**)&_ppbBackground, ppb);
_hwnd = hwndDlg;
AddRef();
if (SHCreateThread(_ExtractThreadProc, this, CTF_COINIT, NULL))
{
hr = S_OK;
}
else
{
Release(); // thread failed to take ref
}
return hr;
}
HRESULT CFolderCustomize::_CreateThumbnailBitmap(HWND hwndDlg)
{
HRESULT hr = S_OK;
// see if the bitmap is one we've already extracted.
// can't use the thumbs.db cache for this kind of stuff, since the changes
// havent been committed yet we really shouldnt be poking around in data.
if (!_fUsingThumb && _hbmDefault)
{
_SetPreviewToNewState(hwndDlg, FOLDERCUST_MODE_BITMAP, (HBITMAP)CopyImage(_hbmDefault, IMAGE_BITMAP, 0, 0, 0), NULL);
}
else if (_fUsingThumb && _hbmLogo && (lstrcmpi(_szLogoFile, _szCachedLogoFile) == 0))
{
_SetPreviewToNewState(hwndDlg, FOLDERCUST_MODE_BITMAP, (HBITMAP)CopyImage(_hbmLogo, IMAGE_BITMAP, 0, 0, 0), NULL);
}
else
{
// cache miss, figure it out again.
IPropertyBag *ppb;
hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &ppb));
if (SUCCEEDED(hr))
{
hr = SHPropertyBag_WriteStr(ppb, PROPSTR_LOGO, _fUsingThumb ? _szLogoFile : TEXT(""));
if (SUCCEEDED(hr))
{
hr = _ExtractOnSeparateThread(ppb, hwndDlg);
}
ppb->Release();
}
}
return hr;
}
// dont want OFN_NODEREFERENCELINKS so use the rundlg.cpp helper directly
STDAPI_(BOOL) _GetFileNameFromBrowse(HWND hwnd, LPTSTR szFilePath, UINT cbFilePath, LPCTSTR szWorkingDir, LPCTSTR szDefExt, LPCTSTR szFilters, LPCTSTR szTitle, DWORD dwFlags);
BOOL CFolderCustomize::_HandleWMCommand(HWND hwndDlg, WORD wNotify, WORD wID, HWND hwndCtrl)
{
switch(wID)
{
case IDC_FOLDER_TEMPLATES:
if (wNotify == LBN_SELCHANGE)
{
_EnableApply(hwndDlg);
}
break;
case IDC_FOLDER_DEFAULT:
_EnableApply(hwndDlg);
_fUsingThumb = FALSE;
_CreateThumbnailBitmap(hwndDlg);
break;
case IDC_FOLDER_CHANGEICON:
_ChangeFolderIcon(hwndDlg);
break;
case IDC_FOLDER_PICKBROWSE:
TCHAR szFilePath[MAX_PATH] = {0};
TCHAR szInitialDir[MAX_PATH] = {0};
// initial directory is current folder
// todo: load supported file types at runtime
if (SHGetPathFromIDList(_pidl, szInitialDir) &&
_GetFileNameFromBrowse(hwndDlg, szFilePath, ARRAYSIZE(szFilePath), szInitialDir,
MAKEINTRESOURCE(IDS_IMAGES), MAKEINTRESOURCE(IDS_IMAGESFILTER), MAKEINTRESOURCE(IDS_BROWSE),
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR))
{
_EnableApply(hwndDlg);
_fUsingThumb = TRUE;
lstrcpyn(_szLogoFile, szFilePath, ARRAYSIZE(_szLogoFile));
_CreateThumbnailBitmap(hwndDlg);
}
break;
}
return FALSE;
}
BOOL CFolderCustomize::_NotifyAboutWebView(HWND hwnd)
{
BOOL fRet = FALSE;
SHELLSTATE ss;
SHGetSetSettings(&ss, SSF_WEBVIEW, FALSE);
if (!ss.fWebView &&
(IDYES == ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CUSTOMIZE_TURNONWEBVIEW),
MAKEINTRESOURCE(IDS_CUSTOMIZE), MB_YESNO | MB_ICONQUESTION)))
{
ss.fWebView = TRUE;
SHGetSetSettings(&ss, SSF_WEBVIEW, TRUE);
fRet = TRUE;
}
return fRet;
}
typedef struct
{
LPCITEMIDLIST pidlChanged;
BOOL fTurnOnWebView;
BOOL fApplyToChildren;
} CUSTENUMSTRUCT;
BOOL CALLBACK CFolderCustomize::_RefreshView(HWND hwnd, LPCITEMIDLIST pidl, LPARAM lParam)
{
CUSTENUMSTRUCT *pes = (CUSTENUMSTRUCT *)lParam;
if (pes->fTurnOnWebView)
{
PostMessage(hwnd, WM_COMMAND, SFVIDM_MISC_SETWEBVIEW, TRUE);
}
if (ILIsEqual(pes->pidlChanged, pidl) || (pes->fApplyToChildren && ILIsParent(pes->pidlChanged, pidl, FALSE)))
{
PostMessage(hwnd, WM_COMMAND, SFVIDM_MISC_HARDREFRESH, 0L);
}
return TRUE;
}
void CFolderCustomize::_RefreshWindows(BOOL fTurnOnWebView, BOOL fApplyToChildren)
{
CUSTENUMSTRUCT es = { _pidl, fTurnOnWebView, fApplyToChildren };
EnumShellWindows(_RefreshView, (LPARAM)&es);
}
void CFolderCustomize::_UpdateViewState(HWND hwndDlg, IPropertyBag *ppb, int iIndex)
{
TCHAR szOriginalType[25];
szOriginalType[0] = 0;
SHPropertyBag_ReadStr(ppb, PROPSTR_FOLDERTYPE, szOriginalType, ARRAYSIZE(szOriginalType));
// only apply view state change if the folder type is changing.
// also special case so that we dont apply a view state change if the folder has no
// current folder type and the user didnt change the selection from "documents"
// (i.e. they changed folder thumbnail but nothing else).
if ((lstrcmpi(c_wvtiList[iIndex].pszFolderType, szOriginalType) != 0) &&
(szOriginalType[0] || iIndex))
{
// knock out existing state, they don't want it any more.
SHPropertyBag_Delete(ppb, VS_PROPSTR_MODE);
SHPropertyBag_Delete(ppb, VS_PROPSTR_VID);
SHPropertyBag_WriteStr(ppb, PROPSTR_FOLDERTYPE, c_wvtiList[iIndex].pszFolderType);
_RefreshWindows(_NotifyAboutWebView(hwndDlg), Button_GetCheck(GetDlgItem(hwndDlg, IDC_FOLDER_RECURSE)) == BST_CHECKED);
}
}
void CFolderCustomize::_DirTouch(LPITEMIDLIST pidl)
{
FILETIME ftCurrent;
GetSystemTimeAsFileTime(&ftCurrent);
TCHAR szPath[MAX_PATH];
if (SHGetPathFromIDList(pidl, szPath))
{
// woohoo yay for private flags
// 0x100 lets us open a directory in write access
HANDLE h = CreateFile(szPath, GENERIC_READ | 0x100,
FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h != INVALID_HANDLE_VALUE)
{
SetFileTime(h, NULL, NULL, &ftCurrent);
CloseHandle(h);
}
}
}
void CFolderCustomize::_DeleteCustomizationInBag(IPropertyBag *ppb)
{
// this is only called when the inherit bag is getting written out.
// so we need to scorch the existing non-inherit bag so it doesn't
// override the inherit bag.
SHPropertyBag_Delete(ppb, PROPSTR_FOLDERTYPE);
SHPropertyBag_Delete(ppb, PROPSTR_LOGO);
SHPropertyBag_Delete(ppb, VS_PROPSTR_MODE);
SHPropertyBag_Delete(ppb, VS_PROPSTR_VID);
}
HRESULT CFolderCustomize::_ApplyChangesToBag(HWND hwndDlg, IPropertyBag *ppb)
{
// handle webview template
HWND hwndTemplates = GetDlgItem(hwndDlg, IDC_FOLDER_TEMPLATES);
if (hwndTemplates)
{
int iIndex = ComboBox_GetCurSel(hwndTemplates);
if (iIndex != CB_ERR)
{
int iViewIndex = (int)ComboBox_GetItemData(hwndTemplates, iIndex);
_UpdateViewState(hwndDlg, ppb, iViewIndex);
}
}
TCHAR szThumb[MAX_PATH];
szThumb[0] = 0;
if (_fUsingThumb)
{
lstrcpyn(szThumb, _szLogoFile, ARRAYSIZE(szThumb));
}
TCHAR szOriginalLogo[MAX_PATH];
szOriginalLogo[0] = 0;
SHPropertyBag_ReadStr(ppb, PROPSTR_LOGO, szOriginalLogo, ARRAYSIZE(szOriginalLogo));
if (lstrcmpi(szThumb, szOriginalLogo) != 0)
{
SHPropertyBag_WriteStr(ppb, PROPSTR_LOGO, szThumb);
_DirTouch(_pidl);
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, _pidl, NULL);
}
return S_OK;
}
HRESULT CFolderCustomize::_ApplyChanges(HWND hwndDlg)
{
// handle icon change
switch (_hrFromIconChange)
{
case S_OK:
_pIconManager->SetIcon(_szIconPath, _iIconIndex);
break;
case S_FALSE:
_pIconManager->SetDefaultIcon();
break;
}
if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_FOLDER_RECURSE)) == BST_CHECKED)
{
IPropertyBag *ppbInherit;
if (SUCCEEDED(SHGetViewStatePropertyBag(_pidl, VS_BAGSTR_EXPLORER, SHGVSPB_INHERIT, IID_PPV_ARG(IPropertyBag, &ppbInherit))))
{
_DeleteCustomizationInBag(_ppb);
_ApplyChangesToBag(hwndDlg, ppbInherit);
ppbInherit->Release();
}
}
else
{
_ApplyChangesToBag(hwndDlg, _ppb);
}
return S_OK;
}
int CFolderCustomize::_GetTemplateIndexFromType(LPCTSTR pszType)
{
// default to "documents"
int iIndexFound = 0;
for (int iIndex = 0; iIndex < ARRAYSIZE(c_wvtiList); iIndex++)
{
if (lstrcmpi(c_wvtiList[iIndex].pszFolderType, pszType) == 0)
{
iIndexFound = iIndex;
break;
}
}
return iIndexFound;
}
// Fill the combobox with templates' friendly names.
void CFolderCustomize::_FillTemplateComboBox(HWND hwndTemplates)
{
// Disable redraws while we mess repeatedly with the contents.
SendMessage(hwndTemplates, WM_SETREDRAW, FALSE, 0);
TCHAR szType[25];
szType[0] = 0;
SHPropertyBag_ReadStr(_ppb, PROPSTR_FOLDERTYPE, szType, ARRAYSIZE(szType));
int nFolderTypeIndex = _GetTemplateIndexFromType(szType); // store index into c_wvtiList
int iIndex = 0; // index into combobox
// Add each template to the listview.
for (int nTemplate = 0; nTemplate < ARRAYSIZE(c_wvtiList); nTemplate++)
{
TCHAR szPath[MAX_PATH];
SFVM_WEBVIEW_TEMPLATE_DATA wvData;
if (!(c_wvtiList[nTemplate].dwFlags & WVTI_SHOWIFOLDTEMPLATE) ||
(SHGetPathFromIDList(_pidl, szPath) && SUCCEEDED(DefaultGetWebViewTemplateFromPath(szPath, &wvData))))
{
TCHAR szFriendlyName[100];
LoadString(HINST_THISDLL, c_wvtiList[nTemplate].uIDFriendly, szFriendlyName, ARRAYSIZE(szFriendlyName));
int iIndexAdd = ComboBox_AddString(hwndTemplates, szFriendlyName);
if (iIndexAdd != -1)
{
if (nTemplate == nFolderTypeIndex)
{
iIndex = iIndexAdd;
}
ComboBox_SetItemData(hwndTemplates, iIndexAdd, nTemplate);
}
}
}
// pick default
ComboBox_SetCurSel(hwndTemplates, iIndex);
// Reenable redraws.
SendMessage(hwndTemplates, WM_SETREDRAW, TRUE, 0);
InvalidateRect(hwndTemplates, NULL, TRUE);
}
void CFolderCustomize::_SetThumbnail(HWND hwnd)
{
_szLogoFile[0] = 0;
SHPropertyBag_ReadStr(_ppb, PROPSTR_LOGO, _szLogoFile, ARRAYSIZE(_szLogoFile));
_fUsingThumb = _szLogoFile[0];
_CreateThumbnailBitmap(hwnd);
}
void CFolderCustomize::_SetRecurseBox(HWND hwnd)
{
IPropertyBag *ppbInherit;
if (SUCCEEDED(SHGetViewStatePropertyBag(_pidl, VS_BAGSTR_EXPLORER, SHGVSPB_INHERIT, IID_PPV_ARG(IPropertyBag, &ppbInherit))))
{
TCHAR szTypeInherit[MAX_PATH];
if (SUCCEEDED(SHPropertyBag_ReadStr(ppbInherit, PROPSTR_FOLDERTYPE, szTypeInherit, ARRAYSIZE(szTypeInherit))) && szTypeInherit[0])
{
TCHAR szType[MAX_PATH];
if (SUCCEEDED(SHPropertyBag_ReadStr(_ppb, PROPSTR_FOLDERTYPE, szType, ARRAYSIZE(szType))) &&
(lstrcmpi(szTypeInherit, szType) == 0))
{
Button_SetCheck(GetDlgItem(hwnd, IDC_FOLDER_RECURSE), TRUE);
}
}
ppbInherit->Release();
}
}
// since changing the icon isn't in the peruser property bag (yet [it was punted from whistler])
// we need to disable this section if we know it can't be modified.
void CFolderCustomize::_HideIconSection(HWND hwndDlg)
{
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_CHANGEICONGROUP), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_CHANGEICON), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_CHANGEICONTEXT), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_FOLDER_ICON), SW_HIDE);
}
void CFolderCustomize::_InitDialog(HWND hwndDlg)
{
HWND hwndTemplates = GetDlgItem(hwndDlg, IDC_FOLDER_TEMPLATES);
if (hwndTemplates)
{
_FillTemplateComboBox(GetDlgItem(hwndDlg, IDC_FOLDER_TEMPLATES));
EnableWindow(hwndTemplates, TRUE);
_SetThumbnail(hwndDlg);
// Disable the Icon Change button if we the IShellFolder doesn't support ICustomIconManager interface.
if (_ShouldEnableChangeOfIcon())
{
_CreateFolderIcon(hwndDlg);
}
else
{
_HideIconSection(hwndDlg);
}
_SetRecurseBox(hwndDlg);
}
}
// helpers moved from mulprsht
// How do we selectively disable for .exe
BOOL CFolderCustomize::_ShouldEnableChangeOfIcon()
{
if (!_pIconManager)
{
SHGetUIObjectFromFullPIDL(_pidl, NULL, IID_PPV_ARG(ICustomIconManager, &_pIconManager));
}
return BOOLIFY(_pIconManager);
}
void CFolderCustomize::_EnableApply(HWND hwnd)
{
PropSheet_Changed(GetParent(hwnd), hwnd);
}
void CFolderCustomize::_ChangeFolderIcon(HWND hwndDlg)
{
ASSERT(_pIconManager);
TCHAR szDialogCaptionFmt[MAX_PATH];
LoadString(HINST_THISDLL, IDS_FOLDER_PICKICONDLG_CAPTION, szDialogCaptionFmt, ARRAYSIZE(szDialogCaptionFmt));
TCHAR szFileName[MAX_PATH], szDialogCaption[MAX_PATH];
if (SUCCEEDED(SHGetNameAndFlags(_pidl, SHGDN_NORMAL, szFileName, ARRAYSIZE(szFileName), NULL)))
{
wnsprintf(szDialogCaption, ARRAYSIZE(szDialogCaption), szDialogCaptionFmt, PathFindFileName(szFileName));
}
if (SUCCEEDED(_ProcessIconChange(szDialogCaption, hwndDlg)))
{
_EnableApply(hwndDlg);
}
}
HRESULT CFolderCustomize::_ProcessIconChange(LPCTSTR pszPickIconDialogCaption, HWND hwndDlg)
{
int nIconIndex = -1;
TCHAR szIconPath[MAX_PATH];
szIconPath[0] = 0;
HRESULT hr = PickIconDlgWithTitle(hwndDlg, pszPickIconDialogCaption, TRUE, szIconPath, ARRAYSIZE(szIconPath), &nIconIndex);
_hrFromIconChange = hr;
switch (hr)
{
case S_OK:
{
HICON hIcon = ExtractIcon(HINST_THISDLL, szIconPath, nIconIndex);
if (hIcon != NULL)
{
ReplaceDlgIcon(hwndDlg, IDC_FOLDER_ICON, hIcon);
StrCpyN(_szIconPath, szIconPath, ARRAYSIZE(_szIconPath));
_iIconIndex = nIconIndex;
}
else
{
_hrFromIconChange = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
break;
}
case S_FALSE:
{
HICON hIcon;
if (SUCCEEDED(_pIconManager->GetDefaultIconHandle(&hIcon)))
{
ReplaceDlgIcon(hwndDlg, IDC_FOLDER_ICON, hIcon);
}
else
{
_hrFromIconChange = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
break;
}
case HRESULT_FROM_WIN32(ERROR_CANCELLED):
{
break;
}
}
return hr;
}