//@doc //@module AutoListCtrl.cpp | Implementation of CAutoListCtrl class //------------------------------------------------------------------- // CAutoListCtrl implementation file //------------------------------------------------------------------- // // Copyright ©2000 Virtual Office Systems Incorporated // All Rights Reserved // // This code may be used in compiled form in any way you desire. This // file may be redistributed unmodified by any means PROVIDING it is // not sold for profit without the authors written consent, and // providing that this notice and the authors name is included. // // This code can be compiled, modified and distributed freely, providing // that this copyright information remains intact in the distribution. // // This code may be compiled in original or modified form in any private // or commercial application. // // This file is provided "as is" with no expressed or implied warranty. // The author accepts no liability for any damage, in any form, caused // by this code. Use it at your own risk. //------------------------------------------------------------------- #include "commctrl.h" #include "stdafx.h" #include "AutoListCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif class CAutoListItemData { public: CAutoListItemData(DWORD dwUser = 0); ~CAutoListItemData(); CMapWordToPtr& GetTextMap() { return m_Map; } DWORD GetData() { return m_dwUser; } BOOL SetData(DWORD dwUser) { m_dwUser = dwUser; return TRUE; } private: CMapWordToPtr m_Map; DWORD m_dwUser; }; CAutoListItemData::CAutoListItemData(DWORD dwUser) { m_dwUser = dwUser; } CAutoListItemData::~CAutoListItemData() { } ///////////////////////////////////////////////////////////////////////////// // CAutoListCtrl //@mfunc Constructor for CAutoListCtrl class CAutoListCtrl::CAutoListCtrl() { m_uColumnCount = 0; m_dwImageWidth = 0; m_fLocked = FALSE; m_nSortColumn = -1; } //@mfunc Destructor for CAutoListCtrl class CAutoListCtrl::~CAutoListCtrl() { } BEGIN_MESSAGE_MAP(CAutoListCtrl, CListCtrl) //{{AFX_MSG_MAP(CAutoListCtrl) ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick) ON_WM_DESTROY() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CAutoListCtrl message handlers //@mfunc Insert a column into the list control //@rdesc Returns the index of the new column, or -1 if there is an error. int CAutoListCtrl::InsertColumn( int nCol, //@parm Index of the column to insert before. // @flag -1 | Add column at end of list LPCTSTR pszColumnHeading, //@parm Title of the column int nFormat, //@parm Format of the column. See CListCtrl // for details. int nWidth, //@parm Width of the column in pixels. // @flag -1 (default) | Autosize the column to // the header text int nSubItem) //@parm Index of the subitem associated with // the column. Default is -1. // @flag -1 (default) | No subitem is associatied // with the column. { ASSERT(AfxIsValidString(pszColumnHeading)); if(nCol == -1) { nCol = m_uColumnCount; } int iNew = CListCtrl::InsertColumn(nCol, pszColumnHeading, nFormat, nWidth, nSubItem); if(iNew != -1) { if(nWidth == -1) { CListCtrl::SetColumnWidth(iNew, LVSCW_AUTOSIZE_USEHEADER); nWidth = CListCtrl::GetColumnWidth(iNew); } if((int)m_uColumnCount <= nCol) { m_uColumnCount = nCol + 1; m_HeaderWidth.SetAtGrow(nCol, nWidth); m_ColumnType.SetAtGrow(nCol, CAutoListCtrl::TextCol); } else SetMinColumnWidth(nCol, nWidth); } return iNew; } //@mfunc Insert a column into the list control //@rdesc Returns the index of the new column, or -1 if there is an error. int CAutoListCtrl::InsertColumn( int nCol, //@parm Index of the column to insert before. // @flag -1 | Add column at end of list const LV_COLUMN* pColumn ) //@parm Pointer to structure // containing parameters for new column. { ASSERT(AfxIsValidAddress(pColumn, sizeof(LV_COLUMN), FALSE)); if(nCol == -1) nCol = m_uColumnCount; int iNew = CListCtrl::InsertColumn(nCol, pColumn); if(iNew != -1) { if( pColumn->cx == -1) { CListCtrl::SetColumnWidth(iNew, LVSCW_AUTOSIZE_USEHEADER); ((LV_COLUMN*)pColumn)->cx = CListCtrl::GetColumnWidth(iNew); } if((int)m_uColumnCount <= nCol) { m_uColumnCount = nCol + 1; m_HeaderWidth.SetAtGrow(nCol, pColumn->cx); m_ColumnType.SetAtGrow(nCol, CAutoListCtrl::TextCol); } else SetMinColumnWidth(nCol, pColumn->cx); } return iNew; } //@mfunc Remove a column from the list //@rdesc TRUE | Column Removed //@rdesc FALSE | Unable to remove column or column does not exist. BOOL CAutoListCtrl::DeleteColumn( int nCol ) //@parm Zero based index of column to remove. { BOOL fSuccess = CListCtrl::DeleteColumn(nCol); if(fSuccess) { if(m_uColumnCount) m_uColumnCount--; } return fSuccess; } //@mfunc Insert a new item into the list //@rdesc Returns the index of the new item. //@rvalue -1 | Unable to insert item int CAutoListCtrl::InsertItem( const LV_ITEM* pItem ) //@parm Pointer to structure containing // parameters for the new item. { ASSERT(AfxIsValidAddress(pItem, sizeof(LV_ITEM), FALSE)); if(GetSortColumn() == -1) { if(pItem->iItem == -1) (int)pItem->iItem = GetItemCount(); } int iNew = CListCtrl::InsertItem(pItem); if(iNew != -1) { CListCtrl::SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER); // LVSCW_AUTOSIZE int iNewWidth = CListCtrl::GetColumnWidth(0); SetMinColumnWidth(0, iNewWidth); CAutoListItemData* pData = new CAutoListItemData; CString* pstrCur; if(pData->GetTextMap().Lookup(0, (PVOID&)pstrCur)) { pData->GetTextMap().RemoveKey(0); delete pstrCur; } pData->GetTextMap().SetAt(0, new CString( GetItemText(iNew, 0) )); for(int iCol = 1; iCol < GetColumnCount(); iCol++) pData->GetTextMap().SetAt(iCol, new CString("")); CListCtrl::SetItemData(iNew, (DWORD)pData); } return iNew; } //@mfunc Insert a new item into the list //@rdesc Returns the index of the new item. //@rvalue -1 | Unable to insert item int CAutoListCtrl::InsertItem( int nItem, //@parm Index where item is to be inserted LPCTSTR pszItem ) //@parm Name of the item { ASSERT(AfxIsValidString(pszItem)); LV_ITEM Item; Item.mask = LVIF_TEXT; Item.iItem = nItem; Item.iSubItem = 0; Item.pszText = (LPTSTR)pszItem; return InsertItem(&Item); } //@mfunc Insert a new item into the list //@rdesc Returns the index of the new item. //@rvalue -1 | Unable to insert item int CAutoListCtrl::InsertItem( int nItem, //@parm Index where item is to be inserted LPCTSTR pszItem, //@parm Name of the item int nImage ) //@parm Image in imagemap to use for this item { ASSERT(AfxIsValidString(pszItem)); LV_ITEM Item; Item.mask = LVIF_TEXT | LVIF_IMAGE; Item.iItem = nItem; Item.iSubItem = 0; Item.pszText = (LPTSTR)pszItem; Item.iImage = nImage; return InsertItem(&Item); } //@mfunc Insert a new item into the list //@rdesc Returns the index of the new item. //@rvalue -1 | Unable to insert item int CAutoListCtrl::InsertItem( UINT nMask, //@parm See for details int nItem, //@parm Index where item is to be inserted LPCTSTR pszItem, //@parm Name of the item UINT nState, //@parm See for details UINT nStateMask, //@parm See for details int nImage, //@parm Image from image map to use for this item LPARAM lParam ) //@parm 32-bit value to associate with this item { ASSERT(AfxIsValidString(pszItem)); LV_ITEM Item; Item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; Item.iItem = nItem; Item.iSubItem = 0; Item.state = nState; Item.stateMask = nStateMask; Item.pszText = (LPTSTR)pszItem; Item.iImage = nImage; Item.lParam = lParam; return InsertItem(&Item); } //@mfunc Set or change the text for a specified column in an item. //@rvalue TRUE | Text set successfully //@rvalue FALSE | Unable to set text for the specified item or column. BOOL CAutoListCtrl::SetItemText( int nItem, //@parm Index of item to set int nSubItem, //@parm 0 based column number LPCTSTR pszText) //@parm New text value for the column { ASSERT(AfxIsValidString(pszText)); BOOL fSuccess = CListCtrl::SetItemText(nItem, nSubItem, pszText); if(fSuccess) { CListCtrl::SetColumnWidth(nSubItem, LVSCW_AUTOSIZE_USEHEADER); // LVSCW_AUTOSIZE int iNewWidth = CListCtrl::GetColumnWidth(nSubItem); SetMinColumnWidth(nSubItem, iNewWidth); CAutoListItemData *pData = (CAutoListItemData*)CListCtrl::GetItemData(nItem); CString* pstrCur; if(pData->GetTextMap().Lookup(nSubItem, (PVOID&)pstrCur)) { pData->GetTextMap().RemoveKey(nSubItem); delete pstrCur; } pData->GetTextMap().SetAt(nSubItem, new CString(pszText)); CString strName = GetItemText(nItem, 0); } return fSuccess; } //@mfunc Retrieve an array of the indexes of all selected items in the list //@rvalue Returns the number of elements returned. int CAutoListCtrl::GetSelectedItems( int iMaxCount, // Size of

