// Start.cpp : DirectUI 
//

#include "stdafx.h"

#ifdef FEATURE_STARTPAGE

#include <lmcons.h> // for UNLEN
#include <shlapip.h>
#include <DUserCtrl.h>

#include "pidlbutton.h"

#include "ProgList.h"
#include "hostutil.h"

#include "..\rcids.h"

using namespace DirectUI;

// Using ALL controls
UsingDUIClass(Element);
UsingDUIClass(HWNDElement);

UsingDUIClass(Button);
UsingDUIClass(Edit);
UsingDUIClass(Progress);
UsingDUIClass(RefPointElement);
UsingDUIClass(RepeatButton);
UsingDUIClass(ScrollBar);
UsingDUIClass(ScrollViewer);
UsingDUIClass(Selector);
UsingDUIClass(Thumb);
UsingDUIClass(Viewer);


EXTERN_C void Tray_OnStartMenuDismissed();
EXTERN_C void Tray_OnStartPageDismissed();
EXTERN_C void Tray_MenuInvoke(int idCmd);
EXTERN_C void Tray_SetStartPaneActive(BOOL fActive);
EXTERN_C void Tray_DoProperties(DWORD nStartPage);

HBITMAP GetBitmapForIDList(IShellFolder *psf, LPCITEMIDLIST pidlLast, UINT cx, UINT cy)
{
    HBITMAP hBitmap = NULL;
    IExtractImage *pei;
    HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractImage, &pei));
    if (SUCCEEDED(hr))
    {
        DWORD dwPriority;
        DWORD dwFlags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
        SIZEL rgSize = {cx, cy};

        WCHAR szBufferW[MAX_PATH];
        hr = pei->GetLocation(szBufferW, ARRAYSIZE(szBufferW), &dwPriority, &rgSize, SHGetCurColorRes(), &dwFlags);
        if (S_OK == hr)
        {
            hr = pei->Extract(&hBitmap);
        }
    }
    return hBitmap;
}


HBITMAP GetBitmapForFile(LPCITEMIDLIST pidl, LPWSTR pszFile, UINT cx, UINT cy)
{
    HBITMAP hbmp = NULL;
    if (pidl)
    {
        IShellFolder *psf;
        HRESULT hr = SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IShellFolder, &psf));
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST pidlChild;
            hr = psf->ParseDisplayName(NULL, NULL, pszFile, NULL, &pidlChild, NULL);
            if (SUCCEEDED(hr))
            {
                hbmp = GetBitmapForIDList(psf, pidlChild, cx, cy);
                ILFree(pidlChild);
            }
            psf->Release();
        }
    }
    else
    {
        LPITEMIDLIST pidlFull = ILCreateFromPath(pszFile);
        if (pidlFull)
        {
            IShellFolder *psf;
            LPCITEMIDLIST pidlChild;
            HRESULT hr = SHBindToParent(pidlFull, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
            if (SUCCEEDED(hr))
            {
                hbmp = GetBitmapForIDList(psf, pidlChild, cx, cy);
                psf->Release();
            }

            ILFree(pidlFull);
        }
    }
    return hbmp;
}




////////////////////////////////////////////////////////
// StartFrame
////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
// Frame declaration

class StartFrame : public HWNDElement, public ByUsageDUI
{
public:
    static HRESULT Create(OUT Element** ppElement);
    static HRESULT Create(HWND hwnd, HINSTANCE hInst, OUT Element** ppElement);

    virtual void OnInput(InputEvent* pie);
    virtual void OnEvent(Event* pEvent);
    virtual LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    virtual void OnKeyFocusMoved(Element* peFrom, Element* peTo);


    HRESULT Populate();

    static void CALLBACK ParseError(LPCWSTR pszError, LPCWSTR pszToken, int dLine);
    static int CALLBACK _SortItemsAfterEnum(PaneItem *p1, PaneItem *p2, ByUsage *pbu);

    // ByUsageDUI
    virtual BOOL AddItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild);
    virtual BOOL RegisterNotify(UINT id, LONG lEvents, LPITEMIDLIST pidl, BOOL fRecursive);
    virtual BOOL UnregisterNotify(UINT id);
    virtual HRESULT Register();


    enum {TIMER_RECENT, TIMER_PROGRAMS, TIMER_ANIMATE};
    StartFrame();

