#include "shellprv.h"
#include "ids.h"
#include "help.h"

#include "ascstr.h"
#include "ftcmmn.h"

#include "ftprop.h"
#include "ftedit.h"
#include "ftadv.h"

#define SUBITEM_EXT         0
#define SUBITEM_PROGIDDESCR 1

#define WM_FINISHFILLLISTVIEW (WM_USER + 1)

static DWORD s_rgdwHelpIDsArray[] =
{  // Context Help IDs
    IDC_NO_HELP_1,                NO_HELP,
    IDC_FT_PROP_LV_FILETYPES,     IDH_FCAB_FT_PROP_LV_FILETYPES,
    IDC_FT_PROP_ANIM,             IDH_FCAB_FT_PROP_LV_FILETYPES,
    IDC_FT_PROP_NEW,              IDH_FCAB_FT_PROP_NEW,
    IDC_FT_PROP_OPENEXE_TXT,      IDH_FCAB_FT_PROP_DETAILS,
    IDC_FT_PROP_OPENICON,         IDH_FCAB_FT_PROP_DETAILS,
    IDC_FT_PROP_OPENEXE,          IDH_FCAB_FT_PROP_DETAILS,
    IDC_FT_PROP_CHANGEOPENSWITH,  IDH_FPROP_GEN_CHANGE,
    IDC_FT_PROP_TYPEOFFILE_TXT,   IDH_FCAB_FT_PROP_DETAILS,
    IDC_FT_PROP_EDITTYPEOFFILE,   IDH_FCAB_FT_PROP_EDIT,
    IDC_FT_PROP_GROUPBOX,         IDH_FCAB_FT_PROP_DETAILS,
    IDC_FT_PROP_REMOVE,           IDH_FCAB_FT_PROP_REMOVE,
    0, 0
};

CFTPropDlg::CFTPropDlg() :
    CFTDlg((ULONG_PTR)s_rgdwHelpIDsArray), _iLVSel(-1), _fStopThread(FALSE)
{}


LRESULT CFTPropDlg::OnInitDialog(WPARAM wParam, LPARAM lParam)
{
    HRESULT hres = _InitAssocStore();

    if (SUCCEEDED(hres))
        hres = _InitListView();

    if (SUCCEEDED(hres))
        _InitPreFillListView();
    
    if (SUCCEEDED(hres))
        SHCreateThread(_FillListViewWrapper, (LPVOID)this,  0, _ThreadAddRefCallBack);

    return TRUE;
}

LRESULT CFTPropDlg::OnFinishInitDialog()
{
    HRESULT hres;
    
    _InitPostFillListView();
    hres = _SelectListViewItem(0);


    if (FAILED(hres))
    {
        if (E_OUTOFMEMORY == hres)
        {
            ShellMessageBox(g_hinst, _hwnd, MAKEINTRESOURCE(IDS_ERROR + 
                ERROR_NOT_ENOUGH_MEMORY), MAKEINTRESOURCE(IDS_FT), 
                MB_OK | MB_ICONSTOP);
        }

        EndDialog(_hwnd, -1);
    }
    else
        SHCreateThread(_UpdateAllListViewItemImagesWrapper, (LPVOID)this,  0, _ThreadAddRefCallBack);

    return TRUE;
}

LRESULT CFTPropDlg::OnCtlColorStatic(WPARAM wParam, LPARAM lParam)
{
    LRESULT fRet = FALSE;
    // This is to set the color of the background of the animate control
    // see doc on ACS_TRANSPARENT and WM_CTLCOLORSTATIC
    if ((HWND)lParam == GetDlgItem(_hwnd, IDC_FT_PROP_ANIM))
    {
        SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, WM_CTLCOLORSTATIC), GetSysColor(COLOR_WINDOW));
        fRet = (LRESULT)GetSysColorBrush(COLOR_WINDOW);
    }

    return fRet;
}

//static
DWORD WINAPI CFTPropDlg::_FillListViewWrapper(LPVOID lpParameter)
{
    ((CFTPropDlg*)lpParameter)->_FillListView();
    ((CFTPropDlg*)lpParameter)->Release();
    return 0;
}

//static
DWORD WINAPI CFTPropDlg::_UpdateAllListViewItemImagesWrapper(LPVOID lpParameter)
{
    ((CFTPropDlg*)lpParameter)->_UpdateAllListViewItemImages();
    ((CFTPropDlg*)lpParameter)->Release();
    return 0;
}

//static
DWORD WINAPI CFTPropDlg::_ThreadAddRefCallBack(LPVOID lpParameter)
{
    return ((CFTPropDlg*)lpParameter)->AddRef();
}


LRESULT CFTPropDlg::OnDestroy(WPARAM wParam, LPARAM lParam)
{
    DWORD dwRet = FALSE;
    int iCount = 0;
    LVITEM lvItem = {0};
    HWND hwndLV = _GetLVHWND();

    _fStopThread = TRUE;
    
    HICON hIconOld = (HICON)SendDlgItemMessage(_hwnd, IDC_FT_PROP_OPENICON, STM_GETIMAGE, IMAGE_ICON,
        (LPARAM)0);

    if (hIconOld)
        DeleteObject(hIconOld);

    // go through all the items in the listview and delete the strings dynamically
    // allocated for progIDs
    lvItem.mask = LVIF_PARAM;
    lvItem.iSubItem = SUBITEM_EXT;

    iCount = ListView_GetItemCount(hwndLV);

    for (lvItem.iItem = 0; lvItem.iItem < iCount; ++lvItem.iItem)
    {
        ListView_GetItem(hwndLV, &lvItem);

        if (lvItem.lParam)
        {
            LocalFree((HLOCAL)lvItem.lParam);
        }
    }

    CFTDlg::OnDestroy(wParam, lParam);

    return TRUE;
}

struct LVCOMPAREINFO
{
    HWND    hwndLV;
    int     iCol;
};

int CALLBACK AlphaCompareItem(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
    struct LVCOMPAREINFO   *plvci = (struct LVCOMPAREINFO *)lParamSort;
    TCHAR   sz1[MAX_PATH];
    TCHAR   sz2[MAX_PATH];

    ListView_GetItemText(plvci->hwndLV, lParam1, plvci->iCol, sz1, ARRAYSIZE(sz1));
    ListView_GetItemText(plvci->hwndLV, lParam2, plvci->iCol, sz2, ARRAYSIZE(sz2));

    return lstrcmpi(sz1, sz2);
}

