#include "pch.hxx" #include #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); }