//
//  Win32 window that hosts a pane on the desktop.
//
//  You are expected to derive from this class and implement the virtual
//  methods.
//
#ifndef __SFTHOST_H__
#define __SFTHOST_H__

#include "uemapp.h"
#include "runtask.h"
#include "hostutil.h"
#include "dobjutil.h"

//****************************************************************************
//
//  Miscellaneous helper functions
//

STDAPI_(HFONT) LoadControlFont(HTHEME hTheme, int iPart, BOOL fUnderline, DWORD dwSizePercentage);

STDAPI_(HRESULT)
    IDataObject_DragQueryFile(IDataObject *pdto, UINT iFile, LPTSTR pszBuf, UINT cch, UINT *puFiles);

STDAPI_(LPITEMIDLIST)
    ConvertToLogIL(LPITEMIDLIST pidl);

LRESULT _SendNotify(HWND hwndFrom, UINT code, OPTIONAL NMHDR *pnm = NULL);

BOOL GetFileCreationTime(LPCTSTR pszFile, LPFILETIME pftCreate);

/* Simple wrapper - the string needs to be freed with SHFree */
LPTSTR _DisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl, UINT shgno);

HICON _IconOf(IShellFolder *psf, LPCITEMIDLIST pidl, int cxIcon);

BOOL ShowInfoTip();

//****************************************************************************

//  The base class uses the following properties in the initializing
//  property bag:
//
//
//      "type"      - type of host to use (see HOSTTYPE array)
//      "asyncEnum" - 1 = enumerate in background; 0 = foreground
//      "iconSize"  - 0 = small, 1 = large
//      "horizontal" - 0 = vertical (default), n = horizontal
//                    n = number of items to show

class PaneItem;

class PaneItem
{
public:
    PaneItem() : _iPos(-1), _iPinPos(PINPOS_UNPINNED) {}
    virtual ~PaneItem() { SHFree(_pszAccelerator); }
    static int CALLBACK DPAEnumCallback(PaneItem *self, LPVOID pData)
        { delete self; return TRUE; }

    BOOL IsPinned() const { return _iPinPos >= 0; }
    BOOL IsSeparator() const { return _iPinPos == PINPOS_SEPARATOR; }
    BOOL GetPinPos() const { return _iPinPos; }
    BOOL IsCascade() const { return _dwFlags & ITEMFLAG_CASCADE; }
    void EnableCascade() { _dwFlags |= ITEMFLAG_CASCADE; }
    BOOL HasSubtitle() const { return _dwFlags & ITEMFLAG_SUBTITLE; }
    void EnableSubtitle() { _dwFlags |= ITEMFLAG_SUBTITLE; }
    BOOL IsDropTarget() const { return _dwFlags & ITEMFLAG_DROPTARGET; }
    void EnableDropTarget() { _dwFlags |= ITEMFLAG_DROPTARGET; }
    BOOL HasAccelerator() { return _pszAccelerator != NULL; }

    virtual BOOL IsEqual(PaneItem *pItem) const { return FALSE; }

    enum {
        PINPOS_UNPINNED = -1,
        PINPOS_SEPARATOR = -2,
    };

    enum {
        ITEMFLAG_CASCADE    = 0x0001,
        ITEMFLAG_SUBTITLE   = 0x0002,
        ITEMFLAG_DROPTARGET = 0x0004,
    };

private:
    friend class SFTBarHost;
    int             _iPos;          /* Position on screen (or garbage if not on screen) */
public:
    int             _iPinPos;       /* Pin position (or special PINPOS value) */
    DWORD           _dwFlags;       /* ITEMFLAG_* values */
    LPTSTR          _pszAccelerator;/* Text with ampersand (for keyboard accelerator) */
};