LRESULT CFTPropDlg::OnListViewColumnClick(int iCol)
{
    struct LVCOMPAREINFO lvci;
    
    lvci.hwndLV = _GetLVHWND();
    lvci.iCol = iCol;

    _fUpdateImageAgain = TRUE;

    return SendMessage(_GetLVHWND(), LVM_SORTITEMSEX, (WPARAM)&lvci, (LPARAM)AlphaCompareItem);    
}

LRESULT CFTPropDlg::OnListViewSelItem(int iItem, LPARAM lParam)
{
    //
    // Need to update the lower pane of the dialog
    //
    // Get the extension
    TCHAR szExt[MAX_EXT];
    TCHAR szProgIDDescr[MAX_PROGIDDESCR];
    LVITEM lvItem = {0};

    _iLVSel = iItem;

    lvItem.mask = LVIF_TEXT | LVIF_PARAM;
    lvItem.iItem = iItem;
    lvItem.iSubItem = SUBITEM_EXT;
    lvItem.pszText = szExt;
    lvItem.cchTextMax = ARRAYSIZE(szExt);

    ListView_GetItem(_GetLVHWND(), &lvItem);

    ListView_GetItemText(_GetLVHWND(), iItem, SUBITEM_PROGIDDESCR, szProgIDDescr,
        ARRAYSIZE(szProgIDDescr));

    _EnableLowerPane(TRUE);

    if (!lvItem.lParam)
    {
        _UpdateGroupBox(szExt, TRUE);
    }
    else
    {
        _UpdateGroupBox(szProgIDDescr, FALSE);
    }

    _UpdateProgIDButtons(szExt, (LPTSTR)lvItem.lParam);

    // We rely on this being after _UpdateProgIDButtons (see _fPerUserAdvButton)
    _UpdateDeleteButton(lvItem.lParam ? FALSE : TRUE);
    _UpdateAdvancedText(szExt, szProgIDDescr, lvItem.lParam ? FALSE : TRUE);

    _UpdateOpensWith(szExt, (LPTSTR)lvItem.lParam);

    return FALSE;
}

HRESULT CFTPropDlg::_UpdateDeleteButton(BOOL fExt)
{
    BOOL fTrue = _ShouldEnableButtons();

    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_REMOVE),
        (_fPerUserAdvButton || !fExt) ? FALSE : fTrue);

    return S_OK;
}

HRESULT CFTPropDlg::_UpdateProgIDButtons(LPTSTR pszExt, LPTSTR pszProgID)
{
    HRESULT hres = E_FAIL;

    if (pszExt && *pszExt)
    {
        TCHAR szButtonText[50];
        HWND hwndAdvButton = GetDlgItem(_hwnd, IDC_FT_PROP_EDITTYPEOFFILE);

        _SetAdvancedRestoreButtonHelpID(IDH_FCAB_FT_PROP_EDIT);

        // Is this a progID only association?
        if (!pszProgID)
        {
            // No
            IAssocInfo* pAI;

            hres = _pAssocStore->GetAssocInfo(pszExt, AIINIT_EXT, &pAI);

            if (SUCCEEDED(hres))
            {
                hres = pAI->GetBOOL(AIBOOL_PERUSERINFOAVAILABLE, &_fPerUserAdvButton);

                ASSERT(SUCCEEDED(hres) || (FAILED(hres) && (FALSE == _fPerUserAdvButton)));

                if (_fPerUserAdvButton)
                {
                    // Restore mode
                    LoadString(g_hinst, IDS_FT_PROP_BTN_RESTORE, szButtonText, ARRAYSIZE(szButtonText));

                    _SetAdvancedRestoreButtonHelpID(IDH_FCAB_FT_PROP_EDIT_RESTORE);
                }
                else
                {
                    TCHAR szProgID[MAX_PROGID];
                    DWORD cchProgID = ARRAYSIZE(szProgID);

                    hres = pAI->GetString(AISTR_PROGID, szProgID, &cchProgID);

                    LoadString(g_hinst, IDS_FT_PROP_BTN_ADVANCED, szButtonText, ARRAYSIZE(szButtonText));

                    if (SUCCEEDED(hres))
                    {
                        IAssocInfo * pAIProgID;

                        hres = _pAssocStore->GetAssocInfo(szProgID, AIINIT_PROGID, &pAIProgID);

                        if (SUCCEEDED(hres))
                        {
                            BOOL fEdit = _ShouldEnableButtons();

                            if (fEdit)
                            {
                                pAIProgID->GetBOOL(AIBOOL_EDIT, &fEdit);
                            }

                            EnableWindow(hwndAdvButton, fEdit);

                            pAIProgID->Release();
                        }
                    }
                }

                pAI->Release();
            }
        }
        else
        {
            // Yes
            IAssocInfo* pAIProgID;

            LoadString(g_hinst, IDS_FT_PROP_BTN_ADVANCED, szButtonText, ARRAYSIZE(szButtonText));

            hres = _pAssocStore->GetAssocInfo(pszProgID, AIINIT_PROGID, &pAIProgID);

            if (SUCCEEDED(hres))
            {
                BOOL fEdit = _ShouldEnableButtons();

                if (fEdit)
                {
                    pAIProgID->GetBOOL(AIBOOL_EDIT, &fEdit);
                }

                EnableWindow(hwndAdvButton, fEdit);

                pAIProgID->Release();
            }

            EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_CHANGEOPENSWITH), FALSE);
        }

        SetWindowText(hwndAdvButton, szButtonText);
    }

    return hres;
}

LRESULT CFTPropDlg::OnDeleteButton(WORD wNotif)
{
    // Warn user about the evil consequences of his act
    if (ShellMessageBox(g_hinst, _hwnd, MAKEINTRESOURCE(IDS_FT_MB_REMOVETYPE),
        MAKEINTRESOURCE(IDS_FT), MB_YESNO | MB_ICONQUESTION) == IDYES)
    {
        LVITEM lvItem = {0};
        TCHAR szExt[MAX_EXT];

        // Set stuff
        lvItem.iSubItem = SUBITEM_EXT;
        lvItem.pszText = szExt;
        lvItem.cchTextMax = ARRAYSIZE(szExt);

        if (_GetListViewSelectedItem(LVIF_TEXT | LVIF_IMAGE, 0, &lvItem))
        {
            HRESULT hres;
            IAssocInfo* pAI;

            hres = _pAssocStore->GetAssocInfo(szExt, AIINIT_EXT, &pAI);

            if (SUCCEEDED(hres))
            {
                hres = pAI->Delete(AIALL_NONE);

                if (SUCCEEDED(hres))
                {
                    _DeleteListViewItem(lvItem.iItem);

                    PropSheet_CancelToClose(GetParent(_hwnd));
                }

                pAI->Release();
            }
        }
    }

    return FALSE;
}

