Fix incorrect handling of ItemsAdded() in wxDataViewCtrl.

When adding items to the model in bulk and then calling ItemsAdded(),
internal representation in both the generic and GTK+ versions wasn't
updated correctly if the order of the notifications was such that an
item would be inserted after other new, but not yet inserted, items.

Fixes bug #13587.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69547 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Václav Slavík
2011-10-26 17:10:27 +00:00
parent 5e0e1372fd
commit 5bb82ad49f
2 changed files with 124 additions and 11 deletions

View File

@@ -377,6 +377,22 @@ public:
m_branchData->children.Remove(node);
}
// returns position of child node for given item in children list or wxNOT_FOUND
int FindChildByItem(const wxDataViewItem& item) const
{
if ( !m_branchData )
return wxNOT_FOUND;
const wxDataViewTreeNodes& nodes = m_branchData->children;
const int len = nodes.size();
for ( int i = 0; i < len; i++ )
{
if ( nodes[i]->m_item == item )
return i;
}
return wxNOT_FOUND;
}
const wxDataViewItem & GetItem() const { return m_item; }
void SetItem( const wxDataViewItem & item ) { m_item = item; }
@@ -2060,17 +2076,59 @@ bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxData
if ( !parentNode )
return false;
wxDataViewItemArray siblings;
GetModel()->GetChildren(parent, siblings);
int itemPos = siblings.Index(item, /*fromEnd=*/true);
wxCHECK_MSG( itemPos != wxNOT_FOUND, false, "adding non-existent item?" );
wxDataViewItemArray modelSiblings;
GetModel()->GetChildren(parent, modelSiblings);
const int modelSiblingsSize = modelSiblings.size();
int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
itemNode->SetHasChildren(GetModel()->IsContainer(item));
parentNode->SetHasChildren(true);
parentNode->InsertChild(itemNode, itemPos);
const wxDataViewTreeNodes& nodeSiblings = parentNode->GetChildNodes();
const int nodeSiblingsSize = nodeSiblings.size();
int nodePos = 0;
if ( posInModel == modelSiblingsSize - 1 )
{
nodePos = nodeSiblingsSize;
}
else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
{
// This is the simple case when our node tree already matches the
// model and only this one item is missing.
nodePos = posInModel;
}
else
{
// It's possible that a larger discrepancy between the model and
// our realization exists. This can happen e.g. when adding a bunch
// of items to the model and then calling ItemsAdded() just once
// afterwards. In this case, we must find the right position by
// looking at sibling items.
// append to the end if we won't find a better position:
nodePos = nodeSiblingsSize;
for ( int nextItemPos = posInModel + 1;
nextItemPos < modelSiblingsSize;
nextItemPos++ )
{
int nextNodePos = parentNode->FindChildByItem(modelSiblings[nextItemPos]);
if ( nextNodePos != wxNOT_FOUND )
{
nodePos = nextNodePos;
break;
}
}
}
parentNode->ChangeSubTreeCount(+1);
parentNode->InsertChild(itemNode, nodePos);
m_count = -1;
}

View File

@@ -432,6 +432,20 @@ public:
}
}
// returns position of child node for given item in children list or wxNOT_FOUND
int FindChildByItem(const wxDataViewItem& item) const
{
const void* itemId = item.GetID();
const wxGtkTreeModelChildren& nodes = m_children;
const int len = nodes.size();
for ( int i = 0; i < len; i++ )
{
if ( nodes[i] == itemId )
return i;
}
return wxNOT_FOUND;
}
wxGtkTreeModelNode* GetParent()
{ return m_parent; }
wxGtkTreeModelNodes &GetNodes()
@@ -3672,15 +3686,56 @@ bool wxDataViewCtrlInternal::ItemAdded( const wxDataViewItem &parent, const wxDa
wxCHECK_MSG(parent_node, false,
"Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
wxDataViewItemArray siblings;
m_wx_model->GetChildren(parent, siblings);
int itemPos = siblings.Index(item, /*fromEnd=*/true);
wxCHECK_MSG( itemPos != wxNOT_FOUND, false, "adding non-existent item?" );
wxDataViewItemArray modelSiblings;
m_wx_model->GetChildren(parent, modelSiblings);
const int modelSiblingsSize = modelSiblings.size();
int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
const wxGtkTreeModelChildren& nodeSiblings = parent_node->GetChildren();
const int nodeSiblingsSize = nodeSiblings.size();
int nodePos = 0;
if ( posInModel == modelSiblingsSize - 1 )
{
nodePos = nodeSiblingsSize;
}
else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
{
// This is the simple case when our node tree already matches the
// model and only this one item is missing.
nodePos = posInModel;
}
else
{
// It's possible that a larger discrepancy between the model and
// our realization exists. This can happen e.g. when adding a bunch
// of items to the model and then calling ItemsAdded() just once
// afterwards. In this case, we must find the right position by
// looking at sibling items.
// append to the end if we won't find a better position:
nodePos = nodeSiblingsSize;
for ( int nextItemPos = posInModel + 1;
nextItemPos < modelSiblingsSize;
nextItemPos++ )
{
int nextNodePos = parent_node->FindChildByItem(modelSiblings[nextItemPos]);
if ( nextNodePos != wxNOT_FOUND )
{
nodePos = nextNodePos;
break;
}
}
}
if (m_wx_model->IsContainer( item ))
parent_node->InsertNode( new wxGtkTreeModelNode( parent_node, item, this ), itemPos );
parent_node->InsertNode( new wxGtkTreeModelNode( parent_node, item, this ), nodePos );
else
parent_node->InsertLeaf( item.GetID(), itemPos );
parent_node->InsertLeaf( item.GetID(), nodePos );
}
ScheduleRefresh();