#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;
}