LRESULT CFTPropDlg::OnNewButton(WORD wNotif)
{
    FTEDITPARAM ftEditParam;
    CFTEditDlg* pEditDlg = NULL;

    // Fill structure
    ftEditParam.dwExt = ARRAYSIZE(ftEditParam.szExt);
    ftEditParam.dwProgIDDescr = ARRAYSIZE(ftEditParam.szProgIDDescr);

    // This one should be one way, it will come back with a value
    *ftEditParam.szProgID = 0;
    ftEditParam.dwProgID = ARRAYSIZE(ftEditParam.szProgID);

    pEditDlg = new CFTEditDlg(&ftEditParam);

    if (pEditDlg)
    {
        if (IDOK == pEditDlg->DoModal(g_hinst, MAKEINTRESOURCE(DLG_FILETYPEOPTIONSEDITNEW),
                        _hwnd))
        {
            HWND hwndLV = _GetLVHWND();
            LRESULT lRes = 0;
            int iIndex = -1;
            HRESULT hres = E_FAIL;
            IAssocInfo* pAI = NULL;
            LVFINDINFO lvFindInfo = {0};
            LPTSTR pszExtNoDot = NULL;
            LVITEM lvItem = {0};
            TCHAR szExt[MAX_EXT];

            lvItem.pszText = szExt;
            lvItem.cchTextMax = ARRAYSIZE(szExt);

            pszExtNoDot = (TEXT('.') != *(ftEditParam.szExt)) ? ftEditParam.szExt :
                ftEditParam.szExt + 1;

            lvFindInfo.flags = LVFI_STRING;
            lvFindInfo.psz = pszExtNoDot;

            iIndex = ListView_FindItem(hwndLV, -1, &lvFindInfo);

            // Is this a brand new Ext-ProgID association?
            if (-1 == iIndex)
            {
                // Yes, Insert a new item
                SetWindowRedraw(hwndLV, FALSE);
            
                // Add new ext-progID association
                hres = _pAssocStore->GetAssocInfo(ftEditParam.szExt, AIINIT_EXT, &pAI);

                if (SUCCEEDED(hres))
                {
                    TCHAR szProgIDDescr[MAX_PROGIDDESCR];
                    DWORD cchProgIDDescr = ARRAYSIZE(szProgIDDescr);

                    hres = pAI->GetString(AISTR_PROGIDDESCR, szProgIDDescr, &cchProgIDDescr);

                    if (FAILED(hres) || !*szProgIDDescr)
                    {
                        MakeDefaultProgIDDescrFromExt(szProgIDDescr, ARRAYSIZE(szProgIDDescr), pszExtNoDot);
                    }

                    // Add to the listview
                    iIndex = _InsertListViewItem(0, pszExtNoDot, szProgIDDescr);
                    pAI->Release();
                }

                // Select newly inserted item
                if (-1 != iIndex)
                {
                    _SelectListViewItem(iIndex);
                }

                // Redraw our list
                SetWindowRedraw(hwndLV, TRUE);

                _GetListViewSelectedItem(LVIF_PARAM | LVIF_TEXT, 0, &lvItem);

                lvItem.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
            }
            else
            {
                // No just update the item
                lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
                lvItem.iItem = iIndex;

                ListView_GetItem(hwndLV, &lvItem);
            }

            _UpdateListViewItem(&lvItem);

            PropSheet_CancelToClose(GetParent(_hwnd));
        }

        pEditDlg->Release();
    }

    return FALSE;
}

LRESULT CFTPropDlg::OnAdvancedButton(WORD wNotif)
{
    LVITEM lvItem = {0};
    TCHAR szExt[MAX_EXT];

    // Set stuff
    lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
    lvItem.iSubItem = SUBITEM_EXT;
    lvItem.pszText = szExt;
    lvItem.cchTextMax = ARRAYSIZE(szExt);

    if (_GetListViewSelectedItem(LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM, 0, &lvItem))
    {
        HRESULT hres;
        IAssocInfo* pAI;

        if (_fPerUserAdvButton)
        {
            // Restore mode
            hres = _pAssocStore->GetAssocInfo(szExt, AIINIT_EXT, &pAI);

            if (SUCCEEDED(hres))
            {
                hres = pAI->Delete(AIALL_PERUSER);

                _UpdateListViewItem(&lvItem);

                OnListViewSelItem(lvItem.iItem, (LPARAM)NULL);

                pAI->Release();

                PropSheet_CancelToClose(GetParent(_hwnd));
            }
        }
        else
        {
            // we might deal with an ext-progid assoc or only a progID
            TCHAR szProgID[MAX_PROGID];

            // Is this a progID only?
            if (lvItem.lParam)
            {
                // Yes
                StrCpyN(szProgID, (LPTSTR)lvItem.lParam, ARRAYSIZE(szProgID));

                hres = S_OK;
            }
            else
            {
                // No
                DWORD cchProgID = ARRAYSIZE(szProgID);

                hres = _pAssocStore->GetAssocInfo(szExt, AIINIT_EXT, &pAI);

                if (SUCCEEDED(hres))
                {
                    hres = THR(pAI->GetString(AISTR_PROGID, szProgID, &cchProgID));

                    pAI->Release();
                }
            }

            if (SUCCEEDED(hres))
            {
                CFTAdvDlg* pAdvDlg = new CFTAdvDlg(szProgID, szExt);

                if (pAdvDlg)
                {
                    if (IDOK == pAdvDlg->DoModal(g_hinst, MAKEINTRESOURCE(DLG_FILETYPEOPTIONSEDIT), _hwnd))
                    {
                        _UpdateListViewItem(&lvItem);

                        OnListViewSelItem(lvItem.iItem, (LPARAM)NULL);

                        PropSheet_CancelToClose(GetParent(_hwnd));
                    }

                    pAdvDlg->Release();
                }
            }
        }
    }

    return FALSE;
}