protected:
    virtual ~StartFrame();
    HRESULT Initialize(HWND hwnd) { return HWNDElement::Initialize(hwnd, true /* false */, 0); }

    IShellFolder *GetRecentFilesFolder(LPITEMIDLIST *ppidl);
    HRESULT InvokePidl(LPITEMIDLIST pidl);

    LRESULT _OnMenuMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnSetMenuForward(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnChangeNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnTimer(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT _OnActivate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);

private:

    BOOL _IsShortcutToFolder(IShellFolder *psf, LPCITEMIDLIST pidl);
    HRESULT _PopulateSpecialFolders();
    HRESULT _PopulateRecentDocuments();
    HRESULT _PopulateRecentPrograms();
    HRESULT _PopulatePictures();

    HRESULT _SetPicture(Element *pe);


    HINSTANCE    _hInstance;

    LPITEMIDLIST _pidlBrowser;
    LPITEMIDLIST _pidlEmail;
    LPITEMIDLIST _pidlSearch;

    Element *   _peAnimating;
    int         _iAnimationColor;
    //
    //  Context menu handling
    //
    PIDLButton *        _pPidlButtonPop;    /* has currently popped-up context menu */

    // Handler for the recent programs list
    ByUsage *           _pByUsage;
    //
    //  _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;

    enum { DUI_MAXNOTIFY = 5 };
    ULONG                   _rguChangeNotify[DUI_MAXNOTIFY];
    
    enum { IDCN_LASTMOD = DUI_MAXNOTIFY -1 };

                                            /* Outstanding change notification (if any) */
    enum {
        SPM_CHANGENOTIFY = WM_USER + 2,  // WM_USER+1 is already being used by the pidl gadgets, WM_USER is used by DirectUI
        SPM_SET_THUMBNAIL = SPM_CHANGENOTIFY + DUI_MAXNOTIFY
    };

    enum { SPM_ACTIVATE = WM_USER + 107  }; // Bad HACK to get activation loss info

    static WCHAR _szParseError[];
    static int _dParseError;

    int _iMaxShow;    // How many items we should show in the recent lists

public:
    // ClassInfo accessors (static and virtual instance-based)
    static IClassInfo* Class;
    virtual IClassInfo* GetClassInfo() { return Class; }
};

////////////////////////////////////////////////////////
// Frame construction
StartFrame::StartFrame() 
{
    _pidlBrowser = ILCreateFromPath(TEXT("shell:::{2559a1f4-21d7-11d4-bdaf-00c04f60b9f0}"));
    _pidlEmail   = ILCreateFromPath(TEXT("shell:::{2559a1f5-21d7-11d4-bdaf-00c04f60b9f0}"));
    _pidlSearch  = ILCreateFromPath(TEXT("shell:::{2559a1f0-21d7-11d4-bdaf-00c04f60b9f0}"));
}

StartFrame::~StartFrame() 
{
    ILFree(_pidlBrowser);
    ILFree(_pidlEmail);
    ILFree(_pidlSearch);
}

HRESULT StartFrame::Create(OUT Element** ppElement)
{
    UNREFERENCED_PARAMETER(ppElement);
    DUIAssertForce("Cannot instantiate an HWND host derived Element via parser. Must use substitution.");
    return E_NOTIMPL;
}

HRESULT StartFrame::Create(HWND hwnd, HINSTANCE hInst, OUT Element** ppElement)
{
    *ppElement = NULL;

    StartFrame* ppf = HNew<StartFrame>();
    if (!ppf)
        return E_OUTOFMEMORY;

    HRESULT hr = ppf->Initialize(hwnd);
    if (FAILED(hr))
        return hr;

    hr = ppf->SetActive(AE_MouseAndKeyboard);
    ppf->_hInstance = hInst;

    *ppElement = ppf;

    return S_OK;
}

HRESULT AddPidlToList(LPITEMIDLIST pidl, Element *peList, BOOL fInsert)
{
    Element *pPidlButton;
    HRESULT hr = PIDLButton::Create(pidl, &pPidlButton);
    if (SUCCEEDED(hr))
    {
        if (fInsert)
            hr = peList->Insert(pPidlButton, 0);
        else
            hr = peList->Add(pPidlButton);

        if (FAILED(hr))
        {
            pPidlButton->Destroy();
        }
    }
    else
    {
        ILFree(pidl);
    }
    return hr;
}


HRESULT InsertCSIDLIntoList(int csidl, Element *peList)
{
    LPITEMIDLIST pidl;
    HRESULT hr = SHGetFolderLocation(NULL, csidl, NULL, 0, &pidl);
    if (SUCCEEDED(hr))
    {
        hr = AddPidlToList(pidl, peList, TRUE);  // Insert
    }
    return hr;
}

BOOL StartFrame::AddItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild)
{
    BOOL fRet = TRUE;
    if (_dpaEnum.AppendPtr(pitem) < 0)
    {
        fRet = FALSE;
        delete pitem;
    }
    return fRet;
}

BOOL StartFrame::_IsShortcutToFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
{
    BOOL fRc = FALSE;               // assume not
    IShellLink *psl;
    HRESULT hr;

    hr = psf->GetUIObjectOf(GetHWND(), 1, &pidl, IID_PPV_ARG_NULL(IShellLink, &psl));
    if (SUCCEEDED(hr))
    {
        LPITEMIDLIST pidlTarget;
        if (SUCCEEDED(psl->GetIDList(&pidlTarget)))
        {
            DWORD dwAttr = SFGAO_FOLDER | SFGAO_BROWSABLE;
            if (SUCCEEDED(SHGetAttributesOf(pidlTarget, &dwAttr)))
            {
                fRc = dwAttr & SFGAO_FOLDER;
            }
            ILFree(pidlTarget);
        }
        psl->Release();
    }
    return fRc;
}

