windows-nt/Source/XPSP1/NT/shell/ext/webcheck/updateui.cpp

837 lines
22 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "private.h"
#include "offline.h"
#include "updateui.h"
//xnotfmgr - most of this file can probably get nuked
#define MAX_CAPTION 128
#undef TF_THISMODULE
#define TF_THISMODULE TF_UPDATEAGENT
typedef CLSID COOKIE, *PCOOKIE;
#define SUBITEM_SIZE 4
#define SUBITEM_URL 3
#define SUBITEM_STATUS 2
#define SUBITEM_IMAGE 1
ColInfoType colDlg[] = {
{0, IDS_NAME_COL, 30, LVCFMT_LEFT},
{1, 0, 3, LVCFMT_LEFT},
{2, IDS_STATUS_COL, 10, LVCFMT_LEFT},
{3, IDS_URL_COL, 40, LVCFMT_LEFT},
{4, IDS_SIZE_COL, 7, LVCFMT_RIGHT}
};
#define ILI_SUCCEEDED 0
#define ILI_FAILED 1
#define ILI_UPDATING 2
#define ILI_PENDING 3
#define ILI_SKIPPED 4
#define ILI_SITE 5
#define ILI_CHANNEL 6
#define ILI_DESKTOP 7
const int g_aIconResourceID[] = {
IDI_STAT_SUCCEEDED,
IDI_STAT_FAILED,
IDI_STAT_UPDATING,
IDI_STAT_PENDING,
IDI_STAT_SKIPPED,
IDI_WEBDOC,
IDI_CHANNEL,
IDI_DESKTOPITEM
};
#define MAX_DLG_COL ARRAYSIZE(colDlg)
//struct for saving window state in registry
typedef struct _PROG_PERSIST_STATE
{
short cbSize;
char bDetails;
char bAdjustWindowPos;
RECT rWindow;
int colOrder [MAX_DLG_COL];
int colWidth [MAX_DLG_COL];
} PROG_PERSIST_STATE;
extern void ResizeDialog(HWND hDlg, BOOL bShowDetail); //in update.cpp
const TCHAR c_szProgressWindowSettings[] = TEXT("Progress Preferences");
const UINT CookieSeg = 32;
CCookieItemMap::CCookieItemMap()
{
_map = NULL;
}
CCookieItemMap::~CCookieItemMap()
{
SAFELOCALFREE (_map);
}
STDMETHODIMP CCookieItemMap::Init(UINT size)
{
// Free old junk.
SAFELOCALFREE (_map);
_lParamNext = 0;
_count = 0;
if (size == 0)
_capacity = CookieSeg;
else
_capacity = size;
_map = (CookieItemMapEntry * )MemAlloc(LPTR, sizeof (CookieItemMapEntry) * _capacity);
if (!_map) {
DBG("Failed to allocate memory");
_capacity = 0;
return E_OUTOFMEMORY;
}
return S_OK;
}
STDMETHODIMP CCookieItemMap::ResetMap(void)
{
_count = 0;
return S_OK;
}
STDMETHODIMP CCookieItemMap::FindCookie(LPARAM lParam, CLSID * pCookie)
{
ASSERT(pCookie);
UINT i;
for (i = 0; i < _count; i ++) {
if (_map[i]._id == lParam) {
* pCookie = _map[i]._cookie;
return S_OK;
}
}
*pCookie = CLSID_NULL;
return E_FAIL;
}
STDMETHODIMP CCookieItemMap::FindLParam(CLSID * pCookie, LPARAM * pLParam)
{
ASSERT(pCookie && pLParam);
UINT i;
for (i = 0; i < _count; i ++) {
if (_map[i]._cookie == *pCookie) {
* pLParam = _map[i]._id;
return S_OK;
}
}
*pLParam = (LPARAM)-1;
return E_FAIL;
}
STDMETHODIMP CCookieItemMap::AddCookie(CLSID * pCookie, LPARAM * pLParam)
{
HRESULT hr = FindLParam(pCookie, pLParam);
if (S_OK == hr)
return S_FALSE;
ASSERT(_count <= _capacity);
ASSERT(CookieSeg);
if (_count == _capacity) {
UINT newSize = CookieSeg + _capacity;
void * newBuf = MemReAlloc(_map, newSize * sizeof(CookieItemMapEntry), LHND);
if (!newBuf) {
DBG("AddCookie::Failed to reallocate buffer");
return E_OUTOFMEMORY;
}
_map = (CookieItemMapEntry *)newBuf;
_capacity = newSize;
}
_map[_count]._cookie = *pCookie;
_map[_count]._id = _lParamNext;
_count ++;
*pLParam = _lParamNext;
_lParamNext ++;
return S_OK;
}
STDMETHODIMP CCookieItemMap::DelCookie(CLSID * pCookie)
{
ASSERT(pCookie);
UINT i;
for (i = 0; i < _count; i ++) {
if (_map[i]._cookie == *pCookie) {
if (i == (_count - 1)) {
_count --;
return S_OK;
} else {
_count --;
_map[i]._cookie = _map[_count]._cookie;
_map[i]._id = _map[_count]._id;
return S_OK;
}
}
}
return S_FALSE;
}
///////////////////////////////////////////////////////////////////////////
//
// Other members
//
int CALLBACK CUpdateDialog::SortUpdatingToTop (LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort)
{
//lparams are cookies; lparamsort is source object
CUpdateDialog * pUpdater = (CUpdateDialog*)lParamSort;
if (!pUpdater)
return 0;
if (!pUpdater->m_pController)
return 0;
CLSID cookie;
pUpdater->cookieMap.FindCookie (lParam1, &cookie);
PReportMap pEntry1 = pUpdater->m_pController->FindReportEntry (&cookie);
pUpdater->cookieMap.FindCookie (lParam2, &cookie);
PReportMap pEntry2 = pUpdater->m_pController->FindReportEntry (&cookie);
//in progress precedes all else
if (pEntry1->status == ITEM_STAT_UPDATING)
return (pEntry2->status == ITEM_STAT_UPDATING ? 0 : -1);
if (pEntry2->status == ITEM_STAT_UPDATING)
return 1;
//queued precedes succeeded or skipped
if (pEntry1->status == ITEM_STAT_QUEUED || pEntry1->status == ITEM_STAT_PENDING)
return ((pEntry2->status == ITEM_STAT_QUEUED
|| pEntry2->status == ITEM_STAT_PENDING) ? 0 : -1);
if (pEntry2->status == ITEM_STAT_QUEUED || pEntry2->status == ITEM_STAT_PENDING)
return 1;
return 0; //don't care
}
BOOL CUpdateDialog::SelectFirstUpdatingSubscription()
{
LV_ITEM lvi = {0};
lvi.iSubItem = SUBITEM_IMAGE;
lvi.mask = LVIF_IMAGE;
int cItems = ListView_GetItemCount (m_hLV);
for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++)
{
ListView_GetItem (m_hLV, &lvi);
if (lvi.iImage == ILI_UPDATING)
{
ListView_SetItemState (m_hLV, lvi.iItem, LVIS_SELECTED, LVIS_SELECTED);
return TRUE;
}
}
return FALSE;
}
DWORD CUpdateDialog::SetSiteDownloadSize (PCOOKIE pCookie, DWORD dwNewSize)
{
//returns previous size, for bookkeeping purposes
HRESULT hr;
TCHAR szKSuffix[10];
// Need enough room for DWORD as string + K suffix
TCHAR szBuf[ARRAYSIZE(szKSuffix) + 11];
if (dwNewSize == -1) //shouldn't happen anymore but if it does,
return -1; //deal gracefully
ASSERT(pCookie);
LPARAM itemParam;
hr = cookieMap.FindLParam(pCookie, &itemParam);
if (S_OK != hr)
{
return dwNewSize;
}
LV_ITEM lvi = {0};
LV_FINDINFO lvfi = {0};
lvfi.flags = LVFI_PARAM;
lvfi.lParam = itemParam;
lvi.iItem = ListView_FindItem(m_hLV, -1, &lvfi);
if (lvi.iItem == -1)
return dwNewSize;
lvi.iSubItem = SUBITEM_SIZE;
lvi.mask = LVIF_TEXT;
lvi.pszText = szBuf;
lvi.cchTextMax = sizeof(szBuf);
ListView_GetItem(m_hLV, &lvi);
DWORD dwOldSize = StrToInt (szBuf);
MLLoadString (IDS_SIZE_KB, szKSuffix, ARRAYSIZE(szKSuffix));
wnsprintf (szBuf, ARRAYSIZE(szBuf), "%d%s", dwNewSize, szKSuffix);
ListView_SetItem(m_hLV, &lvi);
return dwOldSize;
}
// This method is actually called from the second thread(only). So far
// I haven't found any sync problem yet. We only change the internal state
// of this object after it's creation in this method, so we won't mess
// it up. About UI, there is a chance when we try to disable 'Skip'
// button, we may come across another request from the primary thread. Since
// these 2 requests are both designated to disable the button, it won't
// matter anyway.
STDMETHODIMP CUpdateDialog::RefreshStatus(PCOOKIE pCookie, LPTSTR name, STATUS newStat, LPTSTR extraInfo)
{
HRESULT hr;
TCHAR szBuf[MAX_URL];
ASSERT(pCookie);
LPARAM itemParam;
hr = cookieMap.FindLParam(pCookie, &itemParam);
if (S_OK != hr) {
if (name) {
hr = AddItem(pCookie, name, newStat);
if (S_OK != hr) {
return E_FAIL;
}
hr = cookieMap.FindLParam(pCookie, &itemParam);
if (S_OK != hr) {
ASSERT(0);
return E_FAIL;
}
} else {
return hr;
}
}
LV_ITEM lvi = {0};
LV_FINDINFO lvfi = {0};
lvfi.flags = LVFI_PARAM;
lvfi.lParam = itemParam;
lvi.iItem = ListView_FindItem(m_hLV, -1, &lvfi);
if (lvi.iItem == -1)
return E_FAIL;
lvi.iSubItem = SUBITEM_STATUS;
lvi.mask = LVIF_TEXT;
ASSERT ((UINT)newStat <= ITEM_STAT_ABORTED);
if (newStat == ITEM_STAT_UPDATING && extraInfo != NULL) //url available, use it
{
TCHAR szFormat[MAX_URL];
MLLoadString (IDS_ITEM_STAT_UPDATING_URL, szFormat, ARRAYSIZE(szFormat));
wnsprintf (szBuf, ARRAYSIZE(szBuf), szFormat, extraInfo);
}
else
{
MLLoadString(IDS_ITEM_STAT_IDLE + newStat, szBuf, ARRAYSIZE(szBuf));
}
lvi.pszText = szBuf;
ListView_SetItem(m_hLV, &lvi);
lvi.iSubItem = SUBITEM_IMAGE;
lvi.mask = LVIF_IMAGE;
switch (newStat) {
case ITEM_STAT_QUEUED:
case ITEM_STAT_PENDING:
lvi.iImage = ILI_PENDING;
break;
case ITEM_STAT_UPDATING:
lvi.iImage = ILI_UPDATING;
//move to top of list -- t-mattgi
//this happens in sort callback function -- just force resort
//after we update the LV control
break;
case ITEM_STAT_SUCCEEDED:
lvi.iImage = ILI_SUCCEEDED;
if (ListView_GetItemState(m_hLV, lvi.iItem, LVIS_SELECTED))
Button_Enable(GetDlgItem(m_hDlg, IDCMD_SKIP), FALSE);
break;
case ITEM_STAT_SKIPPED:
if (ListView_GetItemState(m_hLV, lvi.iItem, LVIS_SELECTED))
Button_Enable(GetDlgItem(m_hDlg, IDCMD_SKIP), FALSE);
lvi.iImage = ILI_SKIPPED;
break;
default:
lvi.iImage = ILI_FAILED;
break;
}
ListView_SetItem(m_hLV, &lvi);
//force resort since item statuses changed
ListView_SortItems (m_hLV, SortUpdatingToTop, this);
return hr;
}
CUpdateDialog::CUpdateDialog()
{
m_bInitialized = FALSE;
}
CUpdateDialog::~CUpdateDialog()
{
}
STDMETHODIMP CUpdateDialog::CleanUp()
{
if (! m_ThreadID || !m_bInitialized)
return S_OK;
if (m_hDlg)
{
PersistStateToRegistry (m_hDlg);
DestroyWindow(m_hDlg);
m_hDlg = NULL;
}
PostThreadMessage(m_ThreadID, WM_QUIT, 0, 0);
return S_OK;
}
STDMETHODIMP CUpdateDialog::Init(HWND hParent, CUpdateController * pController)
{
ASSERT(m_ThreadID);
ASSERT(g_hInst);
ASSERT(pController);
if (m_bInitialized) {
ASSERT(0);
return S_FALSE;
}
if (FAILED(cookieMap.Init()))
return E_FAIL;
HWND hDlg, hLV;
m_pController = pController;
hDlg = CreateDialogParam(MLGetHinst(), MAKEINTRESOURCE(IDD_PROGRESS), hParent, UpdateDlgProc, (LPARAM)this);
if (!hDlg)
return E_FAIL;
hLV = GetDlgItem(hDlg, IDL_SUBSCRIPTION);
if (!hLV) {
EndDialog(hDlg, FALSE);
return E_FAIL;
}
HIMAGELIST hImage;
HICON hIcon;
hImage = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CXSMICON),
ILC_MASK,
ARRAYSIZE(g_aIconResourceID),
0);
if (hImage == NULL) {
TraceMsg(TF_ALWAYS, TEXT("CUpdateDialog::Init - Failed to create ImageList"));
return E_FAIL;
}
for (int i = 0; i < ARRAYSIZE(g_aIconResourceID); i ++) {
if (g_aIconResourceID[i] == IDI_DESKTOPITEM)
{
hinstSrc = MLGetHinst();
}
else
{
hinstSrc = g_hInst;
}
hIcon = LoadIcon(hinstSrc, MAKEINTRESOURCE(g_aIconResourceID[i]));
if (hIcon == NULL) {
ImageList_Destroy(hImage);
DBG("CUpdateDialog::Init - Failed to load icon");
return E_FAIL;
}
ImageList_AddIcon(hImage, hIcon);
DestroyIcon(hIcon);
}
ListView_SetImageList(hLV, hImage, LVSIL_SMALL);
LV_COLUMN lvc;
TEXTMETRIC tm;
HDC hdc;
hdc = GetDC(hDlg);
if (!hdc) {
EndDialog(hDlg, FALSE);
return E_FAIL;
}
GetTextMetrics(hdc, &tm);
ReleaseDC(hDlg, hdc);
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
PROG_PERSIST_STATE state;
GetPersistentStateFromRegistry(state, tm.tmAveCharWidth);
for (UINT ui = 0; ui < MAX_DLG_COL; ui ++)
{
int colIndex;
TCHAR szCaption[MAX_CAPTION];
if (colDlg[ui].ids)
MLLoadString(colDlg[ui].ids, szCaption, MAX_CAPTION);
else
szCaption[0] = (TCHAR)0;
lvc.pszText = szCaption;
lvc.cx = state.colWidth[ui];
lvc.fmt = colDlg[ui].iFmt;
colIndex = ListView_InsertColumn(hLV, ui, & lvc);
if ( -1 == colIndex) {
ASSERT(0);
EndDialog(hDlg, FALSE);
return E_FAIL;
}
}
ListView_SetColumnOrderArray(hLV, MAX_DLG_COL, state.colOrder);
SendMessage (hLV, LVM_SETEXTENDEDLISTVIEWSTYLE,
LVS_EX_HEADERDRAGDROP | LVS_EX_SUBITEMIMAGES,
LVS_EX_HEADERDRAGDROP | LVS_EX_SUBITEMIMAGES);
SendMessage (hDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon (g_hInst, MAKEINTRESOURCE (IDI_DOWNLOAD)));
SendMessage (hDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon (g_hInst, MAKEINTRESOURCE (IDI_DOWNLOAD)));
if (state.bAdjustWindowPos)
{
//adjust size of *details view* to stored state; if we're not in
//details view, we have to go there temporarily. (The non-details
//view will pick up the position from the details view when we switch back.)
m_bDetail = TRUE;
ResizeDialog (hDlg, m_bDetail);
//don't move dialog, just resize it and center it
//convert right, bottom coordinates to width, height
state.rWindow.right -= state.rWindow.left;
state.rWindow.bottom -= state.rWindow.top;
//calculate left, top to center dialog
state.rWindow.left = (GetSystemMetrics (SM_CXSCREEN) - state.rWindow.right) / 2;
state.rWindow.top = (GetSystemMetrics (SM_CYSCREEN) - state.rWindow.bottom) / 2;
MoveWindow (hDlg, state.rWindow.left, state.rWindow.top,
state.rWindow.right, state.rWindow.bottom, TRUE);
//REVIEW: this centers the details view, then if they don't want details,
//leaves the small dialog with its upper left where the upper left of the
//big dialog is when it's centered. I could center it in whatever view
//it's really in, but if the details view is resized to a fairly large window
//and we bring it up centered in non-details, then when they click details
//the position will be the same and the window will potentially extend offscreen
//to the right and bottom.
//set back to non-details view if that was how it was last used
if (!state.bDetails)
{
m_bDetail = state.bDetails;
ResizeDialog (hDlg, m_bDetail);
}
}
m_bInitialized = TRUE;
m_hDlg = hDlg;
m_hLV = hLV;
m_hParent = hParent;
m_cDlKBytes = 0;
m_cDlDocs = 0;
return S_OK;
}
BOOL CUpdateDialog::PersistStateToRegistry (HWND hDlg)
{
PROG_PERSIST_STATE state;
state.cbSize = sizeof(state);
state.bDetails = m_bDetail;
state.bAdjustWindowPos = TRUE;
//save position and size from *details view* -- if we're not there,
//we'll have to switch temporarily.
BOOL bTempDetail = m_bDetail;
if (!bTempDetail)
{
ShowWindow (hDlg, SW_HIDE);
m_bDetail = TRUE;
ResizeDialog (hDlg, m_bDetail);
}
GetWindowRect (hDlg, &state.rWindow);
if (!bTempDetail)
{
m_bDetail = FALSE;
ResizeDialog (hDlg, m_bDetail);
ShowWindow (hDlg, SW_SHOW);
}
HWND hLV = GetDlgItem (hDlg, IDL_SUBSCRIPTION);
ListView_GetColumnOrderArray (hLV, MAX_DLG_COL, state.colOrder);
for (int i=0; i<MAX_DLG_COL; i++)
state.colWidth[i] = ListView_GetColumnWidth (hLV, i);
HKEY key;
DWORD dwDisposition;
if (ERROR_SUCCESS != RegCreateKeyEx (HKEY_CURRENT_USER, c_szRegKey, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_WRITE, NULL, &key, &dwDisposition))
return FALSE;
RegSetValueEx (key, c_szProgressWindowSettings, 0, REG_BINARY, (LPBYTE)&state, sizeof(state));
RegCloseKey (key);
return TRUE;
}
BOOL CUpdateDialog::GetPersistentStateFromRegistry (PROG_PERSIST_STATE& state, int iCharWidth)
{
HKEY key;
DWORD dwType;
DWORD dwSize = sizeof(state);
RegOpenKeyEx (HKEY_CURRENT_USER, c_szRegKey, 0, KEY_READ, &key);
LONG result = RegQueryValueEx (key, c_szProgressWindowSettings,
0, &dwType, (LPBYTE)&state, &dwSize);
if (ERROR_SUCCESS != result || dwType != REG_BINARY || dwSize != sizeof(state))
{
state.cbSize = 0; //flag as error
}
if (state.cbSize != sizeof(state)) //error or incorrect registry format/version
{ //state not saved in registry; use defaults
int i;
state.bDetails = FALSE;
state.bAdjustWindowPos = FALSE;
state.colOrder[0] = 1;
state.colOrder[1] = 0;
for (i=2; i<MAX_DLG_COL; i++)
state.colOrder[i] = i;
for (i=0; i<MAX_DLG_COL; i++)
state.colWidth[i] = colDlg[i].cchCol * iCharWidth;
}
RegCloseKey (key);
return TRUE;
}
STDMETHODIMP CUpdateDialog::Show(BOOL bShow)
{
if (!m_bInitialized) {
ASSERT(0);
return E_FAIL;
}
ASSERT(m_hDlg);
ShowWindow(m_hDlg, (bShow)?SW_SHOW:SW_HIDE);
ShowWindow(m_hLV, (bShow)?SW_SHOW:SW_HIDE);
return NOERROR;
}
STDMETHODIMP CUpdateDialog::ResetDialog(void)
{
if (!m_bInitialized) {
return S_OK;
}
ASSERT(m_hLV);
ListView_DeleteAllItems(m_hLV);
cookieMap.ResetMap();
m_bInitialized = FALSE;
return S_OK;
}
STDMETHODIMP CUpdateDialog::IItem2Cookie(const int iItem, CLSID * pCookie)
{
LV_ITEM item = {0};
HRESULT hr;
ASSERT(pCookie);
item.iItem = iItem;
item.iSubItem = 0;
item.mask = LVIF_PARAM;
if (!ListView_GetItem(m_hLV, &item)) {
return E_FAIL;
}
hr = cookieMap.FindCookie(item.lParam, pCookie);
ASSERT(SUCCEEDED(hr));
return hr;
}
STDMETHODIMP CUpdateDialog::GetSelectedCookies(CLSID * pCookies, UINT * pCount)
{
if (!m_bInitialized)
return E_INVALIDARG;
ASSERT(pCookies && pCount);
int index = -1;
*pCount = 0;
index = ListView_GetNextItem(m_hLV, index, LVNI_ALL | LVNI_SELECTED);
while (-1 != index) {
if (FAILED(IItem2Cookie(index, pCookies + *pCount))) {
ASSERT(0);
return S_FALSE;
}
index = ListView_GetNextItem(m_hLV, index, LVNI_ALL | LVNI_SELECTED);
*pCount = *pCount + 1;
}
return S_OK;
}
STDMETHODIMP CUpdateDialog::GetSelectionCount(UINT * pCount)
{
if (!m_bInitialized)
return E_INVALIDARG;
ASSERT(pCount);
* pCount = ListView_GetSelectedCount(m_hLV);
return S_OK;
}
STDMETHODIMP CUpdateDialog::AddItem(CLSID * pCookie, LPTSTR name, STATUS stat)
{
HRESULT hr;
TCHAR szBuf[MAX_URL];
LV_ITEM lvi = {0};
BOOL bNew;
lvi.iSubItem = 0;
hr = cookieMap.AddCookie(pCookie, &(lvi.lParam));
if (S_OK == hr) {
bNew = TRUE;
} else if (S_FALSE == hr) {
bNew = FALSE;
} else {
return hr;
}
lvi.pszText = name;
if (bNew) {
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
switch (m_pController->GetSubscriptionType(pCookie))
{
case SUBSTYPE_CHANNEL:
lvi.iImage = ILI_CHANNEL;
break;
case SUBSTYPE_DESKTOPURL:
case SUBSTYPE_DESKTOPCHANNEL:
lvi.iImage = ILI_DESKTOP;
break;
case SUBSTYPE_URL:
default:
lvi.iImage = ILI_SITE;
break;
}
lvi.iItem = ListView_InsertItem(m_hLV, &lvi);
if (lvi.iItem == -1)
return E_FAIL;
if (lvi.iItem == 0) {
ListView_SetItemState(m_hLV, 0, LVIS_SELECTED, LVIS_SELECTED);
}
} else {
LV_FINDINFO lvfi = {0};
lvfi.flags = LVFI_PARAM;
lvfi.lParam = lvi.lParam;
lvi.iItem = ListView_FindItem(m_hLV, -1, &lvfi);
if (lvi.iItem == -1)
return E_FAIL;
lvi.mask = LVIF_TEXT;
ListView_SetItem(m_hLV, &lvi);
}
//add subitem for status icon
lvi.mask = LVIF_IMAGE;
lvi.iSubItem ++; // Icon field.
switch (stat) {
case ITEM_STAT_QUEUED:
case ITEM_STAT_PENDING:
lvi.iImage = ILI_PENDING;
break;
case ITEM_STAT_UPDATING:
lvi.iImage = ILI_UPDATING;
break;
case ITEM_STAT_SUCCEEDED:
lvi.iImage = ILI_SUCCEEDED;
if (ListView_GetItemState(m_hLV, lvi.iItem, LVIS_SELECTED))
Button_Enable(GetDlgItem(m_hDlg, IDCMD_SKIP), FALSE);
break;
case ITEM_STAT_SKIPPED:
lvi.iImage = ILI_SKIPPED;
if (ListView_GetItemState(m_hLV, lvi.iItem, LVIS_SELECTED))
Button_Enable(GetDlgItem(m_hDlg, IDCMD_SKIP), FALSE);
default:
lvi.iImage = ILI_FAILED;
break;
}
ListView_SetItem(m_hLV, &lvi);
//add subitem for status text
lvi.mask = LVIF_TEXT;
ASSERT ((UINT)stat <= ITEM_STAT_SUCCEEDED);
MLLoadString(IDS_ITEM_STAT_IDLE + stat, szBuf, ARRAYSIZE(szBuf));
lvi.pszText = szBuf;
lvi.iSubItem ++;
ListView_SetItem(m_hLV, &lvi);
//add subitem for URL
PReportMap prm = m_pController->FindReportEntry (pCookie);
lvi.pszText = prm->url;
lvi.iSubItem++;
ListView_SetItem(m_hLV, &lvi);
//add subitem for size
lvi.pszText = TEXT("");
lvi.iSubItem++;
ListView_SetItem(m_hLV, &lvi);
return S_OK;
}