int *pIndexArray) // Array of integers to hold item indexes { ASSERT(AfxIsValidAddress(pIndexArray, sizeof(int) * iMaxCount)); int iCount = 0; for(int i = 0; i < GetItemCount(); i++) { if(GetItemState(i, LVIS_SELECTED)) { if(iCount >= iMaxCount) break; pIndexArray[iCount++] = i; } } return iCount; } //@mfunc Change the text of a column header //@rvalue TRUE | Column text set successfully //@rvalue FALSE | Unable to set column text BOOL CAutoListCtrl::SetColumnText( int nCol, //@parm Zero based column number to set. LPCTSTR pszHeading) //@parm New column header text { ASSERT(AfxIsValidString(pszHeading)); LV_COLUMN ColumnInfo; BOOL fSuccess; ColumnInfo.mask = LVCF_TEXT; ColumnInfo.pszText = (LPTSTR)pszHeading; ColumnInfo.cchTextMax = _tcslen(pszHeading); fSuccess = SetColumn(nCol, &ColumnInfo); if(fSuccess) { if(*pszHeading == 0) { m_HeaderWidth[nCol] = 0; SetColumnWidth(nCol, 0); } else { ListView_SetColumnWidth(m_hWnd, nCol, LVSCW_AUTOSIZE_USEHEADER); // LVSCW_AUTOSIZE if(GetColumnWidth(nCol) < GetHeaderWidth(nCol)) SetColumnWidth(nCol, GetHeaderWidth(nCol)); } } return fSuccess; } //@mfunc Assigns an image list to a list view control. //@rdesc Returns a pointer to the previous image list. CImageList* CAutoListCtrl::SetImageList( CImageList* pImageList, //@parm Pointer to the image list to assign int nImageList ) //@parm Type of image list. See // for // details. { ASSERT(AfxIsValidAddress(pImageList, sizeof(CImageList), FALSE)); if(pImageList) { IMAGEINFO ImageInfo; memset(&ImageInfo, 0, sizeof(IMAGEINFO)); pImageList->GetImageInfo(0, &ImageInfo); m_dwImageWidth = ImageInfo.rcImage.right - ImageInfo.rcImage.left; } ModifyStyle(0, LVS_SHOWSELALWAYS, 0); return CListCtrl::SetImageList(pImageList, nImageList); } //@mfunc Notification from list control that the user has clicked // on the header of a column. void CAutoListCtrl::OnColumnclick( NMHDR* pNMHDR, //@parm Pointer to structure LRESULT* pResult) //@parm Pointer to result. // @flag 0 | Message processed { ASSERT(AfxIsValidAddress(pNMHDR, sizeof(NMHDR), FALSE)); ASSERT(AfxIsValidAddress(pResult, sizeof(LRESULT), TRUE)); NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; SortOnColumn(pNMListView->iSubItem); *pResult = 0; } //@mfunc Callback function used by list control for sorting. //@rvalue 1 | Item 1 should be placed before Item 2 //@rvalue 0 | Item 1 and Item 2 are identical //@rvalue 1 | Item 2 should be placed before Item 1 int CALLBACK CAutoListCtrl::CompareItems( LPARAM lParam1, //@parm Pointer to containing // text values for each column in item 1 LPARAM lParam2, //@parm Pointer to containing // text values for each column in item 2 LPARAM lParamSort) //@parm Pointer to the list control being sorted. //@devnote Required since this is a static // function which therefore does not have a // this pointer. { CMapWordToPtr *pMap1 = (CMapWordToPtr*)lParam1; CMapWordToPtr *pMap2 = (CMapWordToPtr*)lParam2; CAutoListCtrl *pThis = (CAutoListCtrl*)lParamSort; ASSERT(pMap1); ASSERT(pMap2); CString *pValue1; CString *pValue2; VERIFY( pMap1->Lookup(pThis->GetSortColumn(), (PVOID&)pValue1) ); VERIFY( pMap2->Lookup(pThis->GetSortColumn(), (PVOID&)pValue2) ); int iResult; switch(pThis->GetColumnType(pThis->GetSortColumn() )) { case CAutoListCtrl::TextCol: iResult = pValue1->Compare(*pValue2); break; case CAutoListCtrl::NumberCol: { ASSERT(pThis->GetSortColumn() != 0); int nVal1 = atoi((char*)(LPCTSTR)pValue1); int nVal2 = atoi((char*)(LPCTSTR)pValue2); iResult = (nVal2 - nVal1); break; } case CAutoListCtrl::FileSizeCol: { ASSERT(pThis->GetSortColumn() != 0); int nVal1 = atoi((char*)(LPCTSTR)*pValue1); int nVal2 = atoi((char*)(LPCTSTR)*pValue2); if(pValue1->Right(1) == "K") nVal1 *= 1024; else if(pValue1->Right(1) == "M") nVal1 *= 1024000; if(pValue2->Right(1) == "K") nVal2 *= 1024; else if(pValue2->Right(1) == "M") nVal2 *= 1024000; iResult = nVal2 - nVal1; break; } case CAutoListCtrl::DateCol: { ASSERT(pThis->GetSortColumn() != 0); COleDateTime Date1, Date2; Date1.ParseDateTime((LPCTSTR)*pValue1, VAR_DATEVALUEONLY); Date2.ParseDateTime((LPCTSTR)*pValue2, VAR_DATEVALUEONLY); if(Date1 < Date2) iResult = -1; else if (Date1 > Date2) iResult = 1; else iResult = 0; break; } default: ASSERT(FALSE); } if(!pThis->IsSortAscending()) iResult = -iResult; if((iResult == 0) && (pThis->GetSortColumn() != 0)) { // Use name as a secondary key for identical primary keys VERIFY( pMap1->Lookup(0, (PVOID&)pValue1) ); VERIFY( pMap2->Lookup(0, (PVOID&)pValue2) ); iResult = pValue1->Compare(*pValue2); if(!pThis->IsSortAscending()) iResult = -iResult; } return iResult; } //@mfunc Sort contents of list on specified column. Initially, // the column will be sorted ascending. If the list is // already sorted on the specified column, the sort will // toggle between ascending and descending sort order on // each subsequent call. void CAutoListCtrl::SortOnColumn( int nCol) //@parm Zero based column number. { if(nCol == -1) return; ASSERT(nCol < GetColumnCount()); if(nCol == m_nSortColumn) m_fSortAscending = !m_fSortAscending; else { m_nSortColumn = nCol; m_fSortAscending = TRUE; } SortItems(CompareItems, (DWORD)this); } //@mfunc Delete an item from the list //@rvalue TRUE | Item deleted successfully //@rvalue FALSE | Item could not be deleted BOOL CAutoListCtrl::DeleteItem( int nItem ) //@parm Index of item to delete { CAutoListItemData* pData = (CAutoListItemData*)CListCtrl::GetItemData(nItem); if(pData) { POSITION pos = pData->GetTextMap().GetStartPosition(); WORD wKey; CString* pstrValue; while(pos) { pData->GetTextMap().GetNextAssoc(pos, wKey, (PVOID&)pstrValue); delete pstrValue; } delete pData; } BOOL fSuccess = CListCtrl::DeleteItem(nItem); return fSuccess; } //@mfunc Delete all items in the list //@rvalue TRUE | All items deleted //@rvalue FALSE | No items in the list BOOL CAutoListCtrl::DeleteAllItems( ) { CAutoListItemData* pData = NULL; POSITION pos; WORD wKey; CString* pstrValue; for(int nItem = 0; nItem < GetItemCount(); nItem++) { pData = (CAutoListItemData*)CListCtrl::GetItemData(nItem); if(pData) { pos = pData->GetTextMap().GetStartPosition(); while(pos) { pData->GetTextMap().GetNextAssoc(pos, wKey, (PVOID&)pstrValue); delete pstrValue; } delete pData; } } BOOL fSuccess = CListCtrl::DeleteAllItems(); return fSuccess; } //@mfunc Set the type of data in the specified column. Used // for sorting to determine proper sorting method to use. //@rvalue TRUE | Column type set successfully //@rvalue FALSE | Column type could not be set. BOOL CAutoListCtrl::SetColumnType( int nCol, //@parm Zero based index of column to set int nType) //@parm Type of data. Default is // CAutoListCtrl::TextCol. // @flag TextCol | Text // @flag NumberCol | Numeric Value // @flag FileSizeCol | File Size. Supports // terminology such as 45.3k and 3.2Meg // for sorting purposes. // @flag DateCol | Values are dates (international) { if(nCol > GetColumnCount()) return FALSE; switch(nType) { case CAutoListCtrl::TextCol: case CAutoListCtrl::NumberCol: case CAutoListCtrl::FileSizeCol: case CAutoListCtrl::DateCol: m_ColumnType.SetAt(nCol, nType); return TRUE; default: return FALSE; } } //@mfunc Determine the type of a column //@rdesc Type of the column. See // for details. int CAutoListCtrl::GetColumnType( int nCol) // @parm Zero based column number { if((nCol < 0) || (nCol > GetColumnCount() )) return -1; return m_ColumnType[nCol]; } void CAutoListCtrl::OnDestroy() { DeleteAllItems(); CListCtrl::OnDestroy(); } //@mfunc Set extended list view style //@rdesc Returns TRUE if style was set successfully BOOL CAutoListCtrl::SetExtendedStyle( DWORD dwStyle) //@parm Style to set // @flag CAutoListCtrl::Gridlines | Display the list with gridlines // @flag CAutoListCtrl::SubitemImages | Display images on subitems as well as primary column // @flag CAutoListCtrl::CheckBoxes | Display checkboxes in the list // @flag CAutoListCtrl::TrackSelect | Selects item without clicking. Pausing over item causes selection. // @flag CAutoListCtrl::HeaderDragDrop | Support drag and drop column headers. // @flag CAutoListCtrl::FullRowSelect | Allow the user to click anywhere in the item to allow selection instead // of only over the text in the first column of the item. // @flag CAutoListCtrl::OneClickActivate | Clicking on an inactive list surfaces and selects an item // @flag CAutoListCtrl::TwoClickActivate | First click activates the list; second click selects. { if(!IsWindow(*this)) return FALSE; ListView_SetExtendedListViewStyle(*this, dwStyle); return TRUE; } int CAutoListCtrl::SetCurSel( int nSelect ) { ASSERT(nSelect != -1); if(GetItemCount() < nSelect + 1) return -1; // First deselect any selected items int iSelCount = (int)GetItemCount(); if(iSelCount) { int * pSelected = new int[iSelCount]; iSelCount = GetSelectedItems(iSelCount, pSelected); for(int iSelItem = 0; iSelItem < iSelCount; iSelItem++) SetItemState(pSelected[iSelItem], 0, LVIS_SELECTED); delete[] pSelected; // Select the new item if(!SetItemState(nSelect, LVIS_SELECTED, LVIS_SELECTED)) { ASSERT(FALSE); } } return 0; } DWORD CAutoListCtrl::GetItemData( int nItem ) const { CAutoListItemData* pData = (CAutoListItemData*)CListCtrl::GetItemData(nItem); if(!pData) return 0; return pData->GetData(); } BOOL CAutoListCtrl::SetItemData(int nItem, DWORD dwData) { CAutoListItemData* pData = (CAutoListItemData*)CListCtrl::GetItemData(nItem); if(!pData) return CListCtrl::SetItemData(nItem, (DWORD) new CAutoListItemData(dwData)); return pData->SetData(dwData); } int CAutoListCtrl::GetCurSel() { int iSel; int iCount = GetSelectedItems(1, &iSel); if(iCount == 0) return -1; return iSel; }