IShellFolder *StartFrame::GetRecentFilesFolder(LPITEMIDLIST *ppidl)
{
    HRESULT hr;
    IShellFolder *psf = NULL;

    hr = SHGetSpecialFolderLocation(GetHWND(), CSIDL_RECENT, ppidl);
    if (SUCCEEDED(hr))
    {
        hr = SHBindToObjectEx(NULL, *ppidl, NULL, IID_PPV_ARG(IShellFolder, &psf));
        if (FAILED(hr))
        {
            ILFree(*ppidl);
            *ppidl = NULL;
        }
    }
    return psf;
}


HRESULT StartFrame::_PopulateSpecialFolders()
{
    Element *peList;
    peList = FindDescendent(StrToID(L"DocList"));
    if (peList)
    {
        PIDLButton::SetImageSize(SHGFI_LARGEICON);
        InsertCSIDLIntoList(CSIDL_BITBUCKET, peList);
        InsertCSIDLIntoList(CSIDL_NETWORK, peList);
        InsertCSIDLIntoList(CSIDL_MYMUSIC, peList);
        InsertCSIDLIntoList(CSIDL_MYPICTURES, peList);
        InsertCSIDLIntoList(CSIDL_PERSONAL, peList);
    }

    peList = FindDescendent(StrToID(L"SystemDocList"));
    if (peList)
    {
        LPITEMIDLIST pidlHelp = ILCreateFromPath(TEXT("shell:::{2559a1f1-21d7-11d4-bdaf-00c04f60b9f0}")); // Help and support
        if (pidlHelp)
            AddPidlToList(pidlHelp, peList, TRUE); // Insert

        InsertCSIDLIntoList(CSIDL_CONTROLS, peList);
        InsertCSIDLIntoList(CSIDL_DRIVES, peList);
    }
    return S_OK;
}

HRESULT StartFrame::_PopulateRecentDocuments()
{
    Selector *peList;

    peList = (Selector*)FindDescendent(StrToID(L"Documents"));
    if (peList)
    {
        PIDLButton::SetImageSize(SHGFI_LARGEICON);
        peList->DestroyAll();

        IEnumIDList *peidl;
        LPITEMIDLIST pidlRoot;

        IShellFolder *psfRecentFiles = GetRecentFilesFolder(&pidlRoot);

        // The CSIDL_RECENT folder is magic:  The items are enumerated
        // out of it in MRU order!

        if (psfRecentFiles)
        {
            if (SUCCEEDED(psfRecentFiles->EnumObjects(GetHWND(), SHCONTF_NONFOLDERS, &peidl)))
            {
                LPITEMIDLIST pidl;
                int cAdded = 0;

                while (cAdded < _iMaxShow && peidl->Next(1, &pidl, NULL) == S_OK)
                {
                    // Filter out shortcuts to folders
                    if (!_IsShortcutToFolder(psfRecentFiles, pidl))
                    {
                        LPITEMIDLIST pidlFull = ILCombine(pidlRoot, pidl);
                        if (pidlFull)
                        {
                            // AddPidlToList takes ownership of the pidl
                            AddPidlToList(pidlFull, peList, FALSE);
                            cAdded++;
                        }
                    }
                    ILFree(pidl);
                }
                peidl->Release();
            }
            RegisterNotify(IDCN_LASTMOD, SHCNE_DISKEVENTS | SHCNE_UPDATEIMAGE, pidlRoot, FALSE);
            ILFree(pidlRoot);
            psfRecentFiles->Release();
        }
    }
    return S_OK;
}

int CALLBACK StartFrame::_SortItemsAfterEnum(PaneItem *p1, PaneItem *p2, ByUsage *pbu)
{
    //
    //  Put all pinned items (sorted by pin position) ahead of unpinned items.
    //
    if (p1->IsPinned())
    {
        if (p2->IsPinned())
        {
            return p1->GetPinPos() - p2->GetPinPos();
        }
        return -1;
    }
    else if (p2->IsPinned())
    {
        return +1;
    }

    //
    //  Both unpinned - let the client decide.
    //
    return pbu->CompareItems(p1, p2);
}