//
//  Note: Since this is a base class, we can't use ATL because the base
//  class's CreateInstance won't know how to construct the derived classes.
//
class SFTBarHost
    : public IDropTarget
    , public IDropSource
    , public CAccessible
{
public:
    static BOOL Register();
    static BOOL Unregister();

// Would normally be "protected" except that proglist.cpp actually implements
// in a separate class and forwards.
public:
    /*
     *  Classes which derive from this class are expected to implement
     *  the following methods.
     */

    /* Constructor with return code */
    virtual HRESULT Initialize() PURE;

    /* Destructor */
    virtual ~SFTBarHost();

    /* Enumerate the objects and call AddItem for each one you find */
    // TODO: Maybe the EnumItems should be moved to a background thread
    virtual void EnumItems() PURE;

    virtual BOOL NeedBackgroundEnum() { return FALSE; }
    virtual BOOL HasDynamicContent() { return FALSE; }

    /* Compare two objects, tell me which one should come first */
    virtual int CompareItems(PaneItem *p1, PaneItem *p2) PURE;

    /*
     * Given a PaneItem, produce the pidl and IShellFolder associated with it.
     * The IShellFolder will be Release()d when no longer needed.
     */
    virtual HRESULT GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut) PURE;

    // An over-ridable method to add an image to our private imagelist for an item (virtual but not pure)
    virtual int AddImageForItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidl, int iPos);

    /*
     *  Dispatch a shell notification.  Default handler ignores.
     */
    virtual void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { }

    /*
     *  Allows derived classes to control their own icon size.
     */
     enum ICONSIZE {
        ICONSIZE_SMALL,     // typically 16x16
        ICONSIZE_LARGE,     // typically 32x32
        ICONSIZE_MEDIUM,    // typically 24x24
    };

    virtual int ReadIconSize() PURE;


    /*
     *  Optional hook into window procedure.
     *
     *  Default behavior is just to call DefWindowProc.
     */
    virtual LRESULT OnWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    /*
     *  Required if AdjustDeleteMenuItem is customized.
     *  Invoked when a context menu command is invoked.
     *  Host must intercept the
     *  "delete" command.  Other commands can also be intercepted as
     *  necessary.
     */
    virtual HRESULT ContextMenuInvokeItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici, LPCTSTR pszVerb);

    /*
     *  Required if HOSTF_CANRENAME is passed:  Invoked when an item
     *  is renamed.
     *
     *  Note: The client is allowed to change the pidl associated with an
     *  item during a rename.  (In fact, it's expected to!)  So callers
     *  which have called GetFolderAndPidl need to call it again after the
     *  rename to get the correct post-rename pidl.
     */
    virtual HRESULT ContextMenuRenameItem(PaneItem *pitem, LPCTSTR ptszNewName) { return E_NOTIMPL; }

    /*
     *  Optional hook for obtaining the display name of an item.
     *  The default implementation calls IShellFolder::GetDisplayNameOf.
     *  If hooked, the returned string should be allocated by SHAlloc().
     */
    virtual LPTSTR DisplayNameOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem, SHGNO shgno)
    {
        return _DisplayNameOf(psf, pidlItem, shgno);
    }

    /*
     *  Required if pinnned items are created.  Invoked when the user moves
     *  a pinned item.
     */
    virtual HRESULT MovePinnedItem(PaneItem *pitem, int iInsert) { return E_NOTIMPL; }

    /*
     *  Optional hook into the SMN_INITIALUPDATE notification.
     */
    virtual void PrePopulate() { }

    /*
     *  Optional handler that says whether an item is still valid.
     */
    virtual BOOL IsItemStillValid(PaneItem *pitem) { return TRUE; }

    /*
     *  Required if HOSTF_CASCADEMENU.  Invoked when user wants to view
     *  a cascaded menu.
     */
    virtual HRESULT GetCascadeMenu(PaneItem *pitem, IShellMenu **ppsm) { return E_FAIL; }

    /*
     *  Required if any items have subtitles.  Returns the subtitle of the item.
     */
    virtual LPTSTR SubtitleOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem) { return NULL; }

    /*
     *  Optionally over-ridable method to ge the infotip for an item.  Default does a GetFolderAndPidl/GetInfoTip.
     */
    virtual void GetItemInfoTip(PaneItem *pitem, LPTSTR pszText, DWORD cch);

    /*
     *  Specify whether the data object can be inserted into the pin list.
     *  (Default: No.)
     */
    virtual BOOL IsInsertable(IDataObject *pdto) { return FALSE; }

    /*
     *  If you say that something is insertable, they you may be asked to
     *  insert it.
     */
    virtual HRESULT InsertPinnedItem(IDataObject *pdto, int iInsert)
    {
        ASSERT(FALSE); // You must implement this if you implement IsInsertable
        return E_FAIL;
    }

    /*
     *  An over-ridable method to allow hooking into keyboard accelerators.
     */
    virtual TCHAR GetItemAccelerator(PaneItem *pitem, int iItemStart);

    /*
     *  Specify whether the item should be displayed as bold.
     *  Default is to boldface if pinned.
     */
    virtual BOOL IsBold(PaneItem *pitem) { return pitem->IsPinned(); }

    /*
     *  Notify the client that a system imagelist index has changed.
     *  Default is to re-extract icons for any matching listview items.
     */
    virtual void UpdateImage(int iImage);

    /*
     *  Optional method to allow clients to specify how "Delete"
     *  should be exposed (if at all).  Return 0 to disallow "Delete".
     *  Return the string ID of the string to show for the command.
     *  Set *puiFlags to any additional flags to pass to ModifyMenu.
     *  Default is to disallow delete.
     */
    virtual UINT AdjustDeleteMenuItem(PaneItem *pitem, UINT *puiFlags) { return 0; }

    /*
     *  Allow client to reject/over-ride the IContextMenu on a per-item basis
     */

    virtual HRESULT _GetUIObjectOfItem(PaneItem *pitem, REFIID riid, LPVOID *ppv);

