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:
@@ -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.
|
|
||||||
bool m_hasChildren;
|
|
||||||
|
|
||||||
// Is the branch node currently open (expanded)?
|
// Is the branch node currently open (expanded)?
|
||||||
bool m_open;
|
bool open;
|
||||||
|
|
||||||
// Total count of expanded (i.e. visible with the help of some scrolling) items in the subtree,
|
// Total count of expanded (i.e. visible with the help of some
|
||||||
// but excluding this node. I.e. it is 0 for leaves and is the number of rows the subtree occupies
|
// scrolling) items in the subtree, but excluding this node. I.e. it is
|
||||||
// for branch nodes.
|
// 0 for leaves and is the number of rows the subtree occupies for
|
||||||
int m_subTreeCount;
|
// branch nodes.
|
||||||
|
int subTreeCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
BranchNodeData *m_branchData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1909,6 +1958,8 @@ bool Walker( wxDataViewTreeNode * node, DoJob & func )
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( node->HasChildren() )
|
||||||
|
{
|
||||||
const wxDataViewTreeNodes& nodes = node->GetNodes();
|
const wxDataViewTreeNodes& nodes = node->GetNodes();
|
||||||
|
|
||||||
for ( wxDataViewTreeNodes::const_iterator i = nodes.begin();
|
for ( wxDataViewTreeNodes::const_iterator i = nodes.begin();
|
||||||
@@ -1918,6 +1969,7 @@ bool Walker( wxDataViewTreeNode * node, DoJob & func )
|
|||||||
if ( Walker(*i, func) )
|
if ( Walker(*i, func) )
|
||||||
return true;
|
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]);
|
||||||
|
Reference in New Issue
Block a user