HRESULT StartFrame::_PopulateRecentPrograms()
{
    Element *peList;

    peList = FindDescendent(StrToID(L"Programs"));
    if (peList)
    {
        PIDLButton::SetImageSize(SHGFI_LARGEICON);

        peList->DestroyAll();

        if (!_pByUsage)
        {
            _pByUsage =  new ByUsage(NULL, static_cast<ByUsageDUI *>(this));
            if (_pByUsage)
            {
                IPropertyBag *pbag;
                if(SUCCEEDED(CreatePropBagFromReg(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartPage\\Normal\\W32Control1"), &pbag)))
                {
                    if (SUCCEEDED(_pByUsage->Initialize()) && _dpaEnum.Create(4))
                    {
                    }
                    else
                    {
                        delete _pByUsage;
                        _pByUsage = NULL;
                    }

                    pbag->Release();
                }
            }
        }

        if (_pByUsage)
        {
            _pByUsage->EnumItems();
            _dpaEnum.SortEx(_SortItemsAfterEnum, _pByUsage);

            int iPos;                   // the slot we are trying to fill
            int iEnum;                  // the item index we will fill it from
            PaneItem *pitem;            // the item that will fill it

            // Note that the loop control must be a _dpaEnum.GetPtr(), not a
            // _dpaEnum.FastGetPtr(), because iEnum can go past the end of the
            // array if we do't have _iMaxShow items in the first place.
            //

            for (iPos = iEnum = 0;
                iEnum < _iMaxShow && (pitem = _dpaEnum.GetPtr(iEnum)) != NULL;
                iEnum++)
            {
                LPITEMIDLIST pidl = _pByUsage->GetFullPidl(pitem);
                if (pidl)
                {
                    AddPidlToList(pidl, peList, FALSE);
                }
            }

            // Clear out the DPA

            _dpaEnum.EnumCallbackEx(PaneItem::DPAEnumCallback, (LPVOID)NULL);
            _dpaEnum.DeleteAllPtrs();
        }
    }

    return S_OK;
}

LPWSTR GetStrPictureFromID(int nImageID)
{
    LPWSTR pszPic = NULL;
    switch (nImageID)
    {
    case 1: pszPic = L"Picture1";
            break;
    case 2: pszPic = L"Picture2";
            break;
    case 3: pszPic = L"Picture3";
            break;
    }
    return pszPic;
}

BOOL IsValidExtension(LPWSTR pszPath)
{
    if (pszPath)
    {
        WCHAR *pszExt = PathFindExtension(pszPath);
        if (pszExt && (lstrcmpi(pszExt, TEXT(".bmp")) == 0 || lstrcmpi(pszExt, TEXT(".jpg")) == 0 ||
                        lstrcmpi(pszExt, TEXT(".jpeg")) == 0))
            return TRUE;
    }
    
    return FALSE;
}

HRESULT StartFrame::_PopulatePictures()
{
    LPITEMIDLIST pidl;
    HRESULT hr = SHGetFolderLocation(NULL, CSIDL_MYPICTURES, NULL, 0, &pidl);
    if (SUCCEEDED(hr))
    {
        if (SUCCEEDED(hr))
        {
            WCHAR szPath[MAX_PATH];
            hr = SHGetPathFromIDList(pidl, szPath);
            if (SUCCEEDED(hr))
            {
                WIN32_FIND_DATA fd;
                HKEY hkey = NULL;
                RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartPage"), &hkey);

                StrCatBuff(szPath, TEXT("\\*"), ARRAYSIZE(szPath));
                
                int nImageID = 1;
                HANDLE hFind = FindFirstFile(szPath, &fd);
                while (nImageID <= 3)
                {
                    LPWSTR pszPic = GetStrPictureFromID(nImageID);
                    LPWSTR pszPathToImage = NULL;
                    LPITEMIDLIST pidlFolder = NULL;
                    if (hkey)
                    {
                        DWORD dwType;
                        DWORD cb = sizeof(szPath);
                        if (RegQueryValueEx(hkey, pszPic, NULL, &dwType, (LPBYTE)szPath, &cb) == ERROR_SUCCESS 
                            && dwType == REG_SZ && PathFileExists(szPath) && IsValidExtension(szPath))
                            pszPathToImage = szPath;
                    }

                    while (!pszPathToImage && hFind != INVALID_HANDLE_VALUE)
                    {
                        pidlFolder = pidl;
                        pszPathToImage = fd.cFileName;

                        if (!FindNextFile(hFind, &fd))
                        {
                            FindClose(hFind);
                            hFind = INVALID_HANDLE_VALUE;
                        }
                        // Skip the stupid sample
                        if (lstrcmpi(pszPathToImage, TEXT("sample.jpg")) == 0 || !IsValidExtension(pszPathToImage))
                            pszPathToImage = NULL;
                    }

                    if (pszPathToImage)
                    {
                        HBITMAP hbmp = GetBitmapForFile(pidlFolder, pszPathToImage, 150, 113);
                        if (hbmp)
                        {
                            Element *pe = FindDescendent(StrToID(pszPic));
                            if (pe)
                            {
                                Value *pvalIcon = Value::CreateGraphic(hbmp);
                                if (pvalIcon)
                                {
                                    pe->SetValue(ContentProp, PI_Local, pvalIcon);
                                    pvalIcon->Release();
                                }
                                else
                                {
                                    DeleteObject(hbmp);
                                }
                            }
                        }
                    }
                    nImageID++;
                }
                if (hFind != INVALID_HANDLE_VALUE)
                    FindClose(hFind);
                if (hkey)
                    RegCloseKey(hkey);
            }
        }
        ILFree(pidl);
    }

    return hr;
}

HRESULT StartFrame::_SetPicture(Element *pe)
{
    LPITEMIDLIST pidl;
    HRESULT hr = SHGetFolderLocation(NULL, CSIDL_MYPICTURES, NULL, 0, &pidl);
    if (SUCCEEDED(hr))
    {
        WCHAR szPath[MAX_PATH];
        hr = SHGetPathFromIDList(pidl, szPath);

        if (SUCCEEDED(hr))
        {
            OPENFILENAME ofn;       // common dialog box structure
            WCHAR szFile[MAX_PATH];       // buffer for file name

            *szFile = L'\0';

            // Initialize OPENFILENAME
            ZeroMemory(&ofn, sizeof(OPENFILENAME));
            ofn.lStructSize = sizeof(OPENFILENAME);
            ofn.hwndOwner = GetHWND();
            ofn.lpstrFile = szFile;
            ofn.nMaxFile = sizeof(szFile);
            ofn.lpstrFilter = TEXT("Pictures\0*.JPG;*.JPEG;*.BMP\0");
            ofn.nFilterIndex = 1;
            ofn.lpstrFileTitle = NULL;
            ofn.nMaxFileTitle = 0;
            ofn.lpstrInitialDir = szPath;
            ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_DONTADDTORECENT;

            // Display the Open dialog box. 

            if (GetOpenFileName(&ofn)==TRUE)
            {
                WCHAR *pszExt = PathFindExtension(ofn.lpstrFile);
                if (pszExt && (lstrcmpi(pszExt, TEXT(".bmp")) == 0 || lstrcmpi(pszExt, TEXT(".jpg")) == 0 ||
                                lstrcmpi(pszExt, TEXT(".jpg")) == 0))
                {
                    HBITMAP hbmp = GetBitmapForFile(NULL, ofn.lpstrFile, 150, 113);
                    if (hbmp)
                    {
                        Value *pvalIcon = Value::CreateGraphic(hbmp);
                        if (pvalIcon)
                        {
                            pe->SetValue(ContentProp, PI_Local, pvalIcon);
                            pvalIcon->Release();
                            HKEY hkey = NULL;
                            RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartPage"), &hkey);
                            if (hkey)
                            {
                                LPWSTR pszPic;
                                for (int i=1; i<=3; i++)
                                {
                                    pszPic = GetStrPictureFromID(i);
                                    if (FindDescendent(StrToID(pszPic)) == pe)
                                        break;
                                    pszPic = NULL;
                                }
                                if (pszPic)
                                    RegSetValueEx(hkey, pszPic, NULL, REG_SZ, (LPBYTE)ofn.lpstrFile, (lstrlen(ofn.lpstrFile)+1) * sizeof(TCHAR));
                                RegCloseKey(hkey);
                            }

                        }
                        else
                        {
                            DeleteObject(hbmp);
                        }
                    }
                }
            }
        }
    }
    return S_OK;
}

HRESULT StartFrame::Populate()
{
    Element *pe = FindDescendent(StrToID(L"name"));

    if (pe)
    {
        WCHAR szUserName[UNLEN + 1];
        ULONG uLen = ARRAYSIZE(szUserName);
        *szUserName = _T('\0');
        SHGetUserDisplayName(szUserName, &uLen);
        pe->SetContentString(szUserName);
    }

    pe = FindDescendent(StrToID(L"UserPicture"));
    if (pe)
    {
        TCHAR szUserPicturePath[MAX_PATH];
        if (SUCCEEDED(SHGetUserPicturePath(NULL, SHGUPP_FLAG_CREATE, szUserPicturePath)))
        {
            HBITMAP hbmUserPicture = (HBITMAP)LoadImage(NULL, szUserPicturePath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
            if (hbmUserPicture)
            {
                Value *pvalIcon = Value::CreateGraphic(hbmUserPicture);
                if (pvalIcon)
                {
                    pe->SetValue(ContentProp, PI_Local, pvalIcon);
                    pvalIcon->Release();
                }
                else
                {
                    DeleteObject(hbmUserPicture);
                }
            }
        }
    }

    _PopulatePictures();

    _PopulateSpecialFolders();

    return S_OK;
}


void StartFrame::OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    if (id == IDCN_LASTMOD) // Change in last documents
    {
        UnregisterNotify(id);
        KillTimer(GetHWND(), TIMER_RECENT);
        SetTimer(GetHWND(), TIMER_RECENT, 2000, NULL);
    }
    else
    {
        ASSERT(_pByUsage);
        if(_pByUsage)
        {
            _pByUsage->GetMenuCache()->OnChangeNotify(id, lEvent, pidl1, pidl2);
            KillTimer(GetHWND(), TIMER_PROGRAMS);
            SetTimer(GetHWND(), TIMER_PROGRAMS, 10 * 1000, NULL);
        }

    }
}

LRESULT StartFrame::_OnTimer(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (wParam == TIMER_RECENT)
    {
        KillTimer(hwnd, wParam);
        StartDefer();
        _PopulateRecentDocuments();
        EndDefer();
    }
    else if (wParam == TIMER_PROGRAMS)
    {
        KillTimer(hwnd, wParam);
        StartDefer();
        _PopulateRecentPrograms();
        EndDefer();
        SetTimer(GetHWND(), TIMER_PROGRAMS, 1 * 60 * 1000, NULL);  // 1 minute
    } 
    else if (wParam == TIMER_ANIMATE)
    {
        if (_iAnimationColor & 1)
        {
            _iAnimationColor += 16;
            if (_iAnimationColor == 255)
                KillTimer(hwnd, wParam);
        }
        else
        {
            if (_iAnimationColor > 16)
                _iAnimationColor -= 16;
            else 
                _iAnimationColor = 15;
        }
        _peAnimating->SetForegroundColor(RGB(_iAnimationColor, _iAnimationColor, (3*_iAnimationColor + 255) /4));
    }
    return 0;
}

BOOL StartFrame::RegisterNotify(UINT id, LONG lEvents, LPITEMIDLIST pidl, BOOL fRecursive)
{
    ASSERT(id < DUI_MAXNOTIFY);

    if (id < DUI_MAXNOTIFY)
    {
        UnregisterNotify(id);

        SHChangeNotifyEntry fsne;
        fsne.fRecursive = fRecursive;
        fsne.pidl = pidl;

        int fSources = SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel;
        _rguChangeNotify[id] = SHChangeNotifyRegister(GetHWND(), fSources, lEvents,
                                                      SPM_CHANGENOTIFY + id, 1, &fsne);
        return _rguChangeNotify[id];
    }
    return FALSE;
}

BOOL StartFrame::UnregisterNotify(UINT id)
{
    ASSERT(id < DUI_MAXNOTIFY);

    if (id < DUI_MAXNOTIFY && _rguChangeNotify[id])
    {
        UINT uChangeNotify = _rguChangeNotify[id];
        _rguChangeNotify[id] = 0;
        return SHChangeNotifyDeregister(uChangeNotify);
    }
    return FALSE;
}


////////////////////////////////////////////////////////
// System events

void StartFrame::OnInput(InputEvent* pie)
{
    HWNDElement::OnInput(pie);
}

void StartFrame::OnEvent(Event* pEvent)
{
    if (pEvent->nStage == GMF_BUBBLED)
    {
        if (pEvent->uidType == Button::Click)
        {
            if (pEvent->peTarget->GetID() == StrToID(L"email"))
            {
                InvokePidl(_pidlEmail);
            }
            else if (pEvent->peTarget->GetID() == StrToID(L"internet"))
            {
                InvokePidl(_pidlBrowser);
            }
            else if (pEvent->peTarget->GetID() == StrToID(L"search"))
            {
                InvokePidl(_pidlSearch);
            }
            else if (pEvent->peTarget->GetID() == StrToID(L"MorePrograms"))
            {
                LPITEMIDLIST pidl = ILCreateFromPath(TEXT("shell:::{7be9d83c-a729-4d97-b5a7-1b7313c39e0a}"));
                if (pidl)
                {
                    InvokePidl(pidl);
                    ILFree(pidl);
                }
            }
            else if (pEvent->peTarget->GetID() == StrToID(L"MoreDocuments"))
            {
                LPITEMIDLIST pidl = ILCreateFromPath(TEXT("shell:::{9387ae38-d19b-4de5-baf5-1f7767a1cf04}"));
                if (pidl)
                {
                    InvokePidl(pidl);
                    ILFree(pidl);
                }
            }
            else if (pEvent->peTarget->GetID() == StrToID(L"turnoff"))
            {
                Tray_MenuInvoke(IDM_EXITWIN);
            }
            else if (pEvent->peTarget->GetID() == StrToID(L"logoff"))
            {
                Tray_MenuInvoke(IDM_LOGOFF);
            }
            else
            {
                HMENU hmenu = LoadMenu(_hInstance, MAKEINTRESOURCE(IDM_PICTMENU));
                if (hmenu)
                {
                    HMENU hMenuTrack = GetSubMenu(hmenu, 0);
                    ButtonClickEvent *peButton = reinterpret_cast<ButtonClickEvent *>(pEvent);

                    if (peButton->pt.x == -1) // Keyboard context menu
                    {
                        Value *pv;
                        const SIZE *psize = pEvent->peTarget->GetExtent(&pv);
                        peButton->pt.x = psize->cx/2;
                        peButton->pt.y = psize->cy/2;
                        pv->Release();
                    }
                    POINT pt;
                    MapElementPoint(pEvent->peTarget, &peButton->pt, &pt);
                    ClientToScreen(GetHWND(), &pt);

                    int idCmd = TrackPopupMenuEx(hMenuTrack, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, GetHWND(), NULL);

                    DestroyMenu(hmenu);

                    if (idCmd == ID_CMD_CHANGE_PICTURE)
                    {
                        _SetPicture(pEvent->peTarget);
                    }
                }
            }
        }
    }
    HWNDElement::OnEvent(pEvent);
}

HRESULT StartFrame::InvokePidl(LPITEMIDLIST pidl)
{
    HRESULT hr = E_FAIL;
    IShellFolder *psf;
    LPCITEMIDLIST pidlShort;
    // Multi-level child pidl
    if (SUCCEEDED(SHBindToFolderIDListParent(NULL, pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlShort)))
    {
        hr = SHInvokeDefaultCommand(GetHWND(), psf, pidlShort);
        psf->Release();
    }
    return hr;
}

WCHAR StartFrame::_szParseError[201];
int StartFrame::_dParseError;

void StartFrame::ParseError(LPCWSTR pszError, LPCWSTR pszToken, int dLine)
{
    if (dLine != -1)
        swprintf(_szParseError, L"%s '%s' at line %d", pszError, pszToken, dLine);
    else
        swprintf(_szParseError, L"%s '%s'", pszError, pszToken);

    _dParseError = dLine;
}

LRESULT StartFrame::_OnSetMenuForward(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    _pPidlButtonPop = (PIDLButton *)lParam;
    return 0;
}

LRESULT StartFrame::_OnMenuMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (_pPidlButtonPop)
        return _pPidlButtonPop->OnMenuMessage(hwnd, uMsg, wParam, lParam);

    return 0;
}

LRESULT StartFrame::_OnChangeNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPITEMIDLIST *ppidl;
    LONG lEvent;
    LPSHChangeNotificationLock pshcnl;
    pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);

    if (pshcnl)
    {
        OnChangeNotify(uMsg - SPM_CHANGENOTIFY, lEvent, ppidl[0], ppidl[1]);
        SHChangeNotification_Unlock(pshcnl);
    }
    return 0;
}

