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

1560 lines
41 KiB
C++

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