You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2496 lines
66 KiB
2496 lines
66 KiB
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
}
|
|
|