LRESULT StartFrame::_OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    UINT id;
    for (id = 0; id < DUI_MAXNOTIFY; id++)
    {
        UnregisterNotify(id);
    }

    delete _pByUsage;
    _pByUsage = NULL;

#if 0  // REVIEW fabriced
    if (_hwndList)
    {
        RevokeDragDrop(_hwndList);
    }
    if (_psched)
    {
        _psched->RemoveTasks(TOID_SFTBarHostBackgroundEnum, (DWORD_PTR)this, FALSE);
    }
#endif

    return HWNDElement::WndProc(hwnd, uMsg, wParam, lParam);
}

LRESULT StartFrame::_OnActivate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (WA_ACTIVE == LOWORD(wParam))
    {
        // Run a little animation when we come up
        _peAnimating = FindDescendent(StrToID(L"name"));
        _iAnimationColor = 64;
        SetTimer(GetHWND(), TIMER_ANIMATE, 20, NULL);
    }
    // If we are loosing focus, reset the start button.
    else if (WA_INACTIVE == LOWORD(wParam))
    {
        if (_peAnimating)
            _peAnimating->SetForegroundColor(RGB(255, 255, 255));

        KillTimer(GetHWND(), TIMER_ANIMATE);
        Tray_SetStartPaneActive(FALSE);
        Tray_OnStartPageDismissed();
    }

    return 0;
}