LRESULT CFTPropDlg::OnChangeButton(WORD wNotif)
{
    // Bring up the "Open With" dialog
    LVITEM lvItem = {0};
    TCHAR szExt[MAX_EXT];

    // Set stuff
    lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
    lvItem.iSubItem = SUBITEM_EXT;
    lvItem.pszText = szExt;
    lvItem.cchTextMax = ARRAYSIZE(szExt);

    if (_GetListViewSelectedItem(LVIF_TEXT, 0, &lvItem))
    {
        TCHAR szDotExt[MAX_EXT];
        OPENASINFO oai;

        *szDotExt = TEXT('.');
        StrCpyN(szDotExt + 1, szExt, ARRAYSIZE(szDotExt) - 1);

        oai.pcszFile = szDotExt;
        oai.pcszClass = NULL;
        oai.dwInFlags = (OAIF_REGISTER_EXT | OAIF_FORCE_REGISTRATION); // we want the association to be made

        if (SUCCEEDED(OpenAsDialog(GetParent(_hwnd), &oai)))
        {
            // we changed the association so update the "Opens with:" text
            _UpdateOpensWith(szExt, NULL);

            // we don't need LVIF_PARAM since we enable the Change button only for Ext-ProgID asssoc
            lvItem.mask = LVIF_TEXT | LVIF_IMAGE;

            _UpdateListViewItem(&lvItem);

            OnListViewSelItem(lvItem.iItem, (LPARAM)NULL);

            PropSheet_CancelToClose(GetParent(_hwnd));
        }
    }

    return FALSE;
}

HRESULT CFTPropDlg::_UpdateGroupBox(LPTSTR pszText, BOOL fExt)
{
    HRESULT hres = E_OUTOFMEMORY;
    LPTSTR psz = NULL;

    if (fExt)
    {
        psz = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FT_PROP_DETAILSFOR), pszText);
    }
    else
    {
        psz = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FT_PROP_DETAILSFORPROGID), pszText);
    }

    if (psz)
    {
        SetDlgItemText(_hwnd, IDC_FT_PROP_GROUPBOX, psz);
        LocalFree(psz);
        hres = S_OK;
    }

    return hres;
}

HRESULT CFTPropDlg::_UpdateOpensWith(LPTSTR pszExt, LPTSTR pszProgID)
{
    HICON hIconOld = NULL;

    if (!pszProgID)
    {
        IAssocInfo* pAI = NULL;

        HRESULT hres = _pAssocStore->GetAssocInfo(pszExt, AIINIT_EXT, &pAI);

        if (SUCCEEDED(hres))
        {
            TCHAR szAppFriendlyName[MAX_APPFRIENDLYNAME];
            DWORD dwAppFriendlyName = ARRAYSIZE(szAppFriendlyName);

            hres = pAI->GetString(AISTR_APPFRIENDLY, szAppFriendlyName, &dwAppFriendlyName);

            if (SUCCEEDED(hres))
            {
                HICON hIcon = NULL;
                int iIcon;
                SetDlgItemText(_hwnd, IDC_FT_PROP_OPENEXE, szAppFriendlyName);

                hres = pAI->GetDWORD(AIDWORD_APPSMALLICON, (DWORD*)&iIcon);

                HIMAGELIST hIL = NULL;

                // PERF: Why don't we just use _hImageList?  Or ListView_GetImageList()?
                Shell_GetImageLists(NULL, &hIL);

                if (hIL && SUCCEEDED(hres))
                {
                    hIcon = ImageList_ExtractIcon(g_hinst, hIL, iIcon);
                }

                hIconOld = (HICON)SendDlgItemMessage(_hwnd, IDC_FT_PROP_OPENICON, STM_SETIMAGE, IMAGE_ICON,
                    (LPARAM)hIcon);

                if (hIconOld)
                    DestroyIcon(hIconOld);
            }
            else
            {
                SetDlgItemText(_hwnd, IDC_FT_PROP_OPENEXE, TEXT(" "));

                hIconOld = (HICON)SendDlgItemMessage(_hwnd, IDC_FT_PROP_OPENICON, STM_SETIMAGE, IMAGE_ICON,
                    (LPARAM)NULL);

                if (hIconOld)
                    DestroyIcon(hIconOld);
            }

            pAI->Release();
        }
    }
    else
    {
        SetDlgItemText(_hwnd, IDC_FT_PROP_OPENEXE, TEXT(" "));

        hIconOld = (HICON)SendDlgItemMessage(_hwnd, IDC_FT_PROP_OPENICON, STM_SETIMAGE, IMAGE_ICON,
            (LPARAM)NULL);

        if (hIconOld)
            DestroyIcon(hIconOld);
    }
    
    return S_OK;
}

HRESULT CFTPropDlg::_UpdateAdvancedText(LPTSTR pszExt, LPTSTR pszFileType, BOOL fExt)
{
    HRESULT hres = S_OK;
    LPTSTR psz = NULL;

    if (_fPerUserAdvButton)
    {
        TCHAR szProgIDDescr[MAX_PROGIDDESCR];
        DWORD cchProgIDDescr = ARRAYSIZE(szProgIDDescr);
        IAssocInfo* pAI = NULL;

        // we need to show the previous progIDDescr
        hres = _pAssocStore->GetAssocInfo(pszExt, AIINIT_EXT, &pAI);

        if (SUCCEEDED(hres))
        {
            hres = pAI->GetString(AISTR_PROGIDDESCR, szProgIDDescr,
                    &cchProgIDDescr);

            if (SUCCEEDED(hres))
            {
                // Restore mode
                psz = ShellConstructMessageString(HINST_THISDLL,
                    MAKEINTRESOURCE(IDS_FT_PROP_RESTORE),
                    pszExt, szProgIDDescr);
            }

            pAI->Release();
        }
    }
    else
    {
        if (fExt)
        {
            psz = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FT_PROP_ADVANCED),
                        pszExt, pszFileType, pszFileType);
        }
        else
        {
            psz = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FT_PROP_ADVANCED_PROGID),
                        pszFileType);
        }
    }

    if (SUCCEEDED(hres))
    {
        if (psz)
        {
            SetDlgItemText(_hwnd, IDC_FT_PROP_TYPEOFFILE_TXT, psz);
            LocalFree(psz);
        }
        else
            hres = E_OUTOFMEMORY;
    }

    return hres;
}

