|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: listview.cpp
//
// Contents: Implements Mobsync Custom Listview/TreeView control
//
// Classes: CListView
//
// Notes:
//
// History: 23-Jul-98 rogerg Created.
//
//--------------------------------------------------------------------------
#include "lib.h"
//+---------------------------------------------------------------------------
//
// Member: CListView::CListView, public
//
// Synopsis: Constructor
//
// Arguments: hwnd - hwnd of the listView we are wrapping
// hwndParent - Parent for this HWND.
// idCtrl - ControlID for this item
// msgNotify - messageID to use for sending notifyCommand to the Parent.
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
CListView::CListView(HWND hwnd,HWND hwndParent,int idCtrl,UINT MsgNotify) { Assert(hwnd);
m_hwnd = hwnd; m_hwndParent = hwndParent; // if parent null we just don't send notify messages
m_idCtrl = idCtrl; m_MsgNotify = MsgNotify;
m_pListViewItems = NULL; m_iListViewNodeCount = 0; m_iListViewArraySize = 0; m_iNumColumns = 0; m_iCheckCount = 0; m_dwExStyle = 0;
// Up to caller to setup listView as OwnerData
Assert(GetWindowLongA(m_hwnd,GWL_STYLE) & LVS_OWNERDATA); ListView_SetCallbackMask(m_hwnd, LVIS_STATEIMAGEMASK); // set for checkmark
}
//+---------------------------------------------------------------------------
//
// Member: CListView::~CListView, public
//
// Synopsis: Constructor
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
CListView::~CListView() { DeleteAllItems(); }
//+---------------------------------------------------------------------------
//
// Member: CListView::DeleteAllItems, public
//
// Synopsis: Removes all items from the ListView
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::DeleteAllItems() { BOOL fReturn;
fReturn = ListView_DeleteAllItems(m_hwnd);
if (fReturn) { if (m_iListViewNodeCount) { LPLISTVIEWITEM pListViewCurItem;
Assert(m_pListViewItems);
// loop through the listview items deleting any subitems
pListViewCurItem = m_pListViewItems + m_iListViewNodeCount -1;
while(pListViewCurItem >= m_pListViewItems) { if(pListViewCurItem->pSubItems) { DeleteListViewItemSubItems(pListViewCurItem); }
if (pListViewCurItem->lvItemEx.pszText) { Assert(LVIF_TEXT & pListViewCurItem->lvItemEx.mask); FREE(pListViewCurItem->lvItemEx.pszText); }
if (pListViewCurItem->lvItemEx.pBlob) { Assert(LVIFEX_BLOB & pListViewCurItem->lvItemEx.maskEx); FREE(pListViewCurItem->lvItemEx.pBlob); }
pListViewCurItem--; }
m_iListViewNodeCount = 0; }
// free our item buffer
if (m_pListViewItems) { FREE(m_pListViewItems); m_pListViewItems = NULL; m_iListViewArraySize = 0; }
m_iCheckCount = 0; }
return fReturn; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetItemCount, public
//
// Synopsis: returns total number of items in the listview.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
int CListView::GetItemCount() { return m_iListViewNodeCount; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetSelectedCount, public
//
// Synopsis: returns number of selected items from the listview
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
UINT CListView::GetSelectedCount() { Assert(m_hwnd);
return ListView_GetSelectedCount(m_hwnd); }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetSelectionMark, public
//
// Synopsis: returns index of the selection mark
//
// Arguments:
//
// Returns: itemId of the selection
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
int CListView::GetSelectionMark() { int iNativeListViewId; int iReturnItem = -1; LPLISTVIEWITEM pListViewItem;
Assert(m_hwnd);
iNativeListViewId = ListView_GetSelectionMark(m_hwnd);
if (-1 != iNativeListViewId) { pListViewItem = ListViewItemFromNativeListViewItemId(iNativeListViewId);
if (pListViewItem) { iReturnItem = pListViewItem->lvItemEx.iItem;
Assert(pListViewItem->iNativeListViewItemId == iNativeListViewId); } }
return iReturnItem; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetImageList, public
//
// Synopsis: returns specified imagelist
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
HIMAGELIST CListView::GetImageList(int iImageList) { Assert(m_hwnd); return ListView_GetImageList(m_hwnd,iImageList); }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetImageList, public
//
// Synopsis: sets specified imagelist
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
HIMAGELIST CListView::SetImageList(HIMAGELIST himage,int iImageList) { Assert(m_hwnd); return ListView_SetImageList(m_hwnd,himage,iImageList); }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetExtendedListViewStyle, public
//
// Synopsis: sets the list view style
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
void CListView::SetExtendedListViewStyle(DWORD dwExStyle) { // !!Handle checkboxes ourselves.
// AssertSz(0,"impl extended style with checkboxes");
Assert(m_hwnd); ListView_SetExtendedListViewStyle(m_hwnd,dwExStyle); m_dwExStyle = dwExStyle; }
//+---------------------------------------------------------------------------
//
// Member: CListView::InsertItem, public
//
// Synopsis: wrapper for ListView_InsertItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::InsertItem(LPLVITEMEX pitem) { LPLISTVIEWITEM pNewListViewItem = NULL; LPLISTVIEWITEM pListViewSubItems = NULL; LPWSTR pszText = NULL; LPLVBLOB pBlob = NULL; int iListViewIndex; // location item will be inserted
int iParentIndex = LVI_ROOT; BOOL fInsertNative = FALSE; int iIndent = 0; // indent for the item.
int iNativeInsertAtItemID = -1; // Cannot use insert to add a subitem
// and need at least one column
if (0 != pitem->iSubItem || (0 == m_iNumColumns)) { Assert(0 != m_iNumColumns); Assert(0 == pitem->iSubItem); goto error; }
Assert(0 == (pitem->maskEx & ~(LVIFEX_VALIDFLAGMASK)));
// if a parent is specified check for special flags and
// calc iItem or determine given iItem is invalid
// #define LVI_ROOT -1; // itemID to pass in for ParenItemID for root
// #define LVI_FIRST -0x0FFFE
// #define LVI_LAST -0x0FFFF
// While validatin Determine if item should be immediately added to the listview
// by a) if has parent that is expanded and has an assigned listviewID
// or b) this is a toplevel item.
// this is done when validating the itemID and setting the fInsertNative var
LPLISTVIEWITEM pParentItem; LISTVIEWITEM lviRootNode; // if have a valid parent look it up else use the root
if ((LVIFEX_PARENT & pitem->maskEx) && !(LVI_ROOT == pitem->iParent) ) { pParentItem = ListViewItemFromIndex(pitem->iParent); } else { pParentItem = &lviRootNode;
lviRootNode.lvItemEx.iItem = LVI_ROOT; lviRootNode.lvItemEx.iIndent = -1; lviRootNode.fExpanded = TRUE; }
if (NULL == pParentItem) { Assert(NULL != pParentItem); goto error; }
// found parent so go ahead and set the iIndent
iParentIndex = pParentItem->lvItemEx.iItem; iIndent = pParentItem->lvItemEx.iIndent + 1; fInsertNative = pParentItem->fExpanded;
// if LVI_FIRST for item then parent item + 1
// else we need to find either the next node
// at the same level as the parent or hit the
// end of the list.
if (LVI_FIRST == pitem->iItem) { iListViewIndex = pParentItem->lvItemEx.iItem + 1; } else { int iNextParentiItem = -1; LPLISTVIEWITEM pNextParent = pParentItem + 1; LPLISTVIEWITEM pLastItem = m_pListViewItems + m_iListViewNodeCount -1;
// if parent is the root node then skip since know Nextparent is the
// last node.
if (pParentItem != &lviRootNode) { // calc of last item assumes have at least one node
// if we don't then how can we have a parent?
if (m_iListViewNodeCount < 1) { goto error; }
while (pNextParent <= pLastItem) { if (pNextParent->lvItemEx.iIndent == pParentItem->lvItemEx.iIndent) { iNextParentiItem = pNextParent->lvItemEx.iItem; break; }
++pNextParent; } }
// if out of loop and NexParentItem is still -1 means hit the
// end of list
if (-1 == iNextParentiItem) { if (m_iListViewNodeCount) { iNextParentiItem = pLastItem->lvItemEx.iItem + 1; } else { iNextParentiItem = 0; } }
if (LVI_LAST == pitem->iItem) { iListViewIndex = iNextParentiItem; } else { // if user specified theitem it better fall within a valid range.
if (pitem->iItem > iNextParentiItem || pitem->iItem <= pParentItem->lvItemEx.iItem) { Assert(pitem->iItem <= iNextParentiItem); Assert(pitem->iItem > pParentItem->lvItemEx.iItem);
goto error; }
iListViewIndex = pitem->iItem; }
}
// make sure buffer is big enough
// !!! Warning any pointers items in the ListView Array
// will be invalid after the realloc/alloc.
if (m_iListViewArraySize < (m_iListViewNodeCount + 1)) { int iNewArraySize = m_iListViewNodeCount + 10; LPLISTVIEWITEM pListViewItemsOrig = m_pListViewItems; ULONG cbAlloc = iNewArraySize*sizeof(LISTVIEWITEM);
if (m_pListViewItems) { if (ERROR_SUCCESS != REALLOC((void **)&m_pListViewItems, cbAlloc)) { FREE(m_pListViewItems); m_pListViewItems = NULL; } } else { m_pListViewItems = (LPLISTVIEWITEM) ALLOC(cbAlloc); }
// if couldn't alloc or realloc failed then fail the insert
if (NULL == m_pListViewItems) { m_pListViewItems = pListViewItemsOrig; goto error; }
m_iListViewArraySize = iNewArraySize; } Assert(m_pListViewItems); if (NULL == m_pListViewItems) { goto error; }
// if have subitems make sure we can allocate the subitems before
// moving all the nodes. This is number of columns minus one since
// column offset 0 is stored in main array
pListViewSubItems = NULL; if (m_iNumColumns > 1) { ULONG ulAllocSize = (m_iNumColumns -1)*sizeof(LISTVIEWITEM); int iSubItem; LPLISTVIEWITEM pCurSubItem;
pListViewSubItems = (LPLISTVIEWITEM) ALLOC(ulAllocSize);
if (NULL == pListViewSubItems) { goto error; }
pCurSubItem = pListViewSubItems; iSubItem = 1;
while (iSubItem < m_iNumColumns) { pCurSubItem->lvItemEx.iItem = iListViewIndex; pCurSubItem->lvItemEx.iSubItem = iSubItem;
++iSubItem; ++pCurSubItem; } }
// make sure can allocate text and anything else
// that can faile if need to before move
// everything down so don't have to undo.
if (pitem->mask & LVIF_TEXT) { int cchSize;
if (NULL == pitem->pszText) { pszText = NULL; } else { cchSize = (lstrlen(pitem->pszText) + 1);
pszText = (LPWSTR) ALLOC(cchSize * sizeof(WCHAR));
if (NULL == pszText) { goto error; }
StrCpyN(pszText, pitem->pszText, cchSize); } }
if (pitem->maskEx & LVIFEX_BLOB) { ULONG cbSize;
if (NULL == pitem->pBlob) { Assert(pitem->pBlob); goto error; } cbSize = pitem->pBlob->cbSize; pBlob = (LPLVBLOB) ALLOC(cbSize); if (NULL == pBlob) { goto error; } memcpy(pBlob,pitem->pBlob,cbSize);
}
// !!!Nothing should fail after this line other than possibly
// inserting into the Native ListView in which case
// it will still be in our list but not shown to the User.
// Move existing elements down that item is inserted ahead of.
// if the item is going to be immediately added to the ListView then
pNewListViewItem = m_pListViewItems + iListViewIndex; if (m_iListViewNodeCount) { LPLISTVIEWITEM pListViewMoveItem;
pListViewMoveItem = m_pListViewItems + m_iListViewNodeCount -1; Assert(m_iListViewArraySize > m_iListViewNodeCount);
while (pListViewMoveItem >= pNewListViewItem) // want >= so move node at current item location
{ int iMoveParent;
++(pListViewMoveItem->lvItemEx.iItem); // increment the iItem
// if parent fails within move range increment the ParentId
iMoveParent = pListViewMoveItem->lvItemEx.iParent;
if ( (LVI_ROOT != iMoveParent) && (iMoveParent >= iListViewIndex)) { ++(pListViewMoveItem->lvItemEx.iParent); }
*(pListViewMoveItem + 1) = *(pListViewMoveItem); --pListViewMoveItem; } }
// now insert the item at the specified location
++m_iListViewNodeCount; pNewListViewItem->pSubItems = pListViewSubItems; pNewListViewItem->fExpanded = TRUE; /// Review if want this to be user defined.but we expand children by default
pNewListViewItem->iChildren = 0; pNewListViewItem->iNativeListViewItemId = -1;
// fixup lvItem data
pNewListViewItem->lvItemEx = *pitem; pNewListViewItem->lvItemEx.pszText = pszText; pNewListViewItem->lvItemEx.iItem = iListViewIndex; pNewListViewItem->lvItemEx.iIndent = iIndent; pNewListViewItem->lvItemEx.maskEx |= LVIFEX_PARENT; // always force valid parent
pNewListViewItem->lvItemEx.pBlob = pBlob; pNewListViewItem->lvItemEx.iParent = iParentIndex;
// Review - For now Don't call SetItem so State CheckBox isn't updated.
// Client must call SetItem after the insert to setup the ImageState
// Assert that client isn't passing in a statImage on an Insert.
Assert(!(pNewListViewItem->lvItemEx.mask & LVIF_STATE) || !(pNewListViewItem->lvItemEx.stateMask & LVIS_STATEIMAGEMASK));
pNewListViewItem->lvItemEx.state = 0; pNewListViewItem->lvItemEx.stateMask = 0;
// if have a parent other than the root incrment its children count
// !! Note have to find again in case a realloc happened
if (iParentIndex != LVI_ROOT) { pParentItem = ListViewItemFromIndex(iParentIndex);
Assert(pParentItem); if (pParentItem) { ++(pParentItem->iChildren); } }
if (fInsertNative) { // walk back and add 1 to
// first item we come to that already is in the listview. if none assigned
// iNativeInsertAtItemID should be zero.
iNativeInsertAtItemID = 0; LPLISTVIEWITEM pListViewPrevItem;
pListViewPrevItem = pNewListViewItem -1; while (pListViewPrevItem >= m_pListViewItems) { if (-1 != pListViewPrevItem->iNativeListViewItemId) { iNativeInsertAtItemID = pListViewPrevItem->iNativeListViewItemId + 1; break; }
--pListViewPrevItem; } } else { iNativeInsertAtItemID = -1; }
if (-1 != iNativeInsertAtItemID) { LV_ITEM lvi = { 0 }; LPLISTVIEWITEM pListViewMoveItem; LPLISTVIEWITEM pLastItem;
Assert(fInsertNative);
lvi.iItem = iNativeInsertAtItemID; pNewListViewItem->iNativeListViewItemId = ListView_InsertItem(m_hwnd,&lvi); Assert(iNativeInsertAtItemID == pNewListViewItem->iNativeListViewItemId); if (-1 != pNewListViewItem->iNativeListViewItemId) { // fix up NativeIds of items below
pLastItem = m_pListViewItems + m_iListViewNodeCount - 1; pListViewMoveItem = pNewListViewItem + 1;
while (pListViewMoveItem <= pLastItem) { if (-1 != pListViewMoveItem->iNativeListViewItemId) { ++(pListViewMoveItem->iNativeListViewItemId); }
++pListViewMoveItem; } }
}
// after calling native listview fix up the state vars in local item to
// not include the low byte
pNewListViewItem->lvItemEx.state &= ~0xFF; pNewListViewItem->lvItemEx.stateMask &= ~0xFF;
return iListViewIndex; // return new index even if fail to add to native listview
error:
if (pListViewSubItems) { FREE(pListViewSubItems); }
if (pszText) { FREE(pszText); }
if (pBlob) { FREE(pBlob); }
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CListView::DeleteItem, public
//
// Synopsis: Deletes the specified lvItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::DeleteItem(int iItem) { LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(iItem); LPLISTVIEWITEM pListViewCurItem; LPLISTVIEWITEM pListViewLastItem; int iNativeListViewId; int iParent;
if (NULL == pListViewItem || (m_iListViewNodeCount < 1)) { Assert(pListViewItem); Assert(m_iListViewNodeCount > 0); // should be at least one item.
return FALSE; }
// delete the item data and then move all items below this one up
// in the array. if the Item is in the ListView and native delete succeeded
// decrement their nativeID count.
// !REMEMBER TO DECREMENT THE parents iChildren count if have a parent and
// total count of number of items in the list view.
if (0 != pListViewItem->iChildren) { Assert(0 == pListViewItem->iChildren); // don't support delete of parent nodes.
return FALSE; }
iNativeListViewId = pListViewItem->iNativeListViewItemId; iParent = pListViewItem->lvItemEx.iParent; // update toplevel vars and item info by calling
// setitem to uncheck and clear text, and blob so
LVITEMEX pitem;
pitem.iItem = iItem; pitem.iSubItem = 0; pitem.mask = LVIF_TEXT; pitem.maskEx = LVIFEX_BLOB;
pitem.pszText = NULL; pitem.pBlob = NULL; // only need to set the state if have checkboxes
if (m_dwExStyle & LVS_EX_CHECKBOXES) { pitem.mask |= LVIF_STATE; pitem.state = LVIS_STATEIMAGEMASK_UNCHECK; pitem.stateMask = LVIS_STATEIMAGEMASK; }
SetItem(&pitem); // update parent to tell it it has one less item.
if (LVI_ROOT != iParent) { LPLISTVIEWITEM pListViewItemParent;
if (pListViewItemParent = ListViewItemFromIndex(iParent)) { --(pListViewItemParent->iChildren); Assert(pListViewItemParent->iChildren >= 0); }
Assert(pListViewItemParent); }
// set the current item to the end of the list
Assert(m_iListViewNodeCount >= 1); // if no nodes should have already bailed.
pListViewLastItem = m_pListViewItems + m_iListViewNodeCount - 1;
// delete the SubItems if any associated with the ListView.
DeleteListViewItemSubItems(pListViewItem); Assert(NULL == pListViewItem->lvItemEx.pszText); Assert(NULL == pListViewItem->lvItemEx.pBlob);
// delete the item from the native listView if fails will just have
// a blank item at the bottom of the native listview.
if (-1 != iNativeListViewId) { ListView_DeleteItem(m_hwnd,iNativeListViewId); }
// decrement the toplevel nodecount
--m_iListViewNodeCount;
pListViewCurItem = pListViewItem;
// move items remaining in the ListView up updateing iNativeListViewId
// if appropriate.
while (pListViewCurItem < pListViewLastItem) { *(pListViewCurItem) = *(pListViewCurItem + 1); if ( (-1 != iNativeListViewId) && (-1 != pListViewCurItem->iNativeListViewItemId)) { --pListViewCurItem->iNativeListViewItemId; }
--(pListViewCurItem->lvItemEx.iItem); // decrement it iItem
// if items parentID falls within pListViewItem and this item
// range need to update our iParent.
// parent should nevert be == iItem since don't allow nodes
// with children to be deleted but check <= anyways
if (LVI_ROOT != pListViewCurItem->lvItemEx.iParent) { if (iItem <= pListViewCurItem->lvItemEx.iParent) { --(pListViewCurItem->lvItemEx.iParent); } }
++pListViewCurItem; }
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Member: CListView::DeleteChildren, public
//
// Synopsis: Deletes all child nodes associated with the item.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::DeleteChildren(int iItem) { LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(iItem); LPLISTVIEWITEM pListViewCurItem; LPLISTVIEWITEM pLastListViewItem; int iNumChildren;
if (!pListViewItem || m_iListViewNodeCount < 1) { Assert(pListViewItem); Assert(m_iListViewNodeCount >= 1); // should at least have one node.
return FALSE; } iNumChildren = pListViewItem->iChildren; pLastListViewItem = m_pListViewItems + m_iListViewNodeCount - 1;
if (0 > iNumChildren) { Assert(0 <= iNumChildren); // this count should never go negative.
return FALSE; }
// if no children just return;
if (0 == iNumChildren) { return TRUE; } // verify all children don't have any children of there own. if they
// do we don't support this. Also verify that don't run off
// end of the list in which case we fail too.
pListViewCurItem = pListViewItem + iNumChildren;
if (pListViewCurItem > pLastListViewItem) { AssertSz(0,"Children run off end of ListView"); return FALSE; }
while (pListViewCurItem > pListViewItem) { if (pListViewCurItem->iChildren > 0) { AssertSz(0,"Trying to DeleteChildren when Children have Children"); return FALSE; }
--pListViewCurItem; }
// all items verified, just loop through deleting the items starting at the bottom.
pListViewCurItem = pListViewItem + iNumChildren;
while (pListViewCurItem > pListViewItem) { DeleteItem(pListViewCurItem->lvItemEx.iItem); // if any fail delete what we can.
--pListViewCurItem; }
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetItem, public
//
// Synopsis: wrapper for ListView_SetItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::SetItem(LPLVITEMEX pitem) { int iNativeListViewItemId; LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(pitem->iItem,pitem->iSubItem,&iNativeListViewItemId); LPLVITEMEX plvItemEx; BOOL fCheckCountChanged = FALSE; LVITEMSTATE fNewCheckCountState; LPWSTR pszNewText = NULL; LPLVBLOB pNewBlob = NULL;
if (NULL == pListViewItem) { Assert(pListViewItem); return FALSE; }
Assert(pListViewItem->lvItemEx.iSubItem == pitem->iSubItem); Assert(pListViewItem->lvItemEx.iSubItem > 0 || pListViewItem->iNativeListViewItemId == iNativeListViewItemId);
plvItemEx = &(pListViewItem->lvItemEx);
// allocate new text
if (LVIF_TEXT & pitem->mask) { int cchSize;
if (NULL == pitem->pszText) { pszNewText = NULL; } else { cchSize = (lstrlen(pitem->pszText) + 1); pszNewText = (LPWSTR) ALLOC(cchSize *sizeof(WCHAR));
if (NULL == pszNewText) { goto error; }
StrCpyN(pszNewText, pitem->pszText, cchSize);
}
}
// allocate new blob
if (LVIFEX_BLOB & pitem->maskEx) {
if (NULL == pitem->pBlob) { pNewBlob = NULL; } else { pNewBlob = (LPLVBLOB) ALLOC(pitem->pBlob->cbSize);
if (NULL == pNewBlob) { goto error; }
memcpy(pNewBlob,pitem->pBlob,pitem->pBlob->cbSize);
}
}
// now that can't fail update the text and blob field appropriately
if (LVIF_TEXT & pitem->mask) { if (plvItemEx->pszText) { FREE(plvItemEx->pszText); }
plvItemEx->pszText = pszNewText; plvItemEx->mask |= LVIF_TEXT;
pszNewText = NULL; }
if (LVIFEX_BLOB & pitem->maskEx) { if (plvItemEx->pBlob) { FREE(plvItemEx->pBlob); }
plvItemEx->pBlob = pNewBlob; plvItemEx->mask |= LVIFEX_BLOB;
pNewBlob = NULL; }
if (LVIF_IMAGE & pitem->mask) { plvItemEx->mask |= LVIF_IMAGE; plvItemEx->iImage = pitem->iImage; }
if (LVIF_PARAM & pitem->mask) { plvItemEx->mask |= LVIF_PARAM; plvItemEx->lParam = pitem->lParam; }
// update the item state.
if (LVIF_STATE & pitem->mask) { plvItemEx->mask |= LVIF_STATE;
// only care about #define LVIS_OVERLAYMASK, LVIS_STATEIMAGEMASK
if (pitem->stateMask & LVIS_OVERLAYMASK) { plvItemEx->stateMask |= LVIS_OVERLAYMASK; plvItemEx->state = (pitem->state & LVIS_OVERLAYMASK ) + (plvItemEx->state & ~LVIS_OVERLAYMASK); }
if (pitem->stateMask & LVIS_STATEIMAGEMASK) { // update the m_iCheckCount (indeterminate doesn't contribute.
if ( (plvItemEx->iSubItem == 0) && ( (pitem->state & LVIS_STATEIMAGEMASK) != (plvItemEx->state & LVIS_STATEIMAGEMASK))) {
// don't set fCheckCountChange unless it actually did.
if ( (pListViewItem->lvItemEx.state & LVIS_STATEIMAGEMASK) == LVIS_STATEIMAGEMASK_CHECK) { fCheckCountChanged = TRUE; fNewCheckCountState = LVITEMEXSTATE_UNCHECKED; --m_iCheckCount; } if ( (pitem->state & LVIS_STATEIMAGEMASK) == LVIS_STATEIMAGEMASK_CHECK) { fCheckCountChanged = TRUE; fNewCheckCountState = LVITEMEXSTATE_CHECKED; ++m_iCheckCount; }
Assert(m_iCheckCount >= 0); Assert(m_iCheckCount <= m_iListViewNodeCount); }
plvItemEx->stateMask |= LVIS_STATEIMAGEMASK; plvItemEx->state = (pitem->state & LVIS_STATEIMAGEMASK) + (plvItemEx->state & ~LVIS_STATEIMAGEMASK);
} }
// if the check count changed and we have checkboxes send the notification
// if item state has changed send the count notification
if (fCheckCountChanged && m_hwndParent && (m_dwExStyle & LVS_EX_CHECKBOXES)) { NMLISTVIEWEXITEMCHECKCOUNT lvCheckCount; lvCheckCount.hdr.hwndFrom = m_hwnd; lvCheckCount.hdr.idFrom = m_idCtrl; lvCheckCount.hdr.code = LVNEX_ITEMCHECKCOUNT; lvCheckCount.iCheckCount = m_iCheckCount;
lvCheckCount.iItemId = pitem->iItem; lvCheckCount.dwItemState = fNewCheckCountState; // new state of the item whose checkcount has changed.
SendMessage(m_hwndParent,m_MsgNotify,m_idCtrl,(LPARAM) &lvCheckCount); }
// if item is in the native list view, redraw item to reflect new state
// bug bug, doesn't handle subitems
if (-1 != iNativeListViewItemId) { // if state changed pass it along for focus
if ((LVIF_STATE & pitem->mask) && (0 == pitem->iSubItem)) { int stateMask = pitem->stateMask & 0xff; if (stateMask) { ListView_SetItemState(m_hwnd,iNativeListViewItemId,pitem->state,stateMask); } }
ListView_RedrawItems(m_hwnd,iNativeListViewItemId,iNativeListViewItemId); }
return TRUE;
error:
if (pszNewText) { FREE(pszNewText); }
if (pNewBlob) { FREE(pNewBlob); }
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CListView::SetItemState, public
//
// Synopsis: wrapper for ListView_SetItemState
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::SetItemState(int iItem,UINT state,UINT mask) { LVITEMEX lvitemEx;
lvitemEx.iItem = iItem; lvitemEx.iSubItem = 0; lvitemEx.mask = LVIF_STATE ; lvitemEx.state = state; lvitemEx.stateMask = mask; lvitemEx.maskEx = 0;
return SetItem(&lvitemEx); }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetItemlParam, public
//
// Synopsis: wrapper for setting the lParam
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::SetItemlParam(int iItem,LPARAM lParam) { LVITEMEX lvitemEx;
lvitemEx.iItem = iItem; lvitemEx.iSubItem = 0; lvitemEx.mask = LVIF_PARAM ; lvitemEx.lParam = lParam; lvitemEx.maskEx = 0;
return SetItem(&lvitemEx); }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetItemText, public
//
// Synopsis: wrapper for setting the item text.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::SetItemText(int iItem,int iSubItem,LPWSTR pszText) { LVITEMEX lvitemEx;
lvitemEx.iItem = iItem; lvitemEx.iSubItem = iSubItem; lvitemEx.mask = LVIF_TEXT; lvitemEx.pszText = pszText; lvitemEx.maskEx = 0;
return SetItem(&lvitemEx); }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetItem, public
//
// Synopsis: wrapper for ListView_GetItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::GetItem(LPLVITEMEX pitem) { int iNativeListViewItemId; LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(pitem->iItem,pitem->iSubItem,&iNativeListViewItemId);
if (NULL == pListViewItem) { Assert(pListViewItem); return FALSE; }
// add text first. Since it is the only item
if (LVIF_TEXT & pitem->mask) { if (!(pListViewItem->lvItemEx.pszText) || (0 == pitem->cchTextMax) || !(pListViewItem->lvItemEx.mask & LVIF_TEXT) ) { pitem->pszText = NULL; } else { int cchListTextSize = lstrlen(pListViewItem->lvItemEx.pszText);
StrCpyN(pitem->pszText,pListViewItem->lvItemEx.pszText,pitem->cchTextMax); } }
if (LVIF_IMAGE & pitem->mask) { pitem->iImage = pListViewItem->lvItemEx.iImage; }
if (LVIF_PARAM & pitem->mask) { pitem->lParam = pListViewItem->lvItemEx.lParam; }
// update the item state.
if (LVIF_STATE & pitem->mask) { pitem->state = pListViewItem->lvItemEx.state; }
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Member: CListView::GetItemText, public
//
// Synopsis: wrapper for ListView_GetItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::GetItemText(int iItem,int iSubItem,LPWSTR pszText,int cchTextMax) { LVITEMEX lvitem;
lvitem.mask = LVIF_TEXT; lvitem.maskEx = 0;
lvitem.iItem = iItem; lvitem.iSubItem = iSubItem; lvitem.pszText = pszText; lvitem.cchTextMax = cchTextMax; return GetItem(&lvitem); }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetItemlParam, public
//
// Synopsis: wrapper for gettting the lparam
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::GetItemlParam(int iItem,LPARAM *plParam) { LVITEMEX lvitem; BOOL fReturn;
lvitem.mask = LVIF_PARAM; lvitem.maskEx = 0;
lvitem.iItem = iItem; lvitem.iSubItem = 0;
if (fReturn = GetItem(&lvitem)) { *plParam = lvitem.lParam; }
return fReturn; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetHwnd, public
//
// Synopsis: return Hwnd of the ListView
//
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 07-Sep-98 rogerg Created.
//
//----------------------------------------------------------------------------
HWND CListView::GetHwnd() { return m_hwnd; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetParent, public
//
// Synopsis: return Hwnd of the ListViews Parent
//
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 07-Sep-98 rogerg Created.
//
//----------------------------------------------------------------------------
HWND CListView::GetParent() { return m_hwndParent; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetCheckState, public
//
// Synopsis: wrapper for ListView_GetCheckState
//
// return state from LVITEMEXSTATE enum
// !!!return -1 if not item match to have same behavior as ListView
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
int CListView::GetCheckState(int iItem) { LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(iItem); UINT state; if (NULL == pListViewItem) { return -1; // return -1 same as for native listbox
} // check state is actually define -1 than the image.
// Don't know why. Just what the native listview does.
// change whant supply our own image that will map exactly
state = ((pListViewItem->lvItemEx.state & LVIS_STATEIMAGEMASK) >> 12) -1;
#ifdef _DEBUG
if (-1 != pListViewItem->iNativeListViewItemId) { UINT lvState = ListView_GetCheckState(m_hwnd,pListViewItem->iNativeListViewItemId);
Assert(state == lvState); }
#endif // _DEBUG
// if this is a toplevel item then okay for state to be
// negative -1. Should change this when go indeterminate.
// Review - maybe just make -1 indeterinate state.
if (-1 == state && 0 == pListViewItem->lvItemEx.iIndent) { state = LVITEMEXSTATE_INDETERMINATE; }
Assert(state <= LVITEMEXSTATE_INDETERMINATE); return state; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetCheckedItemsCount, public
//
// Synopsis: returns the number of checked items in the list.
//
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
int CListView::GetCheckedItemsCount() { return m_iCheckCount; }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetColumn, public
//
// Synopsis: wrapper for ListView_SetColumn
//
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::SetColumn(int iCol,LV_COLUMN * pColumn) { Assert(m_hwnd); return ListView_SetColumn(m_hwnd,iCol,pColumn); }
//+---------------------------------------------------------------------------
//
// Member: CListView::InsertColumn, public
//
// Synopsis: wrapper for ListView_InsertColumn
//
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
int CListView::InsertColumn(int iCol,LV_COLUMN * pColumn) { int iReturn;
Assert(m_hwnd);
iReturn = ListView_InsertColumn(m_hwnd,iCol,pColumn);
if (-1 != iReturn) { m_iNumColumns++;
// need to realloc any existing listviewItems with subitems and move
// the column appropriate
//currently only support setting up columns before adding any items
Assert(0 == m_iListViewNodeCount);
}
return iReturn; }
//+---------------------------------------------------------------------------
//
// Member: CListView::SetColumnWidth, public
//
// Synopsis: wrapper for ListView_SetColumnWidth
//
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::SetColumnWidth(int iCol,int cx) {
Assert(m_hwnd);
return ListView_SetColumnWidth(m_hwnd,iCol,cx);
}
//+---------------------------------------------------------------------------
//
// Member: CListView::Expand, public
//
// Synopsis: expands all children of specified Item
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::Expand(int iItemId) { LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(iItemId);
if (!pListViewItem) { return FALSE; }
return ExpandCollapse(pListViewItem,TRUE); }
//+---------------------------------------------------------------------------
//
// Member: CListView::Collapse, public
//
// Synopsis: collapses all children of specified Item
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::Collapse(int iItemId) { LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(iItemId);
if (!pListViewItem) { return FALSE; }
return ExpandCollapse(pListViewItem,FALSE); }
//+---------------------------------------------------------------------------
//
// Member: CListView::FindItemFromBlob, public
//
// Synopsis: returns first item in list that matches blob
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
int CListView::FindItemFromBlob(LPLVBLOB pBlob) { LPLISTVIEWITEM pListViewItem;
// if not items just return
if (m_iListViewNodeCount < 1) { return -1; }
pListViewItem = m_pListViewItems + m_iListViewNodeCount -1;
while(pListViewItem >= m_pListViewItems) { if (IsEqualBlob(pBlob,pListViewItem->lvItemEx.pBlob)) { return pListViewItem->lvItemEx.iItem; }
--pListViewItem; }
return -1; }
//+---------------------------------------------------------------------------
//
// Member: CListView::GetItemBlob, public
//
// Synopsis: finds blob is any associated with an
// item and then fill in mem pointed
// to by pBlob if cbSize in BlobStruc is >
// specified cbBlockSize NULL is returned
//
// Arguments:
//
// Returns: NULL on failure
// on success a pointer to the passed in buffer
// strictly for convenience to the caller.
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
LPLVBLOB CListView::GetItemBlob(int ItemId,LPLVBLOB pBlob,ULONG cbBlobSize) { LPLISTVIEWITEM pListViewItem = ListViewItemFromIndex(ItemId); LPLVBLOB pItemBlob;
if (!pListViewItem || (NULL == pListViewItem->lvItemEx.pBlob) || (NULL == pBlob)) { Assert(pListViewItem); Assert(pBlob); return NULL; }
pItemBlob = pListViewItem->lvItemEx.pBlob;
// make sure out buffer is big enough
if (cbBlobSize < pItemBlob->cbSize) { Assert(cbBlobSize >= pItemBlob->cbSize); return NULL; }
memcpy(pBlob,pItemBlob,pItemBlob->cbSize);
return pBlob;
}
//+---------------------------------------------------------------------------
//
// Member: CListView::IsEqualBlob, private
//
// Synopsis: compares two blobs. Valid to pass in NULL put two NULLS is
// not a match.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::IsEqualBlob(LPLVBLOB pBlob1,LPLVBLOB pBlob2) { if (NULL == pBlob1 || NULL == pBlob2) { return FALSE; }
// compare sizes
if (pBlob1->cbSize != pBlob2->cbSize) { return FALSE; }
if (0 != memcmp(pBlob1,pBlob2,pBlob2->cbSize)) { return FALSE; }
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CListView::OnNotify, public
//
// Synopsis: Called by client whenever a native listview
// notifiication is sent. We turn around, package
// it up and send it as our notification message.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
LRESULT CListView::OnNotify(LPNMHDR pnmv) { LPNMLISTVIEW pnlv = (LPNMLISTVIEW) pnmv; NMLISTVIEWEX nmvEx; LPLISTVIEWITEM pListViewItem;
if ((NULL == pnmv) || ( ((int) pnmv->idFrom) != m_idCtrl) || (NULL == m_hwndParent)) { Assert(pnmv); Assert( ((int) pnmv->idFrom) == m_idCtrl); Assert(m_hwndParent); return 0; }
// take care of notifies we handle ourselves.
switch(pnmv->code) { case LVN_GETDISPINFOW: { OnGetDisplayInfo(pnmv->code,(LV_DISPINFO *) pnmv); return 0; break; } case NM_DBLCLK: case NM_CLICK: { LV_HITTESTINFO lvhti; RECT Rect;
lvhti.pt = pnlv->ptAction;
// Have the ListView tell us what element this was on
if (-1 != ListView_HitTest(m_hwnd, &lvhti)) {
// if flags are onitem change to either label or state depending
// on the click positiion
if (LVHT_ONITEM == lvhti.flags) { lvhti.flags = LVHT_ONITEMLABEL;
if (ListView_GetSubItemRect(m_hwnd,pnlv->iItem,0,LVIR_ICON,&Rect)) { if (lvhti.pt.x < Rect.left) { lvhti.flags = LVHT_ONITEMSTATEICON; } else if (lvhti.pt.x <= Rect.right) { // this doesn't ever get hit since icon is between label and state
// but its here for completeness.
lvhti.flags = LVHT_ONITEMICON; }
}
}
if (OnHandleUIEvent(pnmv->code,lvhti.flags,0,pnlv->iItem)) { return 0; // don't pass on clicks we process
} } break; } case LVN_KEYDOWN: { LV_KEYDOWN* pnkd = (LV_KEYDOWN*) pnmv; int iItem; if (-1 != (iItem = ListView_GetSelectionMark(m_hwnd))) { if(OnHandleUIEvent(pnmv->code,0,pnkd->wVKey,iItem)) { return 0; } } break; } default: break; }
Assert(LVNEX_ITEMCHANGED == LVN_ITEMCHANGED); Assert(LVNEX_DBLCLK == NM_DBLCLK); Assert(LVNEX_CLICK == NM_CLICK);
// only pass along notifications we know how to handle
if (LVN_ITEMCHANGED != pnmv->code && NM_DBLCLK != pnmv->code && NM_CLICK != pnmv->code) { return 0; }
// listview can send a iItem of -1 for example when
// a double-click or click occurs in empty space.
// if get a -1 just pass through
if (-1 == pnlv->iItem) { memcpy(&(nmvEx.nmListView),pnmv,sizeof(nmvEx.nmListView)); nmvEx.iParent = -1; nmvEx.pBlob = NULL;
} else { pListViewItem = ListViewItemFromNativeListViewItemId(pnlv->iItem); if (NULL == pListViewItem) { // if couldn't find itme
Assert(pListViewItem); return 0; }
// assumes only pass along notifications of
// type LPNMLISTVIEW
// fix up the notify structure
memcpy(&(nmvEx.nmListView),pnmv,sizeof(nmvEx.nmListView)); nmvEx.nmListView.iItem = pListViewItem->lvItemEx.iItem; // make item point to our internal id
nmvEx.iParent = pListViewItem->lvItemEx.iParent; nmvEx.pBlob = pListViewItem->lvItemEx.pBlob; if (LVIF_STATE & pnlv->uChanged ) { // update our internal itemState for the item.
// Note don't care about lower state bits
if ( (pnlv->uNewState ^ pnlv->uOldState) & LVIS_STATEIMAGEMASK) { pListViewItem->lvItemEx.state = (pnlv->uNewState & LVIS_STATEIMAGEMASK) + (pListViewItem->lvItemEx.state & ~LVIS_STATEIMAGEMASK); }
if ( (pnlv->uNewState ^ pnlv->uOldState) & LVIS_OVERLAYMASK) { pListViewItem->lvItemEx.state = (pnlv->uNewState & LVIS_OVERLAYMASK) + (pListViewItem->lvItemEx.state & ~LVIS_OVERLAYMASK); }
} }
// send off the message
return SendMessage(m_hwndParent,m_MsgNotify,m_idCtrl,(LPARAM) &nmvEx);
}
//+---------------------------------------------------------------------------
//
// Member: CListView::ListViewItemFromNativeListViewItemId, private
//
// Synopsis: Given a native listview control itemId finds our internal ListViewItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
LPLISTVIEWITEM CListView::ListViewItemFromNativeListViewItemId(int iNativeListViewItemId) { return ListViewItemFromNativeListViewItemId(iNativeListViewItemId,0); }
//+---------------------------------------------------------------------------
//
// Member: CListView::ListViewItemFromNativeListViewItemId, private
//
// Synopsis: Given a native listview control itemId finds our internal ListViewItem
//
// Arguments: iNativeListViewItemId - ItemId of the ListViewItem
// iNativeListViewSubItemId - SubItemID of the ListViewItem
// piNativeListViewItemId - [out] on succes iteId in nativelistview.
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
LPLISTVIEWITEM CListView::ListViewItemFromNativeListViewItemId(int iNativeListViewItemId, int iSubItem) { LPLISTVIEWITEM pListViewItem;
if (-1 == iNativeListViewItemId) { return NULL; }
if (NULL == m_pListViewItems || m_iListViewNodeCount < 1 || (iSubItem > m_iNumColumns - 1)) { Assert(NULL != m_pListViewItems); Assert(m_iListViewNodeCount >= 1); Assert(iSubItem <= (m_iNumColumns - 1)); return NULL; }
pListViewItem = m_pListViewItems + m_iListViewNodeCount -1;
while(pListViewItem >= m_pListViewItems) { if (iNativeListViewItemId == pListViewItem->iNativeListViewItemId) { break; }
--pListViewItem; }
if (pListViewItem < m_pListViewItems) { return NULL; }
// if subItem is zero then we just return this listviewItem, else
// need to walk forward the subItem array.
if (0 == iSubItem) { return pListViewItem; }
Assert(m_iNumColumns > 1); // should have already caught about but double-check
pListViewItem = pListViewItem->pSubItems + iSubItem -1;
return pListViewItem; }
//+---------------------------------------------------------------------------
//
// Member: CListView::ListViewItemFromIndex, private
//
// Synopsis: Finds internal listviewItem from ItemID we gave to client.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
LPLISTVIEWITEM CListView::ListViewItemFromIndex(int iItemID) { return ListViewItemFromIndex(iItemID,0,NULL); }
//+---------------------------------------------------------------------------
//
// Member: CListView::ListViewItemFromIndex, private
//
// Synopsis: Finds internal listviewItem from ItemID we gave to client.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
LPLISTVIEWITEM CListView::ListViewItemFromIndex(int iItemID,int iSubitem,int *piNativeListViewItemId) { LPLISTVIEWITEM pListViewItem;
// if item isn't valid return NULL
if (iItemID < 0 || iItemID >= m_iListViewNodeCount || (iSubitem > m_iNumColumns - 1)) { Assert(iItemID >= 0); Assert(iSubitem <= m_iNumColumns - 1);
// Assert(iItemID < m_iListViewNodeCount); choice dlg Calls GetCheckState until hits -1.
return NULL; }
pListViewItem = m_pListViewItems + iItemID;
if (piNativeListViewItemId) { *piNativeListViewItemId = pListViewItem->iNativeListViewItemId; }
if (0 == iSubitem) { return pListViewItem; }
pListViewItem = pListViewItem->pSubItems + iSubitem -1;
return pListViewItem; }
//+---------------------------------------------------------------------------
//
// Member: CListView::DeleteListViewItemSubItems, private
//
// Synopsis: Helper function to delete subItems associated with a ListViewItem
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 04-Aug-98 rogerg Created.
//
//----------------------------------------------------------------------------
void CListView::DeleteListViewItemSubItems(LPLISTVIEWITEM pListItem) { LPLISTVIEWITEM pListViewSubItem;
// if not subItems or number of columns isn't at least 2 bail.
if ((NULL == pListItem->pSubItems) || (m_iNumColumns < 2)) { Assert(NULL == pListItem->pSubItems && m_iNumColumns < 2); // should always match.
return; }
pListViewSubItem = pListItem->pSubItems + m_iNumColumns -2; // -2 ; covers first subItem and column of main item
// free any text associated with the subItems
Assert(m_iNumColumns > 1); Assert(pListViewSubItem >= pListItem->pSubItems);
while (pListViewSubItem >= pListItem->pSubItems) { if (pListViewSubItem->lvItemEx.pszText) { Assert(LVIF_TEXT & pListViewSubItem->lvItemEx.mask); FREE(pListViewSubItem->lvItemEx.pszText); }
--pListViewSubItem; }
FREE(pListItem->pSubItems); pListItem->pSubItems = NULL; }
//+---------------------------------------------------------------------------
//
// Member: CListView::ExpandCollapse, private
//
// Synopsis: Expands or collapses children of given node.
//
// Arguments:
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::ExpandCollapse(LPLISTVIEWITEM pListViewItem,BOOL fExpand) { LPLISTVIEWITEM pCurListViewItem = pListViewItem + 1; LPLISTVIEWITEM pLastListViewItem = m_pListViewItems + m_iListViewNodeCount -1; int iIndent = pListViewItem->lvItemEx.iIndent; int iInsertDeleteCount = 0; LV_ITEM lvi = { 0 };
Assert(pListViewItem); Assert(m_iListViewNodeCount); // if specified node isn't in the list view fail
if (-1 == pListViewItem->iNativeListViewItemId) { Assert(-1 != pListViewItem->iNativeListViewItemId); return FALSE; }
lvi.iItem = pListViewItem->iNativeListViewItemId + 1; while (pCurListViewItem <= pLastListViewItem && pCurListViewItem->lvItemEx.iIndent > iIndent) {
if (fExpand) { if ( (-1 == pCurListViewItem->iNativeListViewItemId) && (pCurListViewItem->lvItemEx.iIndent == iIndent + 1)) // only expand next level deep
{ pCurListViewItem->iNativeListViewItemId = ListView_InsertItem(m_hwnd,&lvi); Assert(pCurListViewItem->iNativeListViewItemId == lvi.iItem); if (-1 != pCurListViewItem->iNativeListViewItemId) { ++lvi.iItem; ++iInsertDeleteCount; } } } else { if (-1 != pCurListViewItem->iNativeListViewItemId) { pCurListViewItem->fExpanded = FALSE; if (ListView_DeleteItem(m_hwnd,lvi.iItem)) { pCurListViewItem->iNativeListViewItemId = -1; --iInsertDeleteCount; } } }
++pCurListViewItem; }
// fixup nativeIds of any remaining items in the list
while (pCurListViewItem <= pLastListViewItem) { if (-1 != pCurListViewItem->iNativeListViewItemId) { pCurListViewItem->iNativeListViewItemId += iInsertDeleteCount; Assert(pCurListViewItem->iNativeListViewItemId >= 0); Assert(pCurListViewItem->iNativeListViewItemId < m_iListViewNodeCount); } ++pCurListViewItem; }
pListViewItem->fExpanded = fExpand;
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CListView::OnGetDisplayInfo, private
//
// Synopsis: Handles Display info notification.
//
// Arguments: code - code from notification header either
// LVN_GETDISPINFOW, LVN_GETDISPINFOA. need this
// so know how to handle Text.
//
// Returns:
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
void CListView::OnGetDisplayInfo(UINT code,LV_DISPINFO *plvdi) { LPLISTVIEWITEM pListViewItem = ListViewItemFromNativeListViewItemId(plvdi->item.iItem, plvdi->item.iSubItem);
if (NULL == pListViewItem) return;
// verify subitem iound matches item we asked for.
Assert(pListViewItem->lvItemEx.iSubItem == plvdi->item.iSubItem);
// The ListView needs text for this row
if (plvdi->item.mask & LVIF_TEXT) {
if (pListViewItem->lvItemEx.pszText) { if (LVN_GETDISPINFOW == code) { StrCpyN(plvdi->item.pszText,pListViewItem->lvItemEx.pszText,plvdi->item.cchTextMax); } } }
// The ListView needs an image
if (plvdi->item.mask & LVIF_IMAGE) { // plvdi->item.iItem, plvdi->item.iSubItem, &(plvdi->item.iImage));
plvdi->item.iImage = pListViewItem->lvItemEx.iImage; }
// The ListView needs the indent level
if (plvdi->item.mask & LVIF_INDENT) { // if (m_fThreadMessages)
// m_pTable->GetIndentLevel(plvdi->item.iItem, (LPDWORD) &(plvdi->item.iIndent));
// else
// for no checks on top-level set image state to empty pict and
// indent to -1. Need additional State Logic if want to
// choose if toplevel checks are shown.
plvdi->item.iIndent = pListViewItem->lvItemEx.iIndent;
if ( (m_dwExStyle & LVS_EX_CHECKBOXES) && (0 == plvdi->item.iIndent) ) { plvdi->item.iIndent = -1; } }
// The ListView needs the state image
if (plvdi->item.mask & LVIF_STATE) { //nt iIcon = 0;
// _GetColumnStateImage(plvdi->item.iItem, plvdi->item.iSubItem, &iIcon);
plvdi->item.state = pListViewItem->lvItemEx.state & LVIS_STATEIMAGEMASK; }
}
//+---------------------------------------------------------------------------
//
// Member: CListView::OnHandleUIEvent private
//
// Synopsis: sent when a click or double-click,keyboard, is sent to
// the listview
//
// Arguments: code - indicates why this function was called
// Support NM_DBLCLK, NM_CLICK, LVN_KEYDOWN:
//
// flags - flags from hitTest. Only valid for DBCLK and CLICK
// flag = LVHT_ONITEMSTATEICON | LVHT_ONITEMICON) |LVHT_ONITEMLABEL
// wVKey - Virtual key code of key presses. Ony valide cfor LVN_KEYDOWN
// iItemNative - ItemID in NativeListView
//
//
// Returns: TRUE - if handled event and notification shouldn't be passed on.
//
// Modifies:
//
// History: 23-Jul-98 rogerg Created.
//
//----------------------------------------------------------------------------
BOOL CListView::OnHandleUIEvent(UINT code,UINT flags,WORD wVKey,int iItemNative) { LPLISTVIEWITEM pListViewItem = ListViewItemFromNativeListViewItemId(iItemNative); int iStateMask; BOOL fItemHasCheckBoxes; // for now this is hardcoded depending on the indent. Need to change this.
BOOL fToggleCheckBox = FALSE; BOOL fExpand = FALSE; BOOL fCollapse = FALSE; BOOL fReturn = FALSE;
if (NULL == pListViewItem) { return TRUE; // no need passing this on
}
fItemHasCheckBoxes = pListViewItem->lvItemEx.iIndent ? TRUE : FALSE;
// If Item has chechboxes toggle on keyboard space or mouse clicks
// over the itemState Icon.
// double-clicks on itemIcon togles the whether the branch is expanded/collasse
// if left/right keyboard expand collapse
switch(code) { case LVN_KEYDOWN: { switch(wVKey) { case VK_SPACE: if (fItemHasCheckBoxes) { fToggleCheckBox = TRUE; } break; case VK_RIGHT: case VK_LEFT: if (pListViewItem->iChildren) { fExpand = VK_RIGHT == wVKey ? TRUE : FALSE; fCollapse = !fExpand; } break; default: break; } } case NM_DBLCLK: if ( (flags & LVHT_ONITEMICON) && (pListViewItem->iChildren)) { fExpand = pListViewItem->fExpanded ? FALSE : TRUE; fCollapse = !fExpand; break; } // double-click falls through to a click.
case NM_CLICK: if ((flags & LVHT_ONITEMSTATEICON) && fItemHasCheckBoxes) { fToggleCheckBox = TRUE; } break; default: break; }
if (fExpand || fCollapse) { // don't bother if already in current state
if (pListViewItem->fExpanded != fExpand) { ExpandCollapse(pListViewItem,fExpand); } return TRUE; } else if (fToggleCheckBox) { // for now just toggle the state. if have children need to set them appropriately.
iStateMask = LVITEMEXSTATE_CHECKED == GetCheckState(pListViewItem->lvItemEx.iItem) ? LVIS_STATEIMAGEMASK_UNCHECK : LVIS_STATEIMAGEMASK_CHECK;
SetItemState(pListViewItem->lvItemEx.iItem,iStateMask,LVIS_STATEIMAGEMASK); return TRUE; }
return fReturn; // default we pass it along
}
|