LRESULT StartFrame::_OnSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    Element::StartDefer();
    SetWidth(LOWORD(lParam));
    SetHeight(HIWORD(lParam));
    if (HIWORD(lParam) < 590)
    {
        _iMaxShow = 7;
        Element *pe = FindDescendent(StrToID(L"CurveZone"));
        if (pe)
            pe->SetPadding(0, 0, 0, 74);
        pe = FindDescendent(StrToID(L"LogoffZone"));
        if (pe)
            pe->SetPadding(50, 0, 50, 10);
    }
    else if (HIWORD(lParam) < 700)
    {
        _iMaxShow = 7;
        Element *pe = FindDescendent(StrToID(L"CurveZone"));
        if (pe)
            pe->SetPadding(0, 0, 0, 105);
        pe = FindDescendent(StrToID(L"LogoffZone"));
        if (pe)
            pe->SetPadding(50, 0, 50, 41);
    }
    else if (HIWORD(lParam) < 760)
    {
        _iMaxShow = 10;
        Element *pe = FindDescendent(StrToID(L"CurveZone"));
        if (pe)
            pe->SetPadding(0, 0, 0, 74);
        pe = FindDescendent(StrToID(L"LogoffZone"));
        if (pe)
            pe->SetPadding(50, 0, 50, 10);
    }
    else
    {
        _iMaxShow = 10;
        Element *pe = FindDescendent(StrToID(L"CurveZone"));
        if (pe)
            pe->SetPadding(0, 0, 0, 105);
        pe = FindDescendent(StrToID(L"LogoffZone"));
        if (pe)
            pe->SetPadding(50, 0, 50, 41);
    }

    _PopulateRecentDocuments();
    _PopulateRecentPrograms();

    Element::EndDefer();
    return 0;
}

