Save memory in wxDataViewTreeNode.

Put data that are meaningful only for non-leaf nodes into a separate
struct that is only allocated for branch nodes. This makes branch
nodes larger by sizeof(void*), but leaf nodes save >50% of memory.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@68914 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Václav Slavík
2011-08-27 13:24:22 +00:00
parent 422aa8ecfe
commit d0cfefc4e0

View File

@@ -285,30 +285,40 @@ public:
wxDataViewTreeNode(wxDataViewTreeNode *parent, const wxDataViewItem& item) wxDataViewTreeNode(wxDataViewTreeNode *parent, const wxDataViewItem& item)
: m_item(item), : m_item(item),
m_parent(parent), m_parent(parent),
m_hasChildren(false), m_branchData(NULL)
m_open(false),
m_subTreeCount(0)
{ {
} }
~wxDataViewTreeNode()
{
delete m_branchData;
}
static wxDataViewTreeNode* CreateRootNode() static wxDataViewTreeNode* CreateRootNode()
{ {
wxDataViewTreeNode *n = new wxDataViewTreeNode(NULL, wxDataViewItem()); wxDataViewTreeNode *n = new wxDataViewTreeNode(NULL, wxDataViewItem());
n->m_open = true; n->SetHasChildren(true);
n->m_hasChildren = true; n->m_branchData->open = true;
return n; return n;
} }
wxDataViewTreeNode * GetParent() const { return m_parent; } wxDataViewTreeNode * GetParent() const { return m_parent; }
wxDataViewTreeNodes & GetNodes() { return m_nodes; } wxDataViewTreeNodes& GetNodes()
{
wxASSERT( m_branchData != NULL );
return m_branchData->nodes;
}
void AddNode( wxDataViewTreeNode * node ) void AddNode( wxDataViewTreeNode * node )
{ {
m_nodes.Add( node ); if ( !m_branchData )
m_branchData = new BranchNodeData;
m_branchData->nodes.Add( node );
// TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n) // TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n)
if (g_column >= -1) if (g_column >= -1)
m_nodes.Sort( &wxGenericTreeModelNodeCmp ); m_branchData->nodes.Sort( &wxGenericTreeModelNodeCmp );
} }
const wxDataViewItem & GetItem() const { return m_item; } const wxDataViewItem & GetItem() const { return m_item; }
@@ -328,42 +338,65 @@ public:
bool IsOpen() const bool IsOpen() const
{ {
return m_open; return m_branchData && m_branchData->open;
} }
void ToggleOpen() void ToggleOpen()
{ {
wxCHECK_RET( m_branchData != NULL, "can't open leaf node" );
int sum = 0; int sum = 0;
const int len = m_nodes.GetCount(); const wxDataViewTreeNodes& nodes = m_branchData->nodes;
const int len = nodes.GetCount();
for ( int i = 0;i < len; i ++) for ( int i = 0;i < len; i ++)
sum += 1 + m_nodes[i]->GetSubTreeCount(); sum += 1 + nodes[i]->GetSubTreeCount();
if (m_open) if (m_branchData->open)
{ {
ChangeSubTreeCount(-sum); ChangeSubTreeCount(-sum);
m_open = !m_open; m_branchData->open = !m_branchData->open;
} }
else else
{ {
m_open = !m_open; m_branchData->open = !m_branchData->open;
ChangeSubTreeCount(+sum); ChangeSubTreeCount(+sum);
} }
} }
// "HasChildren" property corresponds to model's IsContainer(). Note that it may be true // "HasChildren" property corresponds to model's IsContainer(). Note that it may be true
// even if GetNodes() is empty; see below. // even if GetNodes() is empty; see below.
bool HasChildren() const { return m_hasChildren; } bool HasChildren() const
void SetHasChildren(bool has) { m_hasChildren = has; } {
return m_branchData != NULL;
}
void SetHasChildren(bool has)
{
if ( !has )
{
wxDELETE(m_branchData);
}
else if ( m_branchData == NULL )
{
m_branchData = new BranchNodeData;
}
}
int GetSubTreeCount() const
{
return m_branchData ? m_branchData->subTreeCount : 0;
}
int GetSubTreeCount() const { return m_subTreeCount; }
void ChangeSubTreeCount( int num ) void ChangeSubTreeCount( int num )
{ {
if( !m_open ) wxASSERT( m_branchData != NULL );
if( !m_branchData->open )
return; return;
m_subTreeCount += num; m_branchData->subTreeCount += num;
wxASSERT( m_subTreeCount >= 0 ); wxASSERT( m_branchData->subTreeCount >= 0 );
if( m_parent ) if( m_parent )
m_parent->ChangeSubTreeCount(num); m_parent->ChangeSubTreeCount(num);
@@ -371,39 +404,55 @@ public:
void Resort() void Resort()
{ {
if ( !m_branchData )
return;
if (g_column >= -1) if (g_column >= -1)
{ {
m_nodes.Sort( &wxGenericTreeModelNodeCmp ); wxDataViewTreeNodes& nodes = m_branchData->nodes;
int len = m_nodes.GetCount();
nodes.Sort( &wxGenericTreeModelNodeCmp );
int len = nodes.GetCount();
for (int i = 0; i < len; i ++) for (int i = 0; i < len; i ++)
{ {
if ( m_nodes[i]->HasChildren() ) if ( nodes[i]->HasChildren() )
m_nodes[i]->Resort(); nodes[i]->Resort();
} }
} }
} }
private: private:
wxDataViewTreeNode *m_parent;
// Corresponding model item. // Corresponding model item.
wxDataViewItem m_item; wxDataViewItem m_item;
wxDataViewTreeNode *m_parent; // Data specific to non-leaf (branch, inner) nodes. They are kept in a
// separate struct in order to conserve memory.
struct BranchNodeData
{
BranchNodeData()
: open(false),
subTreeCount(0)
{
}
// Child nodes. Note that this may be empty even if m_hasChildren in case this branch // Child nodes. Note that this may be empty even if m_hasChildren in
// of the tree wasn't expanded and realized yet. // case this branch of the tree wasn't expanded and realized yet.
wxDataViewTreeNodes m_nodes; wxDataViewTreeNodes nodes;
// True if the node has children, i.e. if model's IsContainer() returned true for it. // Is the branch node currently open (expanded)?
bool m_hasChildren; bool open;
// Is the branch node currently open (expanded)? // Total count of expanded (i.e. visible with the help of some
bool m_open; // scrolling) items in the subtree, but excluding this node. I.e. it is
// 0 for leaves and is the number of rows the subtree occupies for
// branch nodes.
int subTreeCount;
};
// Total count of expanded (i.e. visible with the help of some scrolling) items in the subtree, BranchNodeData *m_branchData;
// but excluding this node. I.e. it is 0 for leaves and is the number of rows the subtree occupies
// for branch nodes.
int m_subTreeCount;
}; };
@@ -1909,14 +1958,17 @@ bool Walker( wxDataViewTreeNode * node, DoJob & func )
break; break;
} }
const wxDataViewTreeNodes& nodes = node->GetNodes(); if ( node->HasChildren() )
for ( wxDataViewTreeNodes::const_iterator i = nodes.begin();
i != nodes.end();
++i )
{ {
if ( Walker(*i, func) ) const wxDataViewTreeNodes& nodes = node->GetNodes();
return true;
for ( wxDataViewTreeNodes::const_iterator i = nodes.begin();
i != nodes.end();
++i )
{
if ( Walker(*i, func) )
return true;
}
} }
return false; return false;
@@ -1942,8 +1994,8 @@ bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxData
wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item); wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
itemNode->SetHasChildren(GetOwner()->GetModel()->IsContainer(item)); itemNode->SetHasChildren(GetOwner()->GetModel()->IsContainer(item));
parentNode->AddNode(itemNode);
parentNode->SetHasChildren(true); parentNode->SetHasChildren(true);
parentNode->AddNode(itemNode);
parentNode->ChangeSubTreeCount(+1); parentNode->ChangeSubTreeCount(+1);
m_count = -1; m_count = -1;
@@ -1997,6 +2049,7 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
if ( !parentNode ) if ( !parentNode )
return false; return false;
wxCHECK_MSG( parentNode->HasChildren(), false, "parent node doesn't have children?" );
const wxDataViewTreeNodes& parentsChildren = parentNode->GetNodes(); const wxDataViewTreeNodes& parentsChildren = parentNode->GetNodes();
// We can't use FindNode() to find 'item', because it was already // We can't use FindNode() to find 'item', because it was already
@@ -2703,12 +2756,14 @@ public:
// If the current node has only leaf children, we can find the // If the current node has only leaf children, we can find the
// desired node directly. This can speed up finding the node // desired node directly. This can speed up finding the node
// in some cases, and will have a very good effect for list views. // in some cases, and will have a very good effect for list views.
if ( (int)node->GetNodes().size() == node->GetSubTreeCount() ) if ( node->HasChildren() &&
(int)node->GetNodes().size() == node->GetSubTreeCount() )
{ {
const int index = static_cast<int>(row) - current - 1; const int index = static_cast<int>(row) - current - 1;
ret = node->GetNodes()[index]; ret = node->GetNodes()[index];
return DoJob::OK; return DoJob::OK;
} }
return DoJob::CONT; return DoJob::CONT;
} }
} }
@@ -3199,10 +3254,9 @@ void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
static void DestroyTreeHelper( wxDataViewTreeNode * node ) static void DestroyTreeHelper( wxDataViewTreeNode * node )
{ {
wxDataViewTreeNodes& nodes = node->GetNodes(); if ( node->HasChildren() )
if( !nodes.empty() )
{ {
wxDataViewTreeNodes& nodes = node->GetNodes();
const int len = nodes.size(); const int len = nodes.size();
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
DestroyTreeHelper(nodes[i]); DestroyTreeHelper(nodes[i]);