protected:
    /*
     *  Classes which derive from this class may call the following
     *  helper methods.
     */

    /*
     * Add a PaneItem to the list - if add fails, item will be delete'd.
     *
     * CLEANUP psf must be NULL; pidl must be the absolute pidl to the item
     * being added.  Leftover from dead HOSTF_PINITEMSBYFOLDER feature.
     * Needs to be cleaned up.
     *
     * Passing psf and pidlChild are for perf.
     */
    BOOL AddItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild);

    /* 
     * Use AddImage when you already have a HICON that needs to go to the private image list.
     */
    int AddImage(HICON hIcon);

    /*
     * Hooking into change notifications
     */
    enum {
        SFTHOST_MAXCLIENTNOTIFY = 7,        // Clients get this many notifications
        SFTHOST_MAXHOSTNOTIFY = 1,          // We use this many ourselves
        SFTHOST_HOSTNOTIFY_UPDATEIMAGE = SFTHOST_MAXCLIENTNOTIFY,
        SFTHOST_MAXNOTIFY = SFTHOST_MAXCLIENTNOTIFY + SFTHOST_MAXHOSTNOTIFY,
    };

    BOOL RegisterNotify(UINT id, LONG lEvents, LPCITEMIDLIST pidl, BOOL fRecursive)
    {
        ASSERT(id < SFTHOST_MAXCLIENTNOTIFY);
        return _RegisterNotify(id, lEvents, pidl, fRecursive);
    }

    BOOL UnregisterNotify(UINT id);

    /*
     * Forcing a re-enumeration.
     */
    void Invalidate() { _fEnumValid = FALSE; }

    /*
     * Informing host of desired size.
     */
    void SetDesiredSize(int cPinned, int cNormal)
    {
        _cPinnedDesired = cPinned;
        _cNormalDesired = cNormal;
    }

    BOOL AreNonPinnedItemsDesired()
    {
        return _cNormalDesired;
    }

    void StartRefreshTimer() { SetTimer(_hwnd, IDT_REFRESH, 5000, NULL); }

    void ForceChange() { _fForceChange = TRUE; }
protected:
    /*
     *  The constructor must be marked "protected" so people can derive
     *  from us.
     */

    enum {
        HOSTF_FIREUEMEVENTS     = 0x00000001,
        HOSTF_CANDELETE         = 0x00000002,
        HOSTF_Unused            = 0x00000004, // recycle me!
        HOSTF_CANRENAME         = 0x00000008,
        HOSTF_REVALIDATE        = 0x00000010,
        HOSTF_RELOADTEXT        = 0x00000020, // requires HOSTF_REVALIDATE
        HOSTF_CASCADEMENU       = 0x00000040,
    };

    SFTBarHost(DWORD dwFlags = 0)
                : _dwFlags(dwFlags)
                , _lRef(1)
                , _iInsert(-1)
                , _clrBG(CLR_INVALID)
                , _iCascading(-1)
    {
    }
    
    enum {
        SFTBM_REPOPULATE = WM_USER,
        SFTBM_CHANGENOTIFY,
        SFTBM_REFRESH = SFTBM_CHANGENOTIFY + SFTHOST_MAXNOTIFY,
        SFTBM_CASCADE,
        SFTBM_ICONUPDATE,
    };