void StartFrame::OnKeyFocusMoved(Element* peFrom, Element* peTo)
{
}

LRESULT StartFrame::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#define HANDLE_SF_MESSAGE(wm, fn) case wm: return fn(hWnd, uMsg, wParam, lParam)

    if (SPM_CHANGENOTIFY <= uMsg && uMsg < SPM_CHANGENOTIFY + DUI_MAXNOTIFY)
        return _OnChangeNotify(hWnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    HANDLE_SF_MESSAGE(WM_DESTROY,      _OnDestroy);
    HANDLE_SF_MESSAGE(WM_TIMER,        _OnTimer);
    HANDLE_SF_MESSAGE(WM_SIZE,         _OnSize);
    HANDLE_SF_MESSAGE(SPM_ACTIVATE,    _OnActivate);

    // Context menus handlers
    HANDLE_SF_MESSAGE(WM_INITMENUPOPUP,_OnMenuMessage);
    HANDLE_SF_MESSAGE(WM_DRAWITEM,     _OnMenuMessage);
    HANDLE_SF_MESSAGE(WM_MENUCHAR,     _OnMenuMessage);
    HANDLE_SF_MESSAGE(WM_MEASUREITEM,  _OnMenuMessage);
    HANDLE_SF_MESSAGE(PIDLButton::PBM_SETMENUFORWARD,  _OnSetMenuForward);
    }

    return HWNDElement::WndProc(hWnd, uMsg, wParam, lParam);
}