HRESULT CFTPropDlg::_EnableLowerPane(BOOL fEnable)
{
    BOOL fTrue = _ShouldEnableButtons();

    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_OPENEXE_TXT), fEnable);
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_OPENEXE), fEnable);
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_TYPEOFFILE_TXT), fEnable);
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_GROUPBOX ), fEnable);

    // if user is locked down then we do not enable the buttons
    if (!fTrue)
        fEnable = FALSE;

    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_CHANGEOPENSWITH), fEnable);
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_EDITTYPEOFFILE), fEnable);

    return S_OK;
}

HRESULT CFTPropDlg::_InitPreFillListView()
{
    // Disable New and Delete
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_NEW), FALSE);
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_REMOVE), FALSE);

    _EnableLowerPane(FALSE);
    _UpdateGroupBox(TEXT(""), TRUE);

    // Hide the advanced text
    ShowWindow(GetDlgItem(_hwnd, IDC_FT_PROP_TYPEOFFILE_TXT), SW_HIDE);

    return S_OK;
}

HRESULT CFTPropDlg::_InitPostFillListView()
{
    BOOL fTrue = _ShouldEnableButtons();

    // Enable New and Delete
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_NEW),  fTrue);
    EnableWindow(GetDlgItem(_hwnd, IDC_FT_PROP_REMOVE), fTrue);

    // Show the advanced text
    ShowWindow(GetDlgItem(_hwnd, IDC_FT_PROP_TYPEOFFILE_TXT), SW_SHOW);

    Animate_Stop(GetDlgItem(_hwnd, IDC_FT_PROP_ANIM));
    ShowWindow(GetDlgItem(_hwnd, IDC_FT_PROP_ANIM), SW_HIDE);
    ShowWindow(_GetLVHWND(), SW_SHOW);

    SetFocus(_GetLVHWND());

    return S_OK;
}

HRESULT CFTPropDlg::_InitListView()
{
    HRESULT hres = S_OK;
    LVCOLUMN lvColumn = {0};
    HWND hwndLV = _GetLVHWND();
    TCHAR szColumnTitle[40];
    RECT rc = {0};
    int iWidth = 80;
    HWND hwndAni;

    //
    // Styles
    //
    ListView_SetExtendedListViewStyleEx(hwndLV, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

    //
    // Set the columns
    //
    lvColumn.mask = LVCF_TEXT|LVCF_SUBITEM|LVCF_WIDTH;

    // Extensions column
    LoadString(g_hinst, IDS_FT_PROP_EXTENSIONS, szColumnTitle, ARRAYSIZE(szColumnTitle));

    lvColumn.cx = 60;
    lvColumn.pszText = szColumnTitle;
    lvColumn.cchTextMax = lstrlen(szColumnTitle);
    lvColumn.iSubItem = SUBITEM_EXT;

    ListView_InsertColumn(hwndLV, SUBITEM_EXT, &lvColumn);

    // ProgIDs column
    LoadString(g_hinst, IDS_FT, szColumnTitle, ARRAYSIZE(szColumnTitle));

    lvColumn.cchTextMax = lstrlen(szColumnTitle);
    lvColumn.iSubItem = SUBITEM_PROGIDDESCR;
    ListView_InsertColumn(hwndLV, SUBITEM_PROGIDDESCR, &lvColumn);

    // Adjust columns width
    // we need to do it after inserting both col, cause the last column resizing 
    // is special cased in list view code.

    // Ext column
    ListView_SetColumnWidth(hwndLV, SUBITEM_EXT, LVSCW_AUTOSIZE_USEHEADER);
    iWidth = ListView_GetColumnWidth(hwndLV, SUBITEM_EXT);

    // File type column
    GetClientRect(hwndLV, &rc);
    ListView_SetColumnWidth(hwndLV, SUBITEM_PROGIDDESCR,
        rc.right - iWidth - GetSystemMetrics(SM_CXBORDER) - GetSystemMetrics(SM_CXVSCROLL));

    //
    // ImageList
    //
    Shell_GetImageLists(NULL, &_hImageList);

    if (_hImageList)
        ListView_SetImageList(hwndLV, _hImageList, LVSIL_SMALL);

    GetWindowRect(hwndLV, &rc);
    MapWindowPoints(NULL, _hwnd, (POINT*)&rc, 2);

    hwndAni = GetDlgItem(_hwnd, IDC_FT_PROP_ANIM);

    Animate_Open(hwndAni, MAKEINTRESOURCE(IDA_SEARCH)); // open the resource
    Animate_Play(hwndAni, 0, -1, -1);     // play from start to finish and repeat

    MoveWindow(hwndAni, rc.left, rc.top,
        rc.right - rc.left, rc.bottom - rc.top, TRUE);

    ShowWindow(hwndLV, SW_HIDE);

    ShowWindow(hwndAni, SW_SHOW);

    return hres;
}

HRESULT CFTPropDlg::_FillListView()
{
    // Data stuff
    IEnumAssocInfo* pEnum = NULL;
    HRESULT hres = E_FAIL;
    int iFirstNAItem = -1;
    HWND hwndLV = NULL;
    int iItem = 0;
    TCHAR szNA[50];

    ASSERT(_pAssocStore);

    // Do the extension first
    if (!_fStopThread)
    {
        hwndLV = _GetLVHWND();
        SetWindowRedraw(hwndLV, FALSE);
    }

    if (!_fStopThread)
    {
        LoadString(g_hinst, IDS_FT_NA, szNA, ARRAYSIZE(szNA));
        hres = _pAssocStore->EnumAssocInfo(ASENUM_EXT |
            ASENUM_ASSOC_YES | ASENUM_NOEXCLUDED | ASENUM_NOEXPLORERSHELLACTION |
            ASENUM_NOEXE, NULL, AIINIT_NONE, &pEnum);
    }
    else
        hres = E_FAIL;

    
    if (SUCCEEDED(hres))
    {
        IAssocInfo* pAI = NULL;
    
        while (!_fStopThread && (S_OK == pEnum->Next(&pAI)))
        {
            TCHAR szExt[MAX_EXT];
            DWORD cchExt = ARRAYSIZE(szExt);

            hres = pAI->GetString(AISTR_EXT, szExt, &cchExt);

            if (SUCCEEDED(hres))
            {
                BOOL fPerUser = FALSE;
                TCHAR szProgIDDescr[MAX_PROGIDDESCR];
                DWORD cchProgIDDescr = ARRAYSIZE(szProgIDDescr);
                HRESULT hresTmp = E_FAIL;

                hresTmp = pAI->GetBOOL(AIBOOL_PERUSERINFOAVAILABLE, &fPerUser);

                ASSERT(SUCCEEDED(hresTmp) || (FAILED(hresTmp) && (FALSE == fPerUser)));

                if (!fPerUser)
                {
                    hresTmp = pAI->GetString(AISTR_PROGIDDESCR, szProgIDDescr,
                            &cchProgIDDescr);
                }
            
                if (fPerUser || FAILED(hresTmp) || !*szProgIDDescr)
                    MakeDefaultProgIDDescrFromExt(szProgIDDescr, ARRAYSIZE(szProgIDDescr), szExt);
                    
                if (!_fStopThread)
                    _InsertListViewItem(iItem, szExt, szProgIDDescr);

                // See comment in ftenum.cpp, CFTEnumAssocInfo::_EnumKCRStop about sorting
                // Check if this is where we need to insert the N/A item later
                if ((-1 == iFirstNAItem) && (lstrcmpi(szExt, szNA) > 0))
                {
                    iFirstNAItem = iItem;
                }
                ++iItem;
            }

            pAI->Release();

            hres = S_OK;
        }

        pEnum->Release();
        pEnum = NULL;
    }

    // Then do the ProgIDs
    if (!_fStopThread)    
        hres = _pAssocStore->EnumAssocInfo(ASENUM_PROGID | ASENUM_SHOWONLY, NULL, AIINIT_NONE, &pEnum);
    else
        hres = E_FAIL;
        
    if (SUCCEEDED(hres))
    {
        IAssocInfo* pAI = NULL;
        int cNAItem = 0;

        while (!_fStopThread && (S_OK == pEnum->Next(&pAI)))
        {
            TCHAR szProgIDDescr[MAX_PROGIDDESCR];
            DWORD cchProgIDDescr = ARRAYSIZE(szProgIDDescr);

            hres = pAI->GetString(AISTR_PROGIDDESCR, szProgIDDescr, &cchProgIDDescr);

            if (SUCCEEDED(hres))
            {
                TCHAR szProgID[MAX_PROGID];
                DWORD cchProgID = ARRAYSIZE(szProgID);

                hres = pAI->GetString(AISTR_PROGID, szProgID, &cchProgID);

                if (SUCCEEDED(hres))
                {
                    // we need to sort the N/A items by the description since they all begin with "N/A"
                    int iNAItem;

                    if (!cNAItem)
                    {
                        iNAItem = iFirstNAItem;
                    }
                    else
                    {
                        if (!_fStopThread)
                            iNAItem = _GetNextNAItemPos(iFirstNAItem, cNAItem, szProgIDDescr);
                    }

                    if (!_fStopThread)
                    {
                        _InsertListViewItem(iNAItem, szNA, szProgIDDescr, szProgID);
                        ++cNAItem;
                    }
                }
            }

            pAI->Release();

            hres = S_OK;
        }

        pEnum->Release();
    }

    if (!_fStopThread)
    {
        SetWindowRedraw(hwndLV, TRUE);
        PostMessage(_hwnd, WM_FINISHFILLLISTVIEW, 0, 0);
    }
    
    return hres;
}

int CFTPropDlg::_GetNextNAItemPos(int iFirstNAItem, int cNAItem, LPTSTR pszProgIDDescr)
{
    LVITEM lvItem = {0};
    TCHAR szProgIDDescr[MAX_PROGIDDESCR];
    int iItem = iFirstNAItem;
    HWND hwndLV = _GetLVHWND();

    lvItem.mask = LVIF_TEXT;
    lvItem.iItem = iItem;
    lvItem.iSubItem = SUBITEM_PROGIDDESCR;
    lvItem.pszText = szProgIDDescr;
    lvItem.cchTextMax = ARRAYSIZE(szProgIDDescr);

    while (iItem < (iFirstNAItem + cNAItem))
    {
        if (ListView_GetItem(hwndLV, &lvItem))
        {
            if (lstrcmpi(pszProgIDDescr, lvItem.pszText) >= 0)
            {
                ++iItem;
                lvItem.iItem = iItem;
            }
            else
            {
                break;
            }
        }
        else
        {
            // This happens when the listview is destroyed (on another thread),
            // but this thread is still doing some work.  The call above fails,
            // we break here or else we'll never go out of the loop.
            break;
        }
    }

    return iItem;
}

DWORD CFTPropDlg::_UpdateAllListViewItemImages()
{
    HWND hwndLV = NULL;
    int iCount = 0;
    LVITEM lvItem = {0};
    TCHAR szExt[MAX_EXT];
    HRESULT hres = E_FAIL;


    HRESULT hrInit = SHCoInitialize();
 
    lvItem.iSubItem = SUBITEM_EXT;
    lvItem.pszText = szExt;
    lvItem.cchTextMax = ARRAYSIZE(szExt);

    if (!_fStopThread)
        hwndLV = _GetLVHWND();

    do
    {
        _fUpdateImageAgain = FALSE;
        
        if (!_fStopThread)
            iCount = ListView_GetItemCount(hwndLV);
        
        for (lvItem.iItem = 0; !_fStopThread && (lvItem.iItem < iCount);
            ++lvItem.iItem)
        {
            IAssocInfo* pAI = NULL;

            lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
            
            if (!_fStopThread)
                ListView_GetItem(hwndLV, &lvItem);
            
            if (!_fStopThread && !lvItem.lParam)
            {
                hres = _pAssocStore->GetAssocInfo(szExt, AIINIT_EXT, &pAI);

                if (SUCCEEDED(hres))
                {
                    BOOL fPerUser = FALSE;

                    hres = pAI->GetBOOL(AIBOOL_PERUSERINFOAVAILABLE, &fPerUser);

                    ASSERT(SUCCEEDED(hres) || (FAILED(hres) && (FALSE == fPerUser)));

                    if (fPerUser)
                        hres = pAI->GetDWORD(AIDWORD_DOCSMALLICON | AIALL_PERUSER, (DWORD*)&lvItem.iImage);
                    else
                        hres = pAI->GetDWORD(AIDWORD_DOCSMALLICON, (DWORD*)&lvItem.iImage);
                }
            }
            else
            {
                hres = _pAssocStore->GetAssocInfo((LPTSTR)lvItem.lParam, AIINIT_PROGID, &pAI);

                if (SUCCEEDED(hres))
                {
                    hres = pAI->GetDWORD(AIDWORD_DOCSMALLICON, (DWORD*)&lvItem.iImage);
                }
            }

            if (SUCCEEDED(hres))
            {
                lvItem.mask = LVIF_IMAGE;
                
                if (!_fStopThread)
                    ListView_SetItem(hwndLV, &lvItem);
            }

            if (pAI)
                pAI->Release();
        }
    }
    while (_fUpdateImageAgain && !_fStopThread);

    SHCoUninitialize(hrInit);

    return (DWORD)_fStopThread;
}

void CFTPropDlg::_UpdateListViewItem(LVITEM* plvItem)
{
    HWND hwndLV = _GetLVHWND();
    LVITEM lvItem = *plvItem;

    // Need to:
    //  - update image
    //  - update progIDDescr

    if (!lvItem.lParam)
    {
        IAssocInfo* pAI = NULL;

        HRESULT hres = _pAssocStore->GetAssocInfo(lvItem.pszText, AIINIT_EXT, &pAI);

        if (SUCCEEDED(hres))
        {
            TCHAR szProgIDDescr[MAX_PROGIDDESCR];
            DWORD cchProgIDDescr = ARRAYSIZE(szProgIDDescr);
            HRESULT hresTmp = E_FAIL;

            SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
        
            // Icon
            BOOL fPerUser = FALSE;

            hres = pAI->GetBOOL(AIBOOL_PERUSERINFOAVAILABLE, &fPerUser);

            ASSERT(SUCCEEDED(hres) || (FAILED(hres) && (FALSE == fPerUser)));

            if (fPerUser)
                hres = pAI->GetDWORD(AIDWORD_DOCSMALLICON | AIALL_PERUSER, (DWORD*)&lvItem.iImage);
            else
                hres = pAI->GetDWORD(AIDWORD_DOCSMALLICON, (DWORD*)&lvItem.iImage);

            if (SUCCEEDED(hres))
            {
                lvItem.mask = LVIF_IMAGE;
                ListView_SetItem(hwndLV, &lvItem);
            }

            // ProgID Description
            if (!fPerUser)
            {
                hresTmp = pAI->GetString(AISTR_PROGIDDESCR, szProgIDDescr,
                        &cchProgIDDescr);
            }
    
            if (fPerUser || FAILED(hresTmp) || !*szProgIDDescr)
                MakeDefaultProgIDDescrFromExt(szProgIDDescr, ARRAYSIZE(szProgIDDescr), lvItem.pszText);

            if (SUCCEEDED(hres))
            {
                lvItem.mask = LVIF_TEXT;
                lvItem.iSubItem = SUBITEM_PROGIDDESCR;
                lvItem.pszText = szProgIDDescr;
                lvItem.cchTextMax = lstrlen(szProgIDDescr);

                ListView_SetItem(hwndLV, &lvItem);
            }

            ListView_RedrawItems(hwndLV, lvItem.iItem, lvItem.iItem);

            pAI->Release();
        }
    }
    else
    {
        IAssocInfo* pAI = NULL;

        HRESULT hres = _pAssocStore->GetAssocInfo((LPTSTR)lvItem.lParam, AIINIT_PROGID, &pAI);

        if (SUCCEEDED(hres))
        {
            TCHAR szProgIDDescr[MAX_PROGIDDESCR];
            DWORD cchProgIDDescr = ARRAYSIZE(szProgIDDescr);
            HRESULT hresTmp = E_FAIL;

            SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
        
            // Icon
            hres = pAI->GetDWORD(AIDWORD_DOCSMALLICON, (DWORD*)&lvItem.iImage);

            if (SUCCEEDED(hres))
            {
                lvItem.mask = LVIF_IMAGE;
                ListView_SetItem(hwndLV, &lvItem);
            }

            // ProgID Description
            pAI->GetString(AISTR_PROGIDDESCR, szProgIDDescr,
                    &cchProgIDDescr);

            if (SUCCEEDED(hres))
            {
                lvItem.mask = LVIF_TEXT;
                lvItem.iSubItem = SUBITEM_PROGIDDESCR;
                lvItem.pszText = szProgIDDescr;
                lvItem.cchTextMax = lstrlen(szProgIDDescr);

                ListView_SetItem(hwndLV, &lvItem);
            }
            ListView_RedrawItems(hwndLV, lvItem.iItem, lvItem.iItem);

            pAI->Release();
        }
    }
}

int CFTPropDlg::_InsertListViewItem(int iItem, LPTSTR pszExt, LPTSTR pszProgIDDescr,
                                    LPTSTR pszProgID)
{
    HWND hwndLV = _GetLVHWND();
    LVITEM lvItem = {0};
    lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;

    // Put generic icon

    lvItem.iImage = Shell_GetCachedImageIndex(TEXT("shell32.dll"), II_DOCNOASSOC, 0);

    CharUpper(pszExt);

    // Extension
    if (pszProgID)
    {
        lvItem.lParam = (LPARAM)LocalAlloc(LPTR, (lstrlen(pszProgID) + 1) * sizeof(TCHAR));

        if (lvItem.lParam)
            lstrcpy((LPTSTR)lvItem.lParam, pszProgID);
    }
    else
    {
        lvItem.lParam = NULL;
    }

    lvItem.iItem = iItem;
    lvItem.iSubItem = SUBITEM_EXT;
    lvItem.pszText = pszExt;
    lvItem.cchTextMax = lstrlen(pszExt);

    lvItem.iItem = ListView_InsertItem(hwndLV, &lvItem);

    if (-1 != lvItem.iItem)
    {
        // ProgID Description
        lvItem.mask = LVIF_TEXT;
        lvItem.iSubItem = SUBITEM_PROGIDDESCR;
        lvItem.pszText = pszProgIDDescr;
        lvItem.cchTextMax = lstrlen(pszProgIDDescr);

        ListView_SetItem(hwndLV, &lvItem);
    }
    else
    {
        // LocalFree checks for NULL
        LocalFree((HLOCAL)lvItem.lParam);
    }

    return lvItem.iItem;
}

HRESULT CFTPropDlg::_SelectListViewItem(int i)
{
    LVITEM lvItem = {0};

    lvItem.iItem = i;
    lvItem.mask = LVIF_STATE;
    lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
    lvItem.state = LVIS_SELECTED | LVIS_FOCUSED;

    ListView_SetItem(_GetLVHWND(), &lvItem);
    ListView_EnsureVisible(_GetLVHWND(), i, FALSE);

    return S_OK;
}

HRESULT CFTPropDlg::_DeleteListViewItem(int i)
{
    HWND hwndLV = _GetLVHWND();
    int iCount = ListView_GetItemCount(hwndLV);
    int iNextSel = -1;        
    LVITEM lvItem = {0};

    lvItem.mask = LVIF_PARAM;
    lvItem.iItem = i;
    lvItem.iSubItem = SUBITEM_EXT;

    ListView_GetItem(hwndLV, &lvItem);

    if (lvItem.lParam)
    {
        LocalFree((HLOCAL)lvItem.lParam);
    }

    ListView_DeleteItem(hwndLV, i);

    if (iCount > i)
        iNextSel = i;
    else
        if (i > 0)
            iNextSel = i - 1;

    if (-1 != iNextSel)
        _SelectListViewItem(iNextSel);

    return S_OK;
}

BOOL CFTPropDlg::_ShouldEnableButtons()
{
    // if we have a locked down user, then we never enable the buttons
    BOOL fRet = TRUE;

    if (S_FALSE == _pAssocStore->CheckAccess())
    {
        fRet = FALSE;
    }

    // If the REST_NOFILEASSOCIATE is set (TRUE), 
    // then we want to NOT enable buttons.
    fRet &= !SHRestricted(REST_NOFILEASSOCIATE);

    return fRet;
}

///////////////////////////////////////////////////////////////////////////////
// Misc
BOOL CFTPropDlg::_GetListViewSelectedItem(UINT uMask, UINT uStateMask, LVITEM* plvItem)
{
    BOOL fSel = FALSE;
    HWND hwndLV = _GetLVHWND();

    plvItem->mask = uMask | LVIF_STATE;
    plvItem->stateMask = uStateMask | LVIS_SELECTED;

    // Do we have the selection cached?
    if (-1 != _iLVSel)
    {
        // Yes, make sure it's valid
        plvItem->iItem = _iLVSel;

        ListView_GetItem(hwndLV, plvItem);

        if (plvItem->state & LVIS_SELECTED)
            fSel = TRUE;
    }
 
    // Cache was wrong
    if (!fSel)
    {
        int iCount = ListView_GetItemCount(hwndLV);

        for (int i=0; (i < iCount) && !fSel; ++i)
        {
            plvItem->iItem = i;
            ListView_GetItem(hwndLV, plvItem);

            if (plvItem->state & LVIS_SELECTED)
                fSel = TRUE;
        }

        if (fSel)
            _iLVSel = i;
    }

    return fSel;
}

HWND CFTPropDlg::_GetLVHWND()
{
    return GetDlgItem(_hwnd, IDC_FT_PROP_LV_FILETYPES);
}

void CFTPropDlg::_SetAdvancedRestoreButtonHelpID(DWORD dwID)
{
    for (int i = 0; i < ARRAYSIZE(s_rgdwHelpIDsArray); i += 2)
    {
        if (IDC_FT_PROP_EDITTYPEOFFILE == s_rgdwHelpIDsArray[i])
        {
            if (i + 1 < ARRAYSIZE(s_rgdwHelpIDsArray))
                s_rgdwHelpIDsArray[i + 1] = dwID;

            break;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// Windows boiler plate code
LRESULT CFTPropDlg::OnNotifyListView(UINT uCode, LPNMHDR pNMHDR)
{
    LRESULT lRes = FALSE;

    switch(uCode)
    {
        case LVN_GETINFOTIP:
        {
            NMLVGETINFOTIP* plvn = (NMLVGETINFOTIP*)pNMHDR;

            break;
        }
        case LVN_ITEMCHANGED:
        {
            NMLISTVIEW* pNMLV = (NMLISTVIEW*)pNMHDR;

            // Is a new item being selected?
            if ((pNMLV->uChanged & LVIF_STATE) &&
                (pNMLV->uNewState & (LVIS_SELECTED | LVIS_FOCUSED)))
            {
                // Yes
                OnListViewSelItem(pNMLV->iItem, pNMLV->lParam);
            }
            break;
        }

        case LVN_COLUMNCLICK:
        {
            NMLISTVIEW* pNMLV = (NMLISTVIEW*)pNMHDR;

            OnListViewColumnClick(pNMLV->iSubItem);
            break;
        }

        case NM_DBLCLK:
            if (IsWindowEnabled(GetDlgItem(_hwnd, IDC_FT_PROP_EDIT)))
                PostMessage(_hwnd, WM_COMMAND, (WPARAM)IDC_FT_PROP_EDIT, 0);
            break;
    }

    return lRes;
}

LRESULT CFTPropDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes = FALSE;

    switch (GET_WM_COMMAND_ID(wParam, lParam))
    {
        case IDC_FT_PROP_NEW:
            lRes = OnNewButton(GET_WM_COMMAND_CMD(wParam, lParam));
            break;

        case IDC_FT_PROP_REMOVE:
            lRes = OnDeleteButton(GET_WM_COMMAND_CMD(wParam, lParam));
            break;

        case IDC_FT_PROP_EDITTYPEOFFILE:
            lRes = OnAdvancedButton(GET_WM_COMMAND_CMD(wParam, lParam));
            break;

        case IDC_FT_PROP_CHANGEOPENSWITH:
            lRes = OnChangeButton(GET_WM_COMMAND_CMD(wParam, lParam));
            break;

        default:
            lRes = CFTDlg::OnCommand(wParam, lParam);
            break;
    }

    return lRes;    
}

LRESULT CFTPropDlg::OnNotify(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes = FALSE;

    LPNMHDR pNMHDR = (LPNMHDR)lParam;
    UINT_PTR idFrom = pNMHDR->idFrom;
    UINT uCode = pNMHDR->code;

    //GET_WM_COMMAND_CMD
    switch(idFrom)
    {
        case IDC_FT_PROP_LV_FILETYPES:
            lRes = OnNotifyListView(uCode, pNMHDR);
            break;
        default:
            lRes = CFTDlg::OnNotify(wParam, lParam);
            break;
    }

    return lRes;    
}

LRESULT CFTPropDlg::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes = FALSE;

    switch(uMsg)
    {
        case WM_CTLCOLORSTATIC:
            lRes = OnCtlColorStatic(wParam, lParam);
            break;

        case WM_FINISHFILLLISTVIEW:
            lRes = OnFinishInitDialog();
            break;

        default:
            lRes = CFTDlg::WndProc(uMsg, wParam, lParam);
            break;
    }

    return lRes;
}