public:
    /*
     *  Interface stuff...
     */

    // *** IUnknown ***
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvOut);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // *** IDropTarget ***
    STDMETHODIMP DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    STDMETHODIMP DragLeave();
    STDMETHODIMP Drop(IDataObject *pdto, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);

    // *** IDropSource ***
    STDMETHODIMP GiveFeedback(DWORD dwEffect);
    STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);

    // *** IAccessible overridden methods ***
    STDMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole);
    STDMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState);
    STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut);
    STDMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction);
    STDMETHODIMP accDoDefaultAction(VARIANT varChild);

    // Helpers

    //
    //  It is pointless to move an object to a place adjacent to itself,
    //  because the end result is that nothing happens.
    //
    inline IsInsertMarkPointless(int iInsert)
    {
        return _fDragToSelf &&
               IsInRange(iInsert, _iPosDragOut, _iPosDragOut + 1);
    }

    void _PurgeDragDropData();
    HRESULT _DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
    HRESULT _TryInnerDropTarget(int iItem, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect);
    void _ClearInnerDropTarget();
    void _SetDragOver(int iItem);

    // Insert mark stuff
    void _SetInsertMarkPosition(int iInsert);
    void _InvalidateInsertMark();
    BOOL _GetInsertMarkRect(LPRECT prc);
    BOOL _IsInsertionMarkActive() { return _iInsert >= 0; }
    void _DrawInsertionMark(LPNMLVCUSTOMDRAW plvcd);

    /*
     *  End of drag/drop stuff...
     */

private:
    /*
     *  Background enumeration stuff...
     */
    class CBGEnum : public CRunnableTask {
    public:
        CBGEnum(SFTBarHost *phost, BOOL fUrgent)
            : CRunnableTask(RTF_DEFAULT)
            , _fUrgent(fUrgent)
            , _phost(phost) { phost->AddRef(); }
        ~CBGEnum() 
        {
            // We should not be the last release or else we are going to deadlock here, when _phost
            // tries to release the scheduler
            ASSERT(_phost->_lRef > 1);
            _phost->Release(); 
        }
        STDMETHODIMP RunInitRT()
        {
            _phost->_EnumerateContentsBackground();
            if (_phost->_hwnd) PostMessage(_phost->_hwnd, SFTBM_REPOPULATE, _fUrgent, 0);
            return S_OK;
        }
    private:
        SFTBarHost *_phost;
        BOOL _fUrgent;
    };

    friend class SFTBarHost::CBGEnum;

