Don't use global variables for generic wxDataViewCtrl sorting.
Improve code quality and also fix the following issue: when 2 or more wxDataViewCtrl were frozen, only the first one was sorted on thaw as g_column was erased by the first wxDataViewCtrl to be sorted and so preventing the other one from being sorted. Closes https://github.com/wxWidgets/wxWidgets/pull/63
This commit is contained in:
committed by
Vadim Zeitlin
parent
3b573aa4d3
commit
09e8876c0c
@@ -80,7 +80,6 @@ static const int EXPANDER_OFFSET = 1;
|
|||||||
// Below is the compare stuff.
|
// Below is the compare stuff.
|
||||||
// For the generic implementation, both the leaf nodes and the nodes are sorted for
|
// For the generic implementation, both the leaf nodes and the nodes are sorted for
|
||||||
// fast search when needed
|
// fast search when needed
|
||||||
static wxDataViewModel* g_model;
|
|
||||||
|
|
||||||
// The column is either the index of the column to be used for sorting or one
|
// The column is either the index of the column to be used for sorting or one
|
||||||
// of the special values in this enum:
|
// of the special values in this enum:
|
||||||
@@ -96,9 +95,6 @@ enum
|
|||||||
SortColumn_Default = -1
|
SortColumn_Default = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
static int g_column = SortColumn_None;
|
|
||||||
static bool g_asending = true;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// helper functions
|
// helper functions
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -407,17 +403,18 @@ public:
|
|||||||
// wxDataViewTreeNode
|
// wxDataViewTreeNode
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class wxDataViewMainWindow;
|
||||||
class wxDataViewTreeNode;
|
class wxDataViewTreeNode;
|
||||||
WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
|
WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
|
||||||
|
|
||||||
int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
|
|
||||||
wxDataViewTreeNode ** node2);
|
|
||||||
|
|
||||||
class wxDataViewTreeNode
|
class wxDataViewTreeNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxDataViewTreeNode(wxDataViewTreeNode *parent, const wxDataViewItem& item)
|
wxDataViewTreeNode(wxDataViewMainWindow *window,
|
||||||
: m_parent(parent),
|
wxDataViewTreeNode *parent,
|
||||||
|
const wxDataViewItem& item)
|
||||||
|
: m_window(window),
|
||||||
|
m_parent(parent),
|
||||||
m_item(item),
|
m_item(item),
|
||||||
m_branchData(NULL)
|
m_branchData(NULL)
|
||||||
{
|
{
|
||||||
@@ -439,9 +436,9 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static wxDataViewTreeNode* CreateRootNode()
|
static wxDataViewTreeNode* CreateRootNode(wxDataViewMainWindow *w)
|
||||||
{
|
{
|
||||||
wxDataViewTreeNode *n = new wxDataViewTreeNode(NULL, wxDataViewItem());
|
wxDataViewTreeNode *n = new wxDataViewTreeNode(w, NULL, wxDataViewItem());
|
||||||
n->m_branchData = new BranchNodeData;
|
n->m_branchData = new BranchNodeData;
|
||||||
n->m_branchData->open = true;
|
n->m_branchData->open = true;
|
||||||
return n;
|
return n;
|
||||||
@@ -455,17 +452,7 @@ public:
|
|||||||
return m_branchData->children;
|
return m_branchData->children;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertChild(wxDataViewTreeNode *node, unsigned index)
|
void InsertChild(wxDataViewTreeNode *node, unsigned index);
|
||||||
{
|
|
||||||
if ( !m_branchData )
|
|
||||||
m_branchData = new BranchNodeData;
|
|
||||||
|
|
||||||
m_branchData->children.Insert(node, index);
|
|
||||||
|
|
||||||
// TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n)
|
|
||||||
if (g_column >= -1)
|
|
||||||
m_branchData->children.Sort( &wxGenericTreeModelNodeCmp );
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveChild(wxDataViewTreeNode *node)
|
void RemoveChild(wxDataViewTreeNode *node)
|
||||||
{
|
{
|
||||||
@@ -580,27 +567,10 @@ public:
|
|||||||
m_parent->ChangeSubTreeCount(num);
|
m_parent->ChangeSubTreeCount(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resort()
|
void Resort();
|
||||||
{
|
|
||||||
if ( !m_branchData )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (g_column >= -1)
|
|
||||||
{
|
|
||||||
wxDataViewTreeNodes& nodes = m_branchData->children;
|
|
||||||
|
|
||||||
nodes.Sort( &wxGenericTreeModelNodeCmp );
|
|
||||||
int len = nodes.GetCount();
|
|
||||||
for (int i = 0; i < len; i ++)
|
|
||||||
{
|
|
||||||
if ( nodes[i]->HasChildren() )
|
|
||||||
nodes[i]->Resort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
wxDataViewMainWindow *m_window;
|
||||||
wxDataViewTreeNode *m_parent;
|
wxDataViewTreeNode *m_parent;
|
||||||
|
|
||||||
// Corresponding model item.
|
// Corresponding model item.
|
||||||
@@ -634,12 +604,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
|
|
||||||
wxDataViewTreeNode ** node2)
|
|
||||||
{
|
|
||||||
return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// wxDataViewMainWindow
|
// wxDataViewMainWindow
|
||||||
@@ -680,10 +644,10 @@ public:
|
|||||||
// SortPrepare() was called -- and ignored -- while we were frozen.
|
// SortPrepare() was called -- and ignored -- while we were frozen.
|
||||||
virtual void DoThaw()
|
virtual void DoThaw()
|
||||||
{
|
{
|
||||||
if ( g_column == SortColumn_OnThaw )
|
if ( m_sortColumn == SortColumn_OnThaw )
|
||||||
{
|
{
|
||||||
Resort();
|
Resort();
|
||||||
g_column = SortColumn_None;
|
m_sortColumn = SortColumn_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxWindow::DoThaw();
|
wxWindow::DoThaw();
|
||||||
@@ -691,23 +655,23 @@ public:
|
|||||||
|
|
||||||
void SortPrepare()
|
void SortPrepare()
|
||||||
{
|
{
|
||||||
g_model = GetModel();
|
wxDataViewModel* model = GetModel();
|
||||||
|
|
||||||
wxDataViewColumn* col = GetOwner()->GetSortingColumn();
|
wxDataViewColumn* col = GetOwner()->GetSortingColumn();
|
||||||
if( !col )
|
if( !col )
|
||||||
{
|
{
|
||||||
if (g_model->HasDefaultCompare())
|
if (model->HasDefaultCompare())
|
||||||
{
|
{
|
||||||
// See below for the explanation of IsFrozen() test.
|
// See below for the explanation of IsFrozen() test.
|
||||||
if ( IsFrozen() )
|
if ( IsFrozen() )
|
||||||
g_column = SortColumn_OnThaw;
|
m_sortColumn = SortColumn_OnThaw;
|
||||||
else
|
else
|
||||||
g_column = SortColumn_Default;
|
m_sortColumn = SortColumn_Default;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
g_column = SortColumn_None;
|
m_sortColumn = SortColumn_None;
|
||||||
|
|
||||||
g_asending = true;
|
m_sortAscending = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,12 +680,12 @@ public:
|
|||||||
// them all at once when the window is finally thawed, see above.
|
// them all at once when the window is finally thawed, see above.
|
||||||
if ( IsFrozen() )
|
if ( IsFrozen() )
|
||||||
{
|
{
|
||||||
g_column = SortColumn_OnThaw;
|
m_sortColumn = SortColumn_OnThaw;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_column = col->GetModelColumn();
|
m_sortColumn = col->GetModelColumn();
|
||||||
g_asending = col->IsSortOrderAscending();
|
m_sortAscending = col->IsSortOrderAscending();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
|
void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
|
||||||
@@ -844,6 +808,9 @@ public:
|
|||||||
void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col);
|
void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col);
|
||||||
void FinishEditing();
|
void FinishEditing();
|
||||||
|
|
||||||
|
int GetSortColumn() const { return m_sortColumn; }
|
||||||
|
bool IsAscendingSort() const { return m_sortAscending; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InvalidateCount() { m_count = -1; }
|
void InvalidateCount() { m_count = -1; }
|
||||||
void UpdateCount(int count)
|
void UpdateCount(int count)
|
||||||
@@ -912,6 +879,10 @@ private:
|
|||||||
// This is the tree node under the cursor
|
// This is the tree node under the cursor
|
||||||
wxDataViewTreeNode * m_underMouse;
|
wxDataViewTreeNode * m_underMouse;
|
||||||
|
|
||||||
|
// sorted column + extra flags
|
||||||
|
int m_sortColumn;
|
||||||
|
bool m_sortAscending;
|
||||||
|
|
||||||
// The control used for editing or NULL.
|
// The control used for editing or NULL.
|
||||||
wxWeakRef<wxWindow> m_editorCtrl;
|
wxWeakRef<wxWindow> m_editorCtrl;
|
||||||
|
|
||||||
@@ -1469,12 +1440,73 @@ void wxDataViewRenameTimer::Notify()
|
|||||||
m_owner->OnRenameTimer();
|
m_owner->OnRenameTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// wxDataViewTreeNode
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Used by wxGenericTreeModelNodeCmp sort callback only.
|
||||||
|
//
|
||||||
|
// This is not thread safe but it should be fine if all events are handled in
|
||||||
|
// one thread.
|
||||||
|
static wxDataViewModel* g_model;
|
||||||
|
static int g_column;
|
||||||
|
static bool g_asending;
|
||||||
|
|
||||||
|
int LINKAGEMODE wxGenericTreeModelNodeCmp(wxDataViewTreeNode ** node1,
|
||||||
|
wxDataViewTreeNode ** node2)
|
||||||
|
{
|
||||||
|
return g_model->Compare((*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void wxDataViewTreeNode::InsertChild(wxDataViewTreeNode *node, unsigned index)
|
||||||
|
{
|
||||||
|
if (!m_branchData)
|
||||||
|
m_branchData = new BranchNodeData;
|
||||||
|
|
||||||
|
m_branchData->children.Insert(node, index);
|
||||||
|
|
||||||
|
// TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n)
|
||||||
|
if ((g_column = m_window->GetSortColumn()) >= -1)
|
||||||
|
{
|
||||||
|
g_model = m_window->GetModel();
|
||||||
|
g_asending = m_window->IsAscendingSort();
|
||||||
|
|
||||||
|
m_branchData->children.Sort(&wxGenericTreeModelNodeCmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxDataViewTreeNode::Resort()
|
||||||
|
{
|
||||||
|
if (!m_branchData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((g_column = m_window->GetSortColumn()) >= -1)
|
||||||
|
{
|
||||||
|
wxDataViewTreeNodes& nodes = m_branchData->children;
|
||||||
|
|
||||||
|
g_model = m_window->GetModel();
|
||||||
|
g_asending = m_window->IsAscendingSort();
|
||||||
|
nodes.Sort(&wxGenericTreeModelNodeCmp);
|
||||||
|
int len = nodes.GetCount();
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (nodes[i]->HasChildren())
|
||||||
|
nodes[i]->Resort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// wxDataViewMainWindow
|
// wxDataViewMainWindow
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// The tree building helper, declared firstly
|
// The tree building helper, declared firstly
|
||||||
static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
|
static void BuildTreeHelper(wxDataViewMainWindow *window,
|
||||||
|
const wxDataViewModel *model,
|
||||||
|
const wxDataViewItem & item,
|
||||||
wxDataViewTreeNode * node);
|
wxDataViewTreeNode * node);
|
||||||
|
|
||||||
wxIMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow);
|
wxIMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow);
|
||||||
@@ -1532,11 +1564,15 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i
|
|||||||
// TODO: maybe there is something system colour to use
|
// TODO: maybe there is something system colour to use
|
||||||
m_penExpander = wxPen(wxColour(0,0,0));
|
m_penExpander = wxPen(wxColour(0,0,0));
|
||||||
|
|
||||||
m_root = wxDataViewTreeNode::CreateRootNode();
|
m_root = wxDataViewTreeNode::CreateRootNode(this);
|
||||||
|
|
||||||
// Make m_count = -1 will cause the class recaculate the real displaying number of rows.
|
// Make m_count = -1 will cause the class recaculate the real displaying number of rows.
|
||||||
m_count = -1;
|
m_count = -1;
|
||||||
m_underMouse = NULL;
|
m_underMouse = NULL;
|
||||||
|
|
||||||
|
m_sortColumn = SortColumn_None;
|
||||||
|
m_sortAscending = true;
|
||||||
|
|
||||||
UpdateDisplay();
|
UpdateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2330,7 +2366,7 @@ bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxData
|
|||||||
int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
|
int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
|
||||||
wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
|
wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
|
||||||
|
|
||||||
wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
|
wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(this, parentNode, item);
|
||||||
itemNode->SetHasChildren(GetModel()->IsContainer(item));
|
itemNode->SetHasChildren(GetModel()->IsContainer(item));
|
||||||
|
|
||||||
parentNode->SetHasChildren(true);
|
parentNode->SetHasChildren(true);
|
||||||
@@ -2503,7 +2539,7 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
|
|||||||
bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
|
bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
|
||||||
{
|
{
|
||||||
SortPrepare();
|
SortPrepare();
|
||||||
g_model->Resort();
|
GetModel()->Resort();
|
||||||
|
|
||||||
GetOwner()->InvalidateColBestWidths();
|
GetOwner()->InvalidateColBestWidths();
|
||||||
|
|
||||||
@@ -2534,7 +2570,7 @@ bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned i
|
|||||||
return true;
|
return true;
|
||||||
*/
|
*/
|
||||||
SortPrepare();
|
SortPrepare();
|
||||||
g_model->Resort();
|
GetModel()->Resort();
|
||||||
|
|
||||||
GetOwner()->InvalidateColBestWidth(view_column);
|
GetOwner()->InvalidateColBestWidth(view_column);
|
||||||
|
|
||||||
@@ -3148,7 +3184,7 @@ void wxDataViewMainWindow::Expand( unsigned int row )
|
|||||||
if( node->GetChildNodes().empty() )
|
if( node->GetChildNodes().empty() )
|
||||||
{
|
{
|
||||||
SortPrepare();
|
SortPrepare();
|
||||||
::BuildTreeHelper(GetModel(), node->GetItem(), node);
|
::BuildTreeHelper(this, GetModel(), node->GetItem(), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned countNewRows = node->GetSubTreeCount();
|
const unsigned countNewRows = node->GetSubTreeCount();
|
||||||
@@ -3253,7 +3289,7 @@ wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item
|
|||||||
// child nodes in the control's representation yet. We have
|
// child nodes in the control's representation yet. We have
|
||||||
// to realize its subtree now.
|
// to realize its subtree now.
|
||||||
SortPrepare();
|
SortPrepare();
|
||||||
::BuildTreeHelper(model, node->GetItem(), node);
|
::BuildTreeHelper(this, model, node->GetItem(), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxDataViewTreeNodes& nodes = node->GetChildNodes();
|
const wxDataViewTreeNodes& nodes = node->GetChildNodes();
|
||||||
@@ -3463,8 +3499,8 @@ int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
|
static void BuildTreeHelper( wxDataViewMainWindow *window, const wxDataViewModel * model,
|
||||||
wxDataViewTreeNode * node)
|
const wxDataViewItem & item, wxDataViewTreeNode * node)
|
||||||
{
|
{
|
||||||
if( !model->IsContainer( item ) )
|
if( !model->IsContainer( item ) )
|
||||||
return;
|
return;
|
||||||
@@ -3474,7 +3510,7 @@ static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewIte
|
|||||||
|
|
||||||
for ( unsigned int index = 0; index < num; index++ )
|
for ( unsigned int index = 0; index < num; index++ )
|
||||||
{
|
{
|
||||||
wxDataViewTreeNode *n = new wxDataViewTreeNode(node, children[index]);
|
wxDataViewTreeNode *n = new wxDataViewTreeNode(window, node, children[index]);
|
||||||
|
|
||||||
if( model->IsContainer(children[index]) )
|
if( model->IsContainer(children[index]) )
|
||||||
n->SetHasChildren( true );
|
n->SetHasChildren( true );
|
||||||
@@ -3496,12 +3532,12 @@ void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_root = wxDataViewTreeNode::CreateRootNode();
|
m_root = wxDataViewTreeNode::CreateRootNode(this);
|
||||||
|
|
||||||
// First we define a invalid item to fetch the top-level elements
|
// First we define a invalid item to fetch the top-level elements
|
||||||
wxDataViewItem item;
|
wxDataViewItem item;
|
||||||
SortPrepare();
|
SortPrepare();
|
||||||
BuildTreeHelper( model, item, m_root);
|
BuildTreeHelper(this, model, item, m_root);
|
||||||
InvalidateCount();
|
InvalidateCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user