////////////////////////////////////////////////////////
// ClassInfo (must appear after property definitions)

// Define class info with type and base type, set static class pointer

IClassInfo* StartFrame::Class = NULL;
HRESULT StartFrame::Register()
{
    return ClassInfo<StartFrame,HWNDElement>::Register(L"StartFrame", NULL, 0);
}


////////////////////////////////////////////////////////
// Parser
////////////////////////////////////////////////////////

void CALLBACK ParseError(LPCWSTR pszError, LPCWSTR pszToken, int dLine)
{
    WCHAR buf[201];

    if (dLine != -1)
        swprintf(buf, L"%s '%s' at line %d", pszError, pszToken, dLine);
    else
        swprintf(buf, L"%s '%s'", pszError, pszToken);

    MessageBoxW(NULL, buf, L"Parser Message", MB_OK);
}


// Privates from Shell32.
STDAPI_(HWND) SetPeekMsgEx(FARPROC fp, HANDLE hDesktop);
STDAPI_(BOOL) SetStartPageHWND(HANDLE hDesktop, HWND hwnd);

typedef HWND (*SetPeek)(FARPROC fp, HANDLE hDesktop);

/////////////////////////////////////
// Creation of the Start Page
void CreateStartPage(HINSTANCE hInstance, HANDLE hDesktop)
{
    HWND hwndParent = NULL;

    CoInitialize(NULL);
    // DirectUI init thread in caller
    InitThread();

    hwndParent = SetPeekMsgEx((FARPROC)PeekMessageEx, hDesktop);

    Element::StartDefer();

    // HWND Root
    StartFrame* psf;
    HRESULT hr = StartFrame::Create(hwndParent, hInstance, (Element**)&psf);
    if (FAILED(hr))
        return;

    // Fill content of frame (using substitution)
    Parser* pParser;
    Parser::Create(IDR_STARTUI, hInstance, ParseError, &pParser);
    if (!pParser)
        return;

    if (pParser->WasParseError())
    {
        ASSERTMSG(FALSE, "Parse error!");
        pParser->Destroy();
        return;
    }

    Element* pe;
    pParser->CreateElement(L"main", psf, &pe);

    // Done with parser
    pParser->Destroy();

    // Disable Drag-drop for now.
    BuildDropTarget( psf->GetDisplayNode(), psf->GetHWND());

    psf->Populate();
    // Set visible and host
    psf->SetVisible(true);

    RECT rect;
    GetWindowRect(hwndParent, &rect);

    psf->SetWidth(RECTWIDTH(rect));
    psf->SetHeight(RECTHEIGHT(rect));

    Element::EndDefer();

    SetStartPageHWND(hDesktop, psf->GetHWND());

    SetTimer(psf->GetHWND(), StartFrame::TIMER_PROGRAMS, 1 * 60 * 1000, NULL);  // 1 minute

    HWND hwndDesktop = GetParent(GetParent(hwndParent));

    ShowWindow(hwndDesktop, SW_SHOW);
    UpdateWindow(hwndDesktop);

    return;
}

#endif  // FEATURE_STARTPAGE