private:
    /* Window procedure helpers */

    static LRESULT CALLBACK _WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    static LRESULT _OnNcCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnNcDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    LRESULT _OnNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnContextMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnCtlColorStatic(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnMenuMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnEraseBackground(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnTimer(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnSetFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnSysColorChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnForwardMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnUpdateUIState(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    LRESULT _OnRepopulate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnChangeNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnRefresh(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnCascade(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnIconUpdate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    LRESULT _OnLVCustomDraw(LPNMLVCUSTOMDRAW plvcd);
    LRESULT _OnLVNItemActivate(LPNMITEMACTIVATE pnmia);
    LRESULT _OnLVNGetInfoTip(LPNMLVGETINFOTIP plvn);
    LRESULT _OnLVNGetEmptyText(NMLVDISPINFO *plvdi);
    LRESULT _OnLVNBeginDrag(LPNMLISTVIEW plv);
    LRESULT _OnLVNBeginLabelEdit(NMLVDISPINFO *pldvi);
    LRESULT _OnLVNEndLabelEdit(NMLVDISPINFO *pldvi);
    LRESULT _OnLVNKeyDown(LPNMLVKEYDOWN pkd);
    LRESULT _OnSMNGetMinSize(PSMNGETMINSIZE pgms);
    LRESULT _OnSMNFindItem(PSMNDIALOGMESSAGE pdm);
    LRESULT _OnSMNFindItemWorker(PSMNDIALOGMESSAGE pdm);
    LRESULT _OnSMNDismiss();
    LRESULT _OnHover();

    /* Custom draw helpers */
    LRESULT _OnLVPrePaint(LPNMLVCUSTOMDRAW plvcd);
    LRESULT _OnLVItemPrePaint(LPNMLVCUSTOMDRAW plvcd);
    LRESULT _OnLVSubItemPrePaint(LPNMLVCUSTOMDRAW plvcd);
    LRESULT _OnLVItemPostPaint(LPNMLVCUSTOMDRAW plvcd);
    LRESULT _OnLVPostPaint(LPNMLVCUSTOMDRAW plvcd);

    /* Custom draw push/pop */
    void    _CustomDrawPush(BOOL fReal);
    BOOL    _IsRealCustomDraw();
    void    _CustomDrawPop();

    /* Other helpers */
    void _SetMaxShow(int cx, int cy);
    void _EnumerateContents(BOOL fUrgent);
    void _EnumerateContentsBackground();
    void _RevalidateItems();
    void _RevalidatePostPopup();
    void _ReloadText();
    static int CALLBACK _SortItemsAfterEnum(PaneItem *p1, PaneItem *p2, SFTBarHost *self);
    void _RepopulateList();
    void _InternalRepopulateList();
    int _InsertListViewItem(int iPos, PaneItem *pitem);
    void _ComputeListViewItemPosition(int iItem, POINT *pptOut);
    int _AppendEnumPaneItem(PaneItem *pitem);
    void _RepositionItems();
    void _ComputeTileMetrics();
    void _SetTileWidth(int cxTile);
    BOOL _CreateMarlett();
    void _CreateBoldFont();
    int  _GetLVCurSel() {
            return ListView_GetNextItem(_hwndList, -1, LVNI_FOCUSED);
    }
    BOOL _OnCascade(int iItem, DWORD dwFlags);
    BOOL _IsPrivateImageList() const { return _iconsize == ICONSIZE_MEDIUM; }
    BOOL _CanHaveSubtitles() const { return _iconsize == ICONSIZE_LARGE; }
    int _ExtractImageForItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidl);
    void _ClearListView();
    void _EditLabel(int iItem);
    BOOL _RegisterNotify(UINT id, LONG lEvents, LPCITEMIDLIST pidl, BOOL fRecursive);
    void _OnUpdateImage(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);

    /* Returns E_FAIL for separators; otherwise calls client */
    HRESULT _GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut);

    /* Simple wrappers - the string needs to be freed with SHFree */
    LPTSTR _DisplayNameOfItem(PaneItem *pitem, UINT shgno);
    HRESULT _GetUIObjectOfItem(int iItem, REFIID riid, LPVOID *ppv);

    inline PaneItem *_GetItemFromLVLParam(LPARAM lParam)
        { return reinterpret_cast<PaneItem*>(lParam); }
    PaneItem *_GetItemFromLV(int iItem);

    enum {
        AIF_KEYBOARD = 1,
    };

    int _ContextMenuCoordsToItem(LPARAM lParam, POINT *pptOut);
    LRESULT _ActivateItem(int iItem, DWORD dwFlags); // AIF_* values
    HRESULT _InvokeDefaultCommand(int iItem, IShellFolder *psf, LPCITEMIDLIST pidl);
    void _OfferDeleteBrokenItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidl);

    // If you hover the mouse for this much time, we will open it if it
    // cascades.  This is the same value that USER uses for auto-cascading
    // menus.
    DWORD _GetCascadeHoverTime() { return GetDoubleClickTime() * 4 / 5; }

    static void CALLBACK SetIconAsync(LPCITEMIDLIST pidl, LPVOID pvData, LPVOID pvHint, INT iIconIndex, INT iOpenIconIndex);

    /*
     *  Custom commands we add to the context menu.
     */
    enum {
        IDM_REMOVEFROMLIST = 1,
        // Insert private menu items here

        // range used for client QueryContextMenu
        IDM_QCM_MIN   = 0x0100,
        IDM_QCM_MAX   = 0x7000,

    };

    /*
     *  Timer IDs
     */
    enum {
        IDT_ASYNCENUM  = 1,
        IDT_RELOADTEXT = 2,
        IDT_REFRESH    = 3,
    };

    /*
     *  Miscellaneous settings.
     */
    enum {
        MAX_SEPARATORS = 3,                 /* Maximum number of separators allowed */
    };

    /*
     *  Pinning helpers...
     */
    BOOL NeedSeparator() const { return _cPinned; }
    BOOL _HasSeparators() const { return _rgiSep[0] >= 0; }
    void _DrawSeparator(HDC hdc, int x, int y);
    void _DrawSeparators(LPNMLVCUSTOMDRAW plvcd);

    /*
     *  Bookkeeping.
     */
    int _PosToItemNo(int iPos);
    int _ItemNoToPos(int iItem);

    /*
     *  Accessibility helpers...
     */
    PaneItem *_GetItemFromAccessibility(const VARIANT& varChild);

    /*
     *  Debugging helpers...
     */
#if defined(DEBUG) && defined(FULL_DEBUG)
    void _DebugConsistencyCheck();
#else
    inline void _DebugConsistencyCheck() { }
#endif
    BOOL _AreChangesRestricted() 
    {
        return (IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOCHANGESTARMENU, TEXT("Advanced"), TEXT("Start_EnableDragDrop"), ROUS_DEFAULTALLOW | ROUS_KEYALLOWS));
    }

protected:
    HTHEME                  _hTheme;        // theme handle, can be NULL
    int                     _iThemePart;    // SPP_PROGLIST SPP_PLACESLIST
    int                     _iThemePartSep; // theme part for the separator
    HWND                    _hwnd;          /* Our window handle */
    HIMAGELIST              _himl;          // Imagelist handle
    int                     _cxIcon;        /* Icon size for imagelist */
    int                     _cyIcon;        /* Icon size for imagelist */
    ICONSIZE                _iconsize;      /* ICONSIZE_* value */

private:
    HWND                    _hwndList;      /* Handle of inner listview */

    MARGINS                 _margins;       // margins for children (listview and oobe static) valid in theme and non-theme case

    int                     _cPinned;       /* Number of those items that are pinned */

    DWORD                   _dwFlags;       /* Misc flags that derived classes can set */

    //  _dpaEnum is the DPA of enumerated items, sorted in the
    //  _SortItemsAfterEnum sense, which prepares them for _RepopulateList.
    //  When _dpaEnum is destroyed, its pointers must be delete'd.
    CDPA<PaneItem>          _dpaEnum;
    CDPA<PaneItem>          _dpaEnumNew; // Used during background enumerations

    int                     _rgiSep[MAX_SEPARATORS];    /* Only _cSep elements are meaningful */
    int                     _cSep;          /* Number of separators */

    //
    //  Context menu handling
    //
    IContextMenu2 *         _pcm2Pop;       /* Currently popped-up context menu */
    IContextMenu3 *         _pcm3Pop;       /* Currently popped-up context menu */

    IDropTargetHelper *     _pdth;          /* For cool-looking drag/drop */
    IDragSourceHelper *     _pdsh;          /* For cool-looking drag/drop */
    IDataObject *           _pdtoDragOut;   /* Data object being dragged out */
    IDataObject *           _pdtoDragIn;    /* Data object being dragged in */
    IDropTarget *           _pdtDragOver;   /* Object being dragged over (if any) */

    IShellTaskScheduler *   _psched;        /* Task scheduler */

    int                     _iDragOut;      /* The item being dragged out (-1 if none) */
    int                     _iPosDragOut;   /* The position of item _iDragOut */
    int                     _iDragOver;     /* The item being dragged over (-1 if none) */
    DWORD                   _tmDragOver;    /* Time the dragover started (to see if we need to auto-open) */

    int                     _iInsert;       /* Where the insert mark should be drawn (-1 if none) */
    BOOL                    _fForceArrowCursor; /* Should we force a regular cursor during drag/drop? */
    BOOL                    _fDragToSelf;   /* Are we dragging an object to ourselves? */
    BOOL                    _fInsertable;   /* Is item being dragged pinnable? */
    DWORD                   _grfKeyStateLast; /* Last grfKeyState passed to DragOver */

    int                     _cyTile;        /* Height of a tile */
    int                     _cxTile;        /* Width of a tile */
    int                     _cyTilePadding; /* Extra vertical space between tiles */
    int                     _cySepTile;     /* Height of a separator tile */
    int                     _cySep;         /* Height of a separator line */

    int                     _cxMargin;      /* Left margin */
    int                     _cyMargin;      /* Top margin */
    int                     _cxIndent;      /* So bonus texts line up with listview text */
    COLORREF                _clrBG;         /* Color for background */
    COLORREF                _clrHot;        /* Color for hot text*/
    COLORREF                _clrSubtitle;   /* Color for subtitle text*/


    LONG                    _lRef;          /* Reference count */
    BOOL                    _fBGTask;       /* Is a background task already scheduled? */
    BOOL                    _fRestartEnum;  /* Should in-progress enumeration be restarted? */
    BOOL                    _fRestartUrgent;/* Is the _fRestartEnum urgent? */
    BOOL                    _fEnumValid;    /* Is the list of items all fine? */
    BOOL                    _fNeedsRepopulate; /* Do we need to call _RepopulateList ? */
    BOOL                    _fForceChange;  /* Should we act as if there was a change even if there didn't seem to be one? */
    ULONG                   _rguChangeNotify[SFTHOST_MAXNOTIFY];
                                            /* Outstanding change notification (if any) */

    BOOL                    _fAllowEditLabel; /* Is this an approved label-editing state? */

    HFONT                   _hfList;        /* Custom listview font (if required) */
    HFONT                   _hfBold;        /* Bold listview font (if required) */
    HFONT                   _hfMarlett;     /* Marlett font (if required) */
    int                     _cxMarlett;     /* Width of the menu cascade glyph */
    int                     _tmAscentMarlett; /* Font ascent for Marlett */

    HWND                    _hwndAni;       /* Handle of flashlight animation, if present */
    UINT                    _idtAni;        /* Animation timer handle */
    HBRUSH                  _hBrushAni;     /* Background brush for the Ani window */

    int                     _cPinnedDesired;/* SetDesiredSize */
    int                     _cNormalDesired;/* SetDesiredSize */

    int                     _iCascading;    /* Which item is the cascade menu appearing over? */
    DWORD                   _dwCustomDrawState; /* Keeps track of whether customdraw is real or fake */
#ifdef DEBUG
    BOOL                    _fEnumerating;  /* Are we enumerating client items? */
    BOOL                    _fPopulating;   /* Are we populating the listview? */
    BOOL                    _fListUnstable; /* The listview is unstable; don't get upset */

    //
    //  To verify that we manage the inner drop target correctly.
    //
    enum {
        DRAGSTATE_UNINITIALIZED = 0,
        DRAGSTATE_ENTERED = 1,
    };
    int                     _iDragState;    /* for debugging */

#endif

    /* Large structures go at the end */
};

_inline SMPANEDATA* PaneDataFromCreateStruct(LPARAM lParam)
{
    LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
    return reinterpret_cast<SMPANEDATA*>(lpcs->lpCreateParams);
}

//****************************************************************************
//
//  Helper functions for messing with UEM info
//

void _GetUEMInfo(const GUID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam, UEMINFO *pueiOut);

#define _GetUEMPidlInfo(psf, pidl, pueiOut)                 \
        _GetUEMInfo(&UEMIID_SHELL, UEME_RUNPIDL,            \
                reinterpret_cast<WPARAM>(psf),              \
                reinterpret_cast<LPARAM>(pidl), pueiOut)

#define _GetUEMPathInfo(pszPath, pueiOut)                   \
    _GetUEMInfo(&UEMIID_SHELL, UEME_RUNPATH, (WPARAM)-1,    \
                reinterpret_cast<LPARAM>(pszPath), pueiOut)

#define _SetUEMPidlInfo(psf, pidl, pueiInOut)               \
        UEMSetEvent(&UEMIID_SHELL, UEME_RUNPIDL,            \
                reinterpret_cast<WPARAM>(psf),              \
                reinterpret_cast<LPARAM>(pidl), pueiInOut)

#define _SetUEMPathInfo(pszPath, pueiInOut)                 \
    UEMSetEvent(&UEMIID_SHELL, UEME_RUNPATH, (WPARAM)-1,    \
                reinterpret_cast<LPARAM>(pszPath), pueiInOut)

// SOMEDAY: Figure out what UEMF_XEVENT means.  I just stole the code
//          from startmnu.cpp.

#define _FireUEMPidlEvent(psf, pidl)                        \
    UEMFireEvent(&UEMIID_SHELL, UEME_RUNPIDL, UEMF_XEVENT,  \
                reinterpret_cast<WPARAM>(psf),              \
                reinterpret_cast<LPARAM>(pidl))


//****************************************************************************
//
//  Constructors for derived classes
//

typedef SFTBarHost *(CALLBACK *PFNHOSTCONSTRUCTOR)(void);

STDAPI_(SFTBarHost *) ByUsage_CreateInstance();
STDAPI_(SFTBarHost *) SpecList_CreateInstance();
STDAPI_(SFTBarHost *) RecentDocs_CreateInstance();

#define RECTWIDTH(rc)   ((rc).right-(rc).left)
#define RECTHEIGHT(rc)  ((rc).bottom-(rc).top)

#endif // __SFTHOST_H__