Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1444 lines
48 KiB

#include "pch.hxx"
#include <shlwapi.h>
#include "resource.h"
#include "msoeobj.h"
#include "strconst.h"
#include "columns.h"
#include "error.h"
#include "imagelst.h"
#include "msgview.h"
#include "shlwapip.h"
#include "goptions.h"
#include "demand.h"
#include "menures.h"
const COLUMN_DATA c_rgColumnData[COLUMN_MAX] =
{
/* COLUMN_TO */ { idsTo, 155, LVCFMT_LEFT, 0 },
/* COLUMN_FROM */ { idsFrom, 155, LVCFMT_LEFT, 0 },
/* COLUMN_SUBJECT */ { idsSubject, 280, LVCFMT_LEFT, 0 },
/* COLUMN_RECEIVED */ { idsReceived, 110, LVCFMT_LEFT, 0 },
/* COLUMN_SENT */ { idsSent, 110, LVCFMT_LEFT, 0 },
/* COLUMN_SIZE */ { idsSize, 75, LVCFMT_RIGHT, 0 },
/* COLUMN_FOLDER */ { idsFolder, 155, LVCFMT_LEFT, 0 },
/* COLUMN_TOTAL */ { idsTotal, 75, LVCFMT_RIGHT, 0 },
/* COLUMN_UNREAD */ { idsUnread, 75, LVCFMT_RIGHT, 0 },
/* COLUMN_NEW */ { idsNew, 75, LVCFMT_RIGHT, 0 },
/* COLUMN_DESCRIPTION */ { idsDescription, 250, LVCFMT_LEFT, 0 },
/* COLUMN_LAST_UPDATED */ { idsLastUpdated, 155, LVCFMT_LEFT, 0 },
/* COLUMN_WASTED_SPACE */ { idsWastedSpace, 75, LVCFMT_RIGHT, 0 },
/* COLUMN_ACCOUNT */ { idsAccount, 155, LVCFMT_LEFT, 0 },
/* COLUMN_LINES */ { idsColLines, 75, LVCFMT_RIGHT, 0 },
/* COLUMN_PRIORITY */ { idsColPriority, 19, LVCFMT_LEFT, iiconHeaderPri },
/* COLUMN_ATTACHMENT */ { idsColAttach, 22, LVCFMT_LEFT, iiconHeaderAttach },
/* COLUMN_SHOW */ { idsShow, 39, LVCFMT_LEFT, IICON_TEXTHDR },
/* COLUMN_DOWNLOAD */ { idsColDownload, 155, LVCFMT_LEFT, 0 },
/* COLUMN_NEWSGROUP */ { idsNewsgroup, 155, LVCFMT_LEFT, 0 },
/* COLUMN_FLAG */ { idsFlag, 25, LVCFMT_LEFT, iiconHeaderFlag },
/* COLUMN_SUBSCRIBE */ { idsSubscribe, 59, LVCFMT_LEFT, IICON_TEXTHDR },
/* COLUMN_DOWNLOADMSG */ { idsColDownloadMsg, 23, LVCFMT_LEFT, iiconHeaderDownload },
/* COLUMN_THREADSTATE */ { idsColThreadState, 29, LVCFMT_LEFT, iiconHeaderThreadState }
};
const COLUMN_SET c_rgColDefaultMail[] =
{
{ COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FLAG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FROM, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_RECEIVED, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_ACCOUNT, 0, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_SENT, 0, -1 },
{ COLUMN_TO, 0, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
};
const COLUMN_SET c_rgColDefaultOutbox[] =
{
{ COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_TO, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_ACCOUNT, COLFLAG_VISIBLE, -1 },
{ COLUMN_FROM, 0, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_RECEIVED, 0, -1 },
{ COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
};
const COLUMN_SET c_rgColDefaultNews[] =
{
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_DOWNLOADMSG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_THREADSTATE, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_FROM, COLFLAG_VISIBLE, -1 },
{ COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_SIZE, COLFLAG_VISIBLE, -1 },
{ COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_LINES, 0, -1 }
};
const COLUMN_SET c_rgColDefaultIMAP[] =
{
{ COLUMN_PRIORITY, COLFLAG_VISIBLE, -1 },
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FLAG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_DOWNLOADMSG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FROM, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_RECEIVED, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_SENT, 0, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_TO, 0, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
};
const COLUMN_SET c_rgColDefaultIMAPOutbox[] =
{
{ COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_TO, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_ACCOUNT, COLFLAG_VISIBLE, -1 },
{ COLUMN_FROM, 0, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_RECEIVED, 0, -1 },
{ COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_DOWNLOADMSG, COLFLAG_FIXED_WIDTH, -1 }
};
const COLUMN_SET c_rgColDefaultFind[] =
{
{ COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FLAG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FROM, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_RECEIVED, COLFLAG_VISIBLE, -1 },
{ COLUMN_FOLDER, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_ACCOUNT, 0, -1 },
{ COLUMN_SENT, 0, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_TO, 0, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_LINES, 0, -1 }
};
const COLUMN_SET c_rgColDefaultNewsAccount[] =
{
{ COLUMN_NEWSGROUP, COLFLAG_VISIBLE, -1 },
{ COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
{ COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
{ COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultIMAPAccount[] =
{
{ COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
{ COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
{ COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
{ COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultLocalStore[] =
{
{ COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
{ COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
{ COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultNewsSub[] =
{
{ COLUMN_NEWSGROUP, COLFLAG_VISIBLE, -1 },
{ COLUMN_DESCRIPTION, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultImapSub[] =
{
{ COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultOffline[] =
{
{ COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
{ COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultPickGrp[] =
{
{ COLUMN_NEWSGROUP, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultHTTPMail[] =
{
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_DOWNLOADMSG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_FROM, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_RECEIVED, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
};
const COLUMN_SET c_rgColDefaultHTTPMailAccount[] =
{
{ COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
{ COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
{ COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
{ COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
};
const COLUMN_SET c_rgColDefaultHTTPMailOutbox[] =
{
{ COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_TO, COLFLAG_VISIBLE, -1 },
{ COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
{ COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
{ COLUMN_ACCOUNT, COLFLAG_VISIBLE, -1 },
{ COLUMN_FROM, 0, -1 },
{ COLUMN_SIZE, 0, -1 },
{ COLUMN_RECEIVED, 0, -1 },
{ COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 },
{ COLUMN_DOWNLOADMSG, COLFLAG_FIXED_WIDTH, -1 }
};
// NOTE - Keep this in the same order as COLUMN_SET_TYPE enumeration.
const COLUMN_SET_INFO c_rgColumnSetInfo[COLUMN_SET_MAX] =
{
{ COLUMN_SET_MAIL, ARRAYSIZE(c_rgColDefaultMail), c_rgColDefaultMail, c_szRegMailColsIn, TRUE },
{ COLUMN_SET_OUTBOX, ARRAYSIZE(c_rgColDefaultOutbox), c_rgColDefaultOutbox, c_szRegMailColsOut, TRUE },
{ COLUMN_SET_NEWS, ARRAYSIZE(c_rgColDefaultNews), c_rgColDefaultNews, c_szRegNewsCols, TRUE },
{ COLUMN_SET_IMAP, ARRAYSIZE(c_rgColDefaultIMAP), c_rgColDefaultIMAP, c_szRegIMAPCols, TRUE },
{ COLUMN_SET_IMAP_OUTBOX, ARRAYSIZE(c_rgColDefaultIMAPOutbox), c_rgColDefaultIMAPOutbox, c_szRegIMAPColsOut, TRUE },
{ COLUMN_SET_FIND, ARRAYSIZE(c_rgColDefaultFind), c_rgColDefaultFind, c_szRegFindPopCols, TRUE },
{ COLUMN_SET_NEWS_ACCOUNT, ARRAYSIZE(c_rgColDefaultNewsAccount), c_rgColDefaultNewsAccount, c_szRegAccountNewsCols, FALSE },
{ COLUMN_SET_IMAP_ACCOUNT, ARRAYSIZE(c_rgColDefaultIMAPAccount), c_rgColDefaultIMAPAccount, c_szRegAccountIMAPCols, FALSE },
{ COLUMN_SET_LOCAL_STORE, ARRAYSIZE(c_rgColDefaultLocalStore), c_rgColDefaultLocalStore, c_szRegLocalStoreCols, FALSE },
{ COLUMN_SET_NEWS_SUB, ARRAYSIZE(c_rgColDefaultNewsSub), c_rgColDefaultNewsSub, c_szRegNewsSubCols, FALSE },
{ COLUMN_SET_IMAP_SUB, ARRAYSIZE(c_rgColDefaultImapSub), c_rgColDefaultImapSub, c_szRegImapSubCols, FALSE },
{ COLUMN_SET_OFFLINE, ARRAYSIZE(c_rgColDefaultOffline), c_rgColDefaultOffline, c_szRegOfflineCols, FALSE },
{ COLUMN_SET_PICKGRP, ARRAYSIZE(c_rgColDefaultPickGrp), c_rgColDefaultPickGrp, NULL, FALSE },
{ COLUMN_SET_HTTPMAIL, ARRAYSIZE(c_rgColDefaultHTTPMail), c_rgColDefaultHTTPMail, c_szRegHTTPMailCols, TRUE },
{ COLUMN_SET_HTTPMAIL_ACCOUNT, ARRAYSIZE(c_rgColDefaultHTTPMailAccount), c_rgColDefaultHTTPMailAccount, c_szRegHTTPMailAccountCols, FALSE },
{ COLUMN_SET_HTTPMAIL_OUTBOX, ARRAYSIZE(c_rgColDefaultHTTPMailOutbox), c_rgColDefaultHTTPMailOutbox, c_szRegHTTPMailColsOut, TRUE },
};
/////////////////////////////////////////////////////////////////////////////
// CColumns
//
CColumns::CColumns()
{
m_cRef = 1;
m_fInitialized = FALSE;
m_pColumnSet = NULL;
m_cColumns = 0;
m_idColumnSort = COLUMN_SUBJECT;
m_fAscending = TRUE;
}
CColumns::~CColumns()
{
SafeMemFree(m_pColumnSet);
}
//
// FUNCTION: CColumns::Init()
//
// PURPOSE: Initializes the class with the listview and column set type
// that will be used later.
//
// PARAMETERS:
// [in] hwndList - Handle of the ListView window that we will manage
// columns for.
// [in] type - Type of column set to apply to this window.
//
// RETURN VALUE:
// S_OK - The data was groovy
// E_INVALIDARG - The data was heinous
//
HRESULT CColumns::Initialize(HWND hwndList, COLUMN_SET_TYPE type)
{
// Verify what was given to us
if (!IsWindow(hwndList))
{
AssertSz(!IsWindow(hwndList), "CColumns::Init() - Called with an invalid window handle.");
return (E_INVALIDARG);
}
if (type >= COLUMN_SET_MAX)
{
AssertSz(type >= COLUMN_SET_MAX, "CColumns::Init() - Called with an invalid column set type.");
return (E_INVALIDARG);
}
// Save the information for later
m_wndList.Attach(hwndList);
m_type = type;
m_hwndHdr = ListView_GetHeader(m_wndList);
m_fInitialized = TRUE;
return (S_OK);
}
//
// FUNCTION: CColumns::ApplyColumns()
//
// PURPOSE: Takes the current column set and applies it to the ListView
// that was provided in the call to Init().
//
// RETURN VALUE:
// HRESULT
//
HRESULT CColumns::ApplyColumns(COLUMN_LOAD_TYPE type, LPBYTE pb, DWORD cb)
{
HKEY hkey;
DWORD cbSize, dwType;
LPBYTE pbT = NULL;
COLUMN_PERSIST_INFO *pInfo = (COLUMN_PERSIST_INFO *) pb;
const COLUMN_SET *rgColumns = NULL;
DWORD cColumns = 0;
// Verify that we have been initialized first
if (!m_fInitialized)
{
AssertSz(m_fInitialized, "CColumns::ApplyColumns() - Class has not yet been initialized.");
return (E_UNEXPECTED);
}
// Double check the listview didn't go away
Assert(IsWindow(m_wndList));
// Check to see what we're supposed to do
if (type == COLUMN_LOAD_REGISTRY)
{
Assert(pInfo == NULL);
if (ERROR_SUCCESS == AthUserOpenKey(c_szRegPathColumns, KEY_READ, &hkey))
{
cbSize = 0;
if (c_rgColumnSetInfo[m_type].pszRegValue != NULL &&
ERROR_SUCCESS == RegQueryValueEx(hkey, c_rgColumnSetInfo[m_type].pszRegValue, NULL, &dwType, NULL, &cbSize) &&
dwType == REG_BINARY &&
cbSize > 0)
{
if (MemAlloc((void **)&pbT, cbSize))
{
if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_rgColumnSetInfo[m_type].pszRegValue, NULL, &dwType, pbT, &cbSize))
pInfo = (COLUMN_PERSIST_INFO *) pbT;
}
}
RegCloseKey(hkey);
}
if (pInfo != NULL)
type = COLUMN_LOAD_BUFFER;
else
type = COLUMN_LOAD_DEFAULT;
}
if (type == COLUMN_LOAD_BUFFER)
{
Assert(pInfo);
if (pInfo->dwVersion == COLUMN_PERSIST_VERSION)
{
rgColumns = pInfo->rgColumns;
cColumns = pInfo->cColumns;
}
else
{
// Do the default
type = COLUMN_LOAD_DEFAULT;
}
}
if (type == COLUMN_LOAD_DEFAULT)
{
// Verify some person didn't mess up the c_rgColumnSetInfo array.
Assert(c_rgColumnSetInfo[m_type].type == m_type);
// We couldn't load from the registry, so instead use the defaults.
rgColumns = c_rgColumnSetInfo[m_type].rgColumns;
cColumns = c_rgColumnSetInfo[m_type].cColumns;
}
// Update the listview to use these new columns
_SetListViewColumns(rgColumns, cColumns);
if (pbT != NULL)
MemFree(pbT);
return (S_OK);
}
HRESULT CColumns::Save(LPBYTE pBuffer, DWORD *pcb)
{
HRESULT hr = S_OK;
COLUMN_PERSIST_INFO *pInfo;
DWORD dwSize;
// Collect the information needed to get a COLUMN_PERSIST_INFO struct put
// together. First allocate a struct big enough.
dwSize = sizeof(COLUMN_PERSIST_INFO) + (sizeof(COLUMN_SET) * (m_cColumns - 1));
IF_NULLEXIT(pInfo = (COLUMN_PERSIST_INFO *) new BYTE[dwSize]);
ZeroMemory( pInfo, dwSize);
// Set the basic information
pInfo->dwVersion = COLUMN_PERSIST_VERSION;
pInfo->cColumns = m_cColumns;
// We want to save the _ordered_ version of the columns
DWORD rgOrder[COLUMN_MAX] = {0};
// Get the count of columns in the header. Make sure that matches
// what we think we have.
#ifdef DEBUG
DWORD cOrder;
cOrder = Header_GetItemCount(m_hwndHdr);
Assert(m_cColumns == cOrder);
#endif
// The columns might have been reordered by the user, so get the order
// arrray from the ListView
if (0 == (Header_GetOrderArray(m_hwndHdr, m_cColumns, rgOrder)))
return (E_FAIL);
// Now loop through out current column set and copy it to the structure
COLUMN_SET *pColumnDst;
DWORD iColumn;
for (iColumn = 0, pColumnDst = pInfo->rgColumns; iColumn < m_cColumns; iColumn++, pColumnDst++)
{
Assert(rgOrder[iColumn] < m_cColumns);
*pColumnDst = m_pColumnSet[rgOrder[iColumn]];
if (pColumnDst->id == m_idColumnSort)
{
// Clear out any old flags
pColumnDst->flags &= ~(COLFLAG_SORT_ASCENDING | COLFLAG_SORT_DESCENDING);
// Add the new one
pColumnDst->flags |= (m_fAscending ? COLFLAG_SORT_ASCENDING : COLFLAG_SORT_DESCENDING);
}
else
{
pColumnDst->flags &= ~(COLFLAG_SORT_ASCENDING | COLFLAG_SORT_DESCENDING);
}
}
if (pBuffer == NULL)
{
Assert(pcb == NULL);
LRESULT lRes = AthUserSetValue(c_szRegPathColumns, c_rgColumnSetInfo[m_type].pszRegValue, REG_BINARY, (LPBYTE) pInfo, dwSize);
Assert( lRes == ERROR_SUCCESS);
if (lRes != ERROR_SUCCESS)
{
hr = E_FAIL;
}
}
else if (dwSize <= *pcb)
{
CopyMemory(pBuffer, (LPBYTE) pInfo, dwSize);
*pcb = dwSize;
}
else
{
hr = E_INVALIDARG;
}
exit:
SafeDelete(pInfo);
return hr;
}
//
// FUNCTION: CColumns::_SetListViewColumns()
//
// PURPOSE: Takes the column set provided and inserts those columns into
// the ListView.
//
// PARAMETERS:
// [in] rgColumns - Array of columns to insert into the ListView
// [in] cColumns - Number of columns in rgColumns
//
// RETURN VALUE:
// HRESULT
//
HRESULT CColumns::_SetListViewColumns(const COLUMN_SET *rgColumns, DWORD cColumns)
{
LV_COLUMN lvc;
TCHAR sz[CCHMAX_STRINGRES];
// Set up the LV_COLUMN structure
lvc.pszText = sz;
// Remove any existing columns
while (ListView_DeleteColumn(m_wndList, 0))
;
// Reset this
m_idColumnSort = COLUMN_MAX;
// Loop through all of the columns in the provided rgColumns looking for
// any that have an icon and is visible.
//
// We have to do this because the listview requires that column zero have
// text. If the user doesn't want column zero to have text, ie attachment
// column, then we insert that column as column 1, and use
// ListView_SetColumnOrderArray later to make it appear as if column zero
// was the image-only column. -- steveser
DWORD iColumn;
DWORD iColumnSkip = cColumns;
DWORD iInsertPos = 0;
const COLUMN_SET *pColumn;
for (iColumn = 0, pColumn = rgColumns; iColumn < cColumns; iColumn++, pColumn++)
{
if ((0 == c_rgColumnData[pColumn->id].iIcon) && (pColumn->flags & COLFLAG_VISIBLE))
{
iColumnSkip = iColumn;
// Insert this column into the ListView as column zero
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.iSubItem = 0;
lvc.fmt = c_rgColumnData[pColumn->id].format;
LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
sz, ARRAYSIZE(sz));
// If the column width provided is -1, then it hasn't been
// customized yet so use the default.
if (pColumn->cxWidth == -1)
lvc.cx = c_rgColumnData[pColumn->id].cxWidth;
else
lvc.cx = pColumn->cxWidth;
// Insert the column
ListView_InsertColumn(m_wndList, 0, &lvc);
// Up the count for the next column position
iInsertPos++;
// Check to see if this is the sort column
if ((pColumn->flags & COLFLAG_SORT_ASCENDING) ||
(pColumn->flags & COLFLAG_SORT_DESCENDING))
{
m_idColumnSort = pColumn->id;
m_fAscending = COLFLAG_SORT_ASCENDING == (pColumn->flags & COLFLAG_SORT_ASCENDING);
}
// Bail out of this loop
break;
}
}
// Now insert the rest of the columns, skipping over the column we inserted
// previously (stored in iColumnSkip).
for (iColumn = 0, pColumn = rgColumns; iColumn < cColumns; iColumn++, pColumn++)
{
// If this column is visible and it's not the one we skipped over
if ((pColumn->flags & COLFLAG_VISIBLE) && (iColumn != iColumnSkip))
{
// Figure out what the mask is and load the icon or string
if (c_rgColumnData[pColumn->id].iIcon <= 0)
{
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
sz, ARRAYSIZE(sz));
}
else
{
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_IMAGE | LVCF_SUBITEM;
lvc.iImage = c_rgColumnData[pColumn->id].iIcon;
}
lvc.iSubItem = iInsertPos;
lvc.fmt = c_rgColumnData[pColumn->id].format;
// If the column width provided is -1, then it hasn't been
// customized yet so use the default.
if (pColumn->cxWidth == -1)
lvc.cx = c_rgColumnData[pColumn->id].cxWidth;
else
lvc.cx = pColumn->cxWidth;
// Check to see if this is the sort column
if ((pColumn->flags & COLFLAG_SORT_ASCENDING) ||
(pColumn->flags & COLFLAG_SORT_DESCENDING))
{
// Save the info
m_idColumnSort = pColumn->id;
m_fAscending = COLFLAG_SORT_ASCENDING == (pColumn->flags & COLFLAG_SORT_ASCENDING);
}
// Insert this column
ListView_InsertColumn(m_wndList, iInsertPos, &lvc);
iInsertPos++;
}
}
// If we had to skip over a column, the we need to set the column order
// array so it appears correctly to the user.
if (iColumnSkip > 0 && iColumnSkip < cColumns)
{
DWORD cColumnOrder = 0;
int rgOrder[COLUMN_MAX];
// Add all of the columns to the order array in order up to iColumnSkip
for (iColumn = 1; iColumn <= iColumnSkip; iColumn++)
{
if (rgColumns[iColumn].flags & COLFLAG_VISIBLE)
rgOrder[cColumnOrder++] = iColumn;
}
// Add the skipped column
rgOrder[cColumnOrder++] = 0;
// Add the rest of the columns
for (iColumn = iColumnSkip + 1; iColumn < cColumns; iColumn++)
{
if (rgColumns[iColumn].flags & COLFLAG_VISIBLE)
rgOrder[cColumnOrder++] = iColumn;
}
// Update the ListView
ListView_SetColumnOrderArray(m_wndList, cColumnOrder, rgOrder);
// Reorder the rgColumns passed in to match the order in the ListView
// and keep a copy of it.
if (m_pColumnSet)
g_pMalloc->Free(m_pColumnSet);
m_pColumnSet = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cColumns);
for (iColumn = 0; iColumn < cColumnOrder; iColumn++)
m_pColumnSet[rgOrder[iColumn]] = rgColumns[iColumn];
m_cColumns = cColumnOrder;
}
else
{
// We still need to keep a copy of the column array ordering for
// filling in the virtual ListView later.
if (m_pColumnSet)
g_pMalloc->Free(m_pColumnSet);
m_pColumnSet = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cColumns);
CopyMemory(m_pColumnSet, rgColumns, sizeof(COLUMN_SET) * cColumns);
m_cColumns = iInsertPos;
}
// If we _still_ don't have sort information, then we pick the first sortable
// column.
if (m_idColumnSort == COLUMN_MAX)
{
m_idColumnSort = m_pColumnSet[0].id;
m_fAscending = TRUE;
}
// Make sure the arrow is drawn correctly
SetSortInfo(m_idColumnSort, m_fAscending);
return (S_OK);
}
HRESULT CColumns::GetColumnInfo(COLUMN_SET_TYPE* pType, COLUMN_SET** prgColumns, DWORD *pcColumns)
{
COLUMN_SET rgColumns[COLUMN_MAX];
DWORD cColumns = COLUMN_MAX;
HRESULT hr;
// This one is easy
if (pType)
*pType = m_type;
// Update our list of columns from the ListView
if (FAILED(hr = _GetListViewColumns(rgColumns, &cColumns)))
{
// If we failed, we should return the default information
cColumns = c_rgColumnSetInfo[m_type].cColumns;
CopyMemory(rgColumns, c_rgColumnSetInfo[m_type].rgColumns, sizeof(COLUMN_SET) * cColumns);
}
if (prgColumns)
{
// Need to allocate an array for this
*prgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cColumns);
CopyMemory(*prgColumns, rgColumns, sizeof(COLUMN_SET) * cColumns);
}
if (pcColumns)
*pcColumns = cColumns;
return (S_OK);
}
HRESULT CColumns::_GetListViewColumns(COLUMN_SET* rgColumns, DWORD* pcColumns)
{
DWORD rgOrder[COLUMN_MAX];
DWORD iColumn;
*pcColumns = m_cColumns;
// The columns might have been reordered by the user, so get the order
// arrray from the ListView
if (!Header_GetOrderArray(m_hwndHdr, m_cColumns, rgOrder))
{
// If this fails, we're pretty much out of luck.
return (E_UNEXPECTED);
}
// Duplicate the stored column set
COLUMN_SET rgColumnsTemp[COLUMN_MAX];
CopyMemory(rgColumnsTemp, m_pColumnSet, sizeof(COLUMN_SET) * m_cColumns);
// Reorder the array
for (iColumn = 0; iColumn < m_cColumns; iColumn++)
{
rgColumns[iColumn] = rgColumnsTemp[rgOrder[iColumn]];
rgColumns[iColumn].flags &= ~(COLFLAG_SORT_ASCENDING | COLFLAG_SORT_DESCENDING);
if (m_idColumnSort == rgColumns[iColumn].id)
rgColumns[iColumn].flags |= (m_fAscending ? COLFLAG_SORT_ASCENDING : COLFLAG_SORT_DESCENDING);
}
#ifdef DEBUG
// Dump the array to make sure it's in the right order
COLUMN_SET* pColumn;
for (iColumn = 0, pColumn = rgColumns; iColumn < m_cColumns; iColumn++, pColumn++)
{
TCHAR sz[CCHMAX_STRINGRES];
LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
sz, ARRAYSIZE(sz));
TRACE("Column %d: %s", iColumn, sz);
}
#endif
// Return 'em
return (S_OK);
}
HRESULT CColumns::SetColumnInfo(COLUMN_SET* rgColumns, DWORD cColumns)
{
Assert(rgColumns != NULL);
Assert(cColumns > 0);
// Update the ListView
_SetListViewColumns(rgColumns, cColumns);
return (S_OK);
}
//
// FUNCTION: CColumns::FillSortMenu()
//
// PURPOSE: Fills the provided menu with the list of columns in the ListView
// and checks the item that is already sorted on.
//
// PARAMETERS:
// [in] hMenu - Handle of the menu to insert items into
// [in] idBase - Base ID for the command IDs
// [out] pcItems - Number of items that were inserted by this function
//
// RETURN VALUE:
// S_OK - Everything succeeded
//
HRESULT CColumns::FillSortMenu(HMENU hMenu, DWORD idBase, DWORD *pcItems, DWORD *pidCurrent)
{
TCHAR sz[CCHMAX_STRINGRES];
int ids;
DWORD iItemChecked = -1;
BOOL fAscending = TRUE;
COLUMN_SET rgColumns[COLUMN_MAX];
DWORD cColumns;
// Update our snapshot of the columns in the ListView
_GetListViewColumns(rgColumns, &cColumns);
// If there aren't any columns yet, bail
if (cColumns == 0)
return (E_UNEXPECTED);
// Clear any items that were already on the menu
while ((WORD) -1 != (WORD) GetMenuItemID(hMenu, 0))
DeleteMenu(hMenu, 0, MF_BYPOSITION);
// Loop through and insert a menu item for each column
COLUMN_SET *pColumn = rgColumns;
DWORD iColumn;
for (iColumn = 0; iColumn < cColumns; iColumn++, pColumn++)
{
// Load the string resource for this column
LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
sz, ARRAYSIZE(sz));
// Insert the menu
InsertMenu(hMenu, iColumn, MF_BYPOSITION | MF_STRING | MF_ENABLED,
idBase + iColumn, sz);
// Check to see if this is the column we're currently sorted on
if (pColumn->id == m_idColumnSort)
{
if (pidCurrent)
*pidCurrent = idBase + iColumn;
iItemChecked = iColumn;
fAscending = m_fAscending;
}
}
// Check the item that is sorted on
CheckMenuRadioItem(hMenu, 0, iColumn - 1, iItemChecked, MF_BYPOSITION);
// Check ascending or descending
CheckMenuRadioItem(hMenu, ID_SORT_ASCENDING, ID_SORT_DESCENDING,
fAscending ? ID_SORT_ASCENDING : ID_SORT_DESCENDING, MF_BYCOMMAND);
// If the caller cares, return the number of items we've added
if (pcItems)
*pcItems = iColumn;
return (S_OK);
}
HRESULT CColumns::ColumnsDialog(HWND hwndParent)
{
CColumnsDlg cDialog;
cDialog.Init(this);
cDialog.DoModal(hwndParent);
return (S_OK);
}
DWORD CColumns::GetCount(void)
{
return (m_cColumns);
}
HRESULT CColumns::GetSortInfo(COLUMN_ID *pidColumn, BOOL *pfAscending)
{
if (pidColumn)
*pidColumn = m_idColumnSort;
if (pfAscending)
*pfAscending = m_fAscending;
return (S_OK);
}
HRESULT CColumns::SetSortInfo(COLUMN_ID idColumn, BOOL fAscending)
{
LV_COLUMN lvc;
COLUMN_SET *pColumn;
DWORD iColumn;
// Loop through the column array and verify this column is visible
for (iColumn = 0, pColumn = m_pColumnSet; iColumn < m_cColumns; iColumn++, pColumn++)
{
if (pColumn->id == idColumn)
{
// Remove the sort arrow from the previously sorted column
if (c_rgColumnSetInfo[m_type].fSort && c_rgColumnData[m_idColumnSort].iIcon == 0)
{
lvc.mask = LVCF_FMT;
lvc.fmt = c_rgColumnData[m_idColumnSort].format;
lvc.fmt &= ~(LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT);
ListView_SetColumn(m_wndList, GetColumn(m_idColumnSort), &lvc);
}
// Update our cached information
m_idColumnSort = idColumn;
m_fAscending = fAscending;
// Update the ListView with a new sort column unless the sort column
// already has an image
if (c_rgColumnSetInfo[m_type].fSort && c_rgColumnData[idColumn].iIcon <= 0)
{
lvc.fmt = LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT | c_rgColumnData[idColumn].format;
lvc.mask = LVCF_IMAGE | LVCF_FMT;
lvc.iImage = fAscending ? iiconSortAsc : iiconSortDesc;
ListView_SetColumn(m_wndList, iColumn, &lvc);
}
return (S_OK);
}
}
return (E_INVALIDARG);
}
COLUMN_ID CColumns::GetId(DWORD iColumn)
{
DWORD rgOrder[COLUMN_MAX];
if (iColumn > m_cColumns)
return COLUMN_MAX;
// The columns might have been reordered by the user, so get the order
// arrray from the ListView
if (0 == Header_GetOrderArray(m_hwndHdr, m_cColumns, rgOrder))
return (COLUMN_MAX);
return (m_pColumnSet[iColumn].id);
}
DWORD CColumns::GetColumn(COLUMN_ID id)
{
COLUMN_SET *pColumn;
DWORD iColumn;
for (iColumn = 0, pColumn = m_pColumnSet; iColumn < m_cColumns; iColumn++, pColumn++)
{
if (pColumn->id == id)
return (iColumn);
}
return (-1);
}
HRESULT CColumns::SetColumnWidth(DWORD iColumn, DWORD cxWidth)
{
if (iColumn > m_cColumns)
return (E_INVALIDARG);
m_pColumnSet[iColumn].cxWidth = cxWidth;
return (S_OK);
}
HRESULT CColumns::InsertColumn(COLUMN_ID id, DWORD iInsertBefore)
{
COLUMN_SET rgOld[COLUMN_MAX];
DWORD cColumns = COLUMN_MAX;
// Update our list of columns from the ListView
_GetListViewColumns(rgOld, &cColumns);
// Allocate an array big enough for all of the possible columns
COLUMN_SET *rgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * (cColumns + 1));
if (!rgColumns)
return (E_OUTOFMEMORY);
// Insert the requested flag first
rgColumns->id = id;
rgColumns->flags = COLFLAG_VISIBLE;
rgColumns->cxWidth = -1;
// Now copy the rest
CopyMemory(&(rgColumns[1]), rgOld, sizeof(COLUMN_SET) * cColumns);
// Set the updated column structure into the ListView
SetColumnInfo(rgColumns, cColumns + 1);
g_pMalloc->Free(rgColumns);
return (S_OK);
}
HRESULT CColumns::IsColumnVisible(COLUMN_ID id, BOOL *pfVisible)
{
if (0 == pfVisible)
return E_INVALIDARG;
// Just do a quick run through the column array to see if the requested
// column is visible
COLUMN_SET *pColumn = m_pColumnSet;
for (DWORD i = 0; i < m_cColumns; i++, pColumn++)
{
if (pColumn->id == id)
{
*pfVisible = !!(pColumn->flags & COLFLAG_VISIBLE);
return (S_OK);
}
}
*pfVisible = FALSE;
return (E_UNEXPECTED);
}
/////////////////////////////////////////////////////////////////////////////
// CColumnsDlg
//
CColumnsDlg::CColumnsDlg() : m_ctlEdit(NULL, this, 1)
{
/*
m_dwTitleID = idsColumnDlgTitle;
m_dwHelpFileID = 0;
m_dwDocStringID = idsColumnDlgTitle;
*/
m_type = COLUMN_SET_MAIL;
m_iItemWidth = -1;
m_pColumnInfo = 0;
m_rgColumns = 0;
}
CColumnsDlg::~CColumnsDlg()
{
SafeRelease(m_pColumnInfo);
if (m_rgColumns)
g_pMalloc->Free(m_rgColumns);
}
#undef SubclassWindow
LRESULT CColumnsDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_hwndList = GetDlgItem(IDC_COLUMN_LIST);
m_ctlEdit.SubclassWindow(GetDlgItem(IDC_WIDTH));
// Set the extended styles on the ListView
ListView_SetExtendedListViewStyle(m_hwndList, LVS_EX_FULLROWSELECT);
// Retrieve some information about the column set we're supposed to be
// displaying.
COLUMN_SET* pColumns;
DWORD cColumns;
m_pColumnInfo->GetColumnInfo(&m_type, &pColumns, &cColumns);
// Allocate an array to hold our column info
DWORD foo = c_rgColumnSetInfo[m_type].cColumns;
Assert(m_rgColumns == NULL);
m_rgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * c_rgColumnSetInfo[m_type].cColumns);
CopyMemory(m_rgColumns, pColumns, sizeof(COLUMN_SET) * cColumns);
g_pMalloc->Free(pColumns);
m_cColumns = cColumns;
// Add a single column to the ListView
RECT rcClient;
::GetClientRect(m_hwndList, &rcClient);
LV_COLUMN lvc;
lvc.mask = LVCF_SUBITEM | LVCF_WIDTH;
lvc.cx = rcClient.right - GetSystemMetrics(SM_CXVSCROLL);
lvc.iSubItem = 0;
ListView_InsertColumn(m_hwndList, 0, &lvc);
// Load the state image bitmap
HIMAGELIST himlState = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idb16x16st),
16, 0, RGB(255, 0, 255));
ListView_SetImageList(m_hwndList, himlState, LVSIL_STATE);
// Fill the ListView
_FillList(m_rgColumns, m_cColumns);
// Set the first item to be focused
ListView_SetItemState(m_hwndList, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
// Everything is clean
SetDirty(FALSE);
return 1; // Let the system set the focus
}
static const HELPMAP g_rgCtxMapColumns[] = {
{IDC_COLUMN_LIST, 50400},
{IDC_MOVEUP, 50405},
{IDC_MOVEDOWN, 50410},
{IDC_SHOW, 50415},
{IDC_HIDE, 50420},
{IDC_WIDTH, 50425},
{IDC_RESET_COLUMNS, 353507},
{0, 0}
};
LRESULT CColumnsDlg::OnHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return(OnContextHelp(m_hWnd, uMsg, wParam, lParam, g_rgCtxMapColumns));
}
HRESULT CColumnsDlg::Apply(void)
{
HRESULT hr = S_OK;
TRACE(_T("CColumnsDlg::Apply\n"));
// Build a column set array from the data in the ListView. Only include
// visible columns.
int cItems = ListView_GetItemCount(m_hwndList);
// Allocate an array big enough for all of the possible columns
COLUMN_SET *rgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cItems);
DWORD cColumns = 0;
if (!rgColumns)
return (E_OUTOFMEMORY);
LV_ITEM lvi;
lvi.mask = LVIF_PARAM | LVIF_STATE;
lvi.stateMask = LVIS_SELECTED;
lvi.iSubItem = 0;
// Loop through the listview
for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++)
{
// Check to see if this one is visible
if (_IsChecked(lvi.iItem))
{
// If so, then retrieve the cached column info pointer
ListView_GetItem(m_hwndList, &lvi);
// And copy the structure into our new array
rgColumns[cColumns] = *((COLUMN_SET *) lvi.lParam);
// If this item was selected, then we should grab the column width
// from the edit box.
if (lvi.state & LVIS_SELECTED)
rgColumns[cColumns].cxWidth = GetDlgItemInt(IDC_WIDTH, NULL, FALSE);
// Make sure the flag sayz visible
rgColumns[cColumns++].flags |= COLFLAG_VISIBLE;
}
}
// Make sure there's at least one column
if (!cColumns)
{
AthMessageBoxW(m_hWnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrSelectOneColumn),
0, MB_ICONEXCLAMATION | MB_OK);
hr = E_UNEXPECTED;
}
else
{
// Set the updated column structure into the ListView
if (SUCCEEDED(m_pColumnInfo->SetColumnInfo(rgColumns, cColumns)))
{
SetDirty(FALSE);
hr = S_OK;
}
else
hr = E_UNEXPECTED;
}
g_pMalloc->Free(rgColumns);
return (hr);
}
LRESULT CColumnsDlg::OnClick(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
{
DWORD dwPos;
LV_HITTESTINFO lvhti;
// Double check this
Assert(idCtrl == IDC_COLUMN_LIST);
// Figure out where the cursor was
dwPos = GetMessagePos();
lvhti.pt.x = (int)(short) LOWORD(dwPos);
lvhti.pt.y = (int)(short) HIWORD(dwPos);
::ScreenToClient(m_hwndList, &(lvhti.pt));
// Ask the ListView where this is
if (-1 == ListView_HitTest(m_hwndList, &lvhti))
return 0;
// If this was on a state image area, toggle the check
if (lvhti.flags == LVHT_ONITEMSTATEICON || pnmh->code == NM_DBLCLK)
{
_SetCheck(lvhti.iItem, !_IsChecked(lvhti.iItem));
}
return (0);
}
LRESULT CColumnsDlg::OnItemChanged(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
{
Assert(idCtrl == IDC_COLUMN_LIST);
// The only change we're looking for is when a new item is selected.
NMLISTVIEW* pnmlv = (NMLISTVIEW *) pnmh;
COLUMN_SET* pColumn = ((COLUMN_SET *) pnmlv->lParam);
DWORD cxWidth = pColumn->cxWidth == -1 ? c_rgColumnData[pColumn->id].cxWidth : pColumn->cxWidth;
// Narrow it down to state changes
if (pnmlv->uChanged & LVIF_STATE)
{
_UpdateButtonState(pnmlv->iItem);
// If the new state contains selected, and the old state does not, then
// we have a new selected item.
if ((pnmlv->uNewState & LVIS_SELECTED) && (pnmlv->uNewState & LVIS_FOCUSED)
&& (0 == (pnmlv->uOldState & LVIS_SELECTED)))
{
LV_ITEM lvi;
lvi.iSubItem = 0;
lvi.mask = LVIF_PARAM;
// If there was a previously selected item
if (m_iItemWidth != -1)
{
lvi.iItem = m_iItemWidth;
ListView_GetItem(m_hwndList, &lvi);
// Save the width
((COLUMN_SET *) lvi.lParam)->cxWidth = GetDlgItemInt(IDC_WIDTH, NULL, FALSE);
}
// Set the column width edit box
SetDlgItemInt(IDC_WIDTH, cxWidth, FALSE);
m_iItemWidth = pnmlv->iItem;
}
}
return (0);
}
BOOL CColumnsDlg::_IsChecked(DWORD iItem)
{
DWORD state;
// Get the state from the selected item
state = ListView_GetItemState(m_hwndList, iItem, LVIS_STATEIMAGEMASK);
return (state & INDEXTOSTATEIMAGEMASK(iiconStateChecked + 1));
}
void CColumnsDlg::_SetCheck(DWORD iItem, BOOL fChecked)
{
ListView_SetItemState(m_hwndList, iItem,
INDEXTOSTATEIMAGEMASK(1 + iiconStateUnchecked + fChecked),
LVIS_STATEIMAGEMASK);
SetDirty(TRUE);
}
void CColumnsDlg::_FillList(const COLUMN_SET *rgColumns, DWORD cColumns)
{
LV_ITEM lvi;
TCHAR sz[CCHMAX_STRINGRES];
COLUMN_SET *pColumn;
BOOL fChecked;
// Set the basic fields in the item struct
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
lvi.iSubItem = 0;
lvi.pszText = sz;
lvi.stateMask = LVIS_STATEIMAGEMASK;
// Loop through the columns in rgColumns, adding each in order to the
// ListView.
for (lvi.iItem = 0, pColumn = (COLUMN_SET *) rgColumns; lvi.iItem < (int) cColumns; lvi.iItem++, pColumn++)
{
// Load the string for the column
LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
sz, ARRAYSIZE(sz));
// Set the checkbox state
fChecked = !!(pColumn->flags & COLFLAG_VISIBLE);
lvi.state = INDEXTOSTATEIMAGEMASK(1 + iiconStateUnchecked + fChecked);
// Save the width in the lParam
if (pColumn->cxWidth == -1)
pColumn->cxWidth = c_rgColumnData[pColumn->id].cxWidth;
lvi.lParam = (LPARAM) pColumn;
// Insert this item into the list
ListView_InsertItem(m_hwndList, &lvi);
}
// Check to see if the columns we just added were the default columns
if (lvi.iItem != (int) c_rgColumnSetInfo[m_type].cColumns)
{
// Now we need to go through and add the columns that are not currently in
// the column set, but could be.
DWORD i, j;
BOOL fInsert;
for (i = 0, pColumn = (COLUMN_SET *) c_rgColumnSetInfo[m_type].rgColumns;
i < c_rgColumnSetInfo[m_type].cColumns;
i++, pColumn++)
{
fInsert = TRUE;
for (j = 0; j < cColumns; j++)
{
if (pColumn->id == m_rgColumns[j].id)
{
fInsert = FALSE;
break;
}
}
// If it wasn't found in m_rgColumns, then insert it
if (fInsert)
{
// Copy the struct
m_rgColumns[lvi.iItem] = *pColumn;
m_rgColumns[lvi.iItem].cxWidth = c_rgColumnData[pColumn->id].cxWidth;
m_rgColumns[lvi.iItem].flags &= ~COLFLAG_VISIBLE;
// Load the string for the column
LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
sz, ARRAYSIZE(sz));
// Set the checkbox state. These are _always_ unchecked.
lvi.state = INDEXTOSTATEIMAGEMASK(1 + iiconStateUnchecked);
// Save the width in the lParam
lvi.lParam = (LPARAM) &m_rgColumns[lvi.iItem];
// Insert this item into the list
ListView_InsertItem(m_hwndList, &lvi);
// Increment the position
lvi.iItem++;
}
}
}
m_cColumns = ListView_GetItemCount(m_hwndList);
}
LRESULT CColumnsDlg::OnShowHide(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
int iItem = -1;
// Loop through the selected items and make them checked
while (-1 != (iItem = ListView_GetNextItem(m_hwndList, iItem, LVNI_SELECTED)))
{
_SetCheck(iItem, wID == IDC_SHOW);
}
return (0);
}
LRESULT CColumnsDlg::OnReset(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
// Remove all of the columns from the ListView
ListView_DeleteAllItems(m_hwndList);
// Fill the array of columns with the default column information
CopyMemory(m_rgColumns, c_rgColumnSetInfo[m_type].rgColumns, sizeof(COLUMN_SET) * c_rgColumnSetInfo[m_type].cColumns);
// Reset the list to contain the default column information
_FillList(m_rgColumns, m_cColumns);
// Set the first item to be focused
ListView_SetItemState(m_hwndList, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
SetDirty(TRUE);
return (0);
}
void CColumnsDlg::_UpdateButtonState(DWORD iItemSel)
{
HWND hwnd;
BOOL fChecked = _IsChecked(iItemSel);
DWORD dwItems = ListView_GetItemCount(m_hwndList);
DWORD dwSel = ListView_GetSelectedCount(m_hwndList);
hwnd = GetFocus();
::EnableWindow(GetDlgItem(IDC_MOVEUP), (iItemSel != 0) && dwSel);
::EnableWindow(GetDlgItem(IDC_MOVEDOWN), (iItemSel != (dwItems - 1)) && dwSel);
::EnableWindow(GetDlgItem(IDC_SHOW), (!fChecked && dwSel));
::EnableWindow(GetDlgItem(IDC_HIDE), fChecked && dwSel);
// don't disable button that has the focus
if (!::IsWindowEnabled(hwnd))
{
hwnd = GetNextDlgTabItem(hwnd, FALSE);
::SetFocus(hwnd);
}
}
LRESULT CColumnsDlg::OnMove(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
COLUMN_SET *pColumn = 0;
// Make sure this is reset
m_iItemWidth = -1;
// Figure out which one is selected
DWORD iItem = ListView_GetNextItem(m_hwndList, -1, LVNI_SELECTED);
// Get the item from the ListView
LV_ITEM lvi;
TCHAR sz[CCHMAX_STRINGRES];
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
lvi.iItem = iItem;
lvi.iSubItem = 0;
lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
lvi.pszText = sz;
lvi.cchTextMax = ARRAYSIZE(sz);
ListView_GetItem(m_hwndList, &lvi);
// Insert this item to the position one up or down from where it is
lvi.iItem += (wID == IDC_MOVEUP) ? -1 : 2;
// Update the column width
pColumn = (COLUMN_SET *) lvi.lParam;
pColumn->cxWidth = GetDlgItemInt(IDC_WIDTH, NULL, FALSE);
ListView_InsertItem(m_hwndList, &lvi);
// Force a redraw of the new item and make sure it's visible
ListView_EnsureVisible(m_hwndList, lvi.iItem, FALSE);
ListView_RedrawItems(m_hwndList, lvi.iItem, lvi.iItem);
// Delete the old item
m_iItemWidth = -1;
ListView_DeleteItem(m_hwndList, iItem + (wID == IDC_MOVEUP));
SetDirty(TRUE);
return (0);
}
LRESULT CColumnsDlg::OnApply(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
{
if (SUCCEEDED(Apply()))
SetWindowLong(DWLP_MSGRESULT, PSNRET_NOERROR);
else
SetWindowLong(DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
return (TRUE);
}