Fix crash in wxDataViewTreeCtrl::DeleteAllItems() in wxGTK
Restore the checks for the model stamp, reverting the changes of
18594afe76
: we still need to ignore the
calls to at least iter_children() and iter_nth_child() model methods
that can be called from inside gtk_tree_view_set_model() when we reset
the model, as running these methods crashes when trying to use the
pointers to already deleted items.
For consistency and robustness, add checks for the model stamp to all
the methods and not just those two, just in case other ones end up being
called later in some way.
Also add a unit test checking that DeleteAllItems() doesn't crash and
does delete all items.
Closes #18533.
This commit is contained in:
@@ -700,12 +700,22 @@ wxgtk_tree_model_init(GTypeInstance* instance, void*)
|
||||
// them call the methods of wxWidgets' wxDataViewModel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Note that all the model methods check if its stamp is valid because we want
|
||||
// to avoid using the model being dissociated from the tree: its items may have
|
||||
// been already deleted and referencing them can result in a crash. And while
|
||||
// it's probably unnecessary to perform this check in all the methods, as only
|
||||
// a couple of them can be really called from inside gtk_tree_view_set_model(),
|
||||
// it does no harm to check this everywhere and this should be more robust.
|
||||
|
||||
static GtkTreeModelFlags
|
||||
wxgtk_tree_model_get_flags (GtkTreeModel *tree_model)
|
||||
{
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), (GtkTreeModelFlags)0 );
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return (GtkTreeModelFlags)0;
|
||||
|
||||
return wxtree_model->internal->get_flags();
|
||||
}
|
||||
|
||||
@@ -715,6 +725,9 @@ wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model)
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return 0;
|
||||
|
||||
return wxtree_model->internal->GetDataViewModel()->GetColumnCount();
|
||||
}
|
||||
|
||||
@@ -725,6 +738,9 @@ wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), G_TYPE_INVALID);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
GType gtype = G_TYPE_INVALID;
|
||||
|
||||
wxString wxtype = wxtree_model->internal->GetDataViewModel()->GetColumnType( (unsigned int) index );
|
||||
@@ -749,6 +765,9 @@ wxgtk_tree_model_get_iter (GtkTreeModel *tree_model,
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->get_iter( iter, path );
|
||||
}
|
||||
|
||||
@@ -760,6 +779,9 @@ wxgtk_tree_model_get_path (GtkTreeModel *tree_model,
|
||||
|
||||
GtkWxTreeModel *wxtree_model = GTK_WX_TREE_MODEL (tree_model);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return gtk_tree_path_new();
|
||||
|
||||
g_return_val_if_fail (iter->stamp == wxtree_model->stamp, NULL);
|
||||
|
||||
return wxtree_model->internal->get_path( iter );
|
||||
@@ -774,6 +796,9 @@ wxgtk_tree_model_get_value (GtkTreeModel *tree_model,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
|
||||
g_return_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model) );
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return;
|
||||
|
||||
wxDataViewModel *model = wxtree_model->internal->GetDataViewModel();
|
||||
wxString mtype = model->GetColumnType( (unsigned int) column );
|
||||
if (mtype == wxT("string"))
|
||||
@@ -800,6 +825,9 @@ wxgtk_tree_model_iter_next (GtkTreeModel *tree_model,
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->iter_next( iter );
|
||||
}
|
||||
|
||||
@@ -809,12 +837,16 @@ wxgtk_tree_model_iter_children (GtkTreeModel *tree_model,
|
||||
GtkTreeIter *parent)
|
||||
{
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
if (parent)
|
||||
{
|
||||
g_return_val_if_fail (wxtree_model->stamp == parent->stamp, FALSE);
|
||||
}
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->iter_children( iter, parent );
|
||||
}
|
||||
|
||||
@@ -837,6 +869,9 @@ wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model,
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0);
|
||||
g_return_val_if_fail ( !iter || wxtree_model->stamp == iter->stamp, 0);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return 0;
|
||||
|
||||
return wxtree_model->internal->iter_n_children( iter );
|
||||
}
|
||||
|
||||
@@ -849,6 +884,9 @@ wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->iter_nth_child( iter, parent, n );
|
||||
}
|
||||
|
||||
@@ -861,6 +899,9 @@ wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model,
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
g_return_val_if_fail (wxtree_model->stamp == child->stamp, FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->iter_parent( iter, child );
|
||||
}
|
||||
|
||||
@@ -872,6 +913,9 @@ wxgtk_tree_model_row_draggable (GtkTreeDragSource *drag_source,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->row_draggable( drag_source, path );
|
||||
}
|
||||
|
||||
@@ -882,6 +926,9 @@ wxgtk_tree_model_drag_data_delete (GtkTreeDragSource *drag_source,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->drag_data_delete( drag_source, path );
|
||||
}
|
||||
|
||||
@@ -908,6 +955,9 @@ wxgtk_tree_model_drag_data_get (GtkTreeDragSource *drag_source,
|
||||
wxPrintf( "format %d\n", selection_data->format );
|
||||
#endif
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->drag_data_get( drag_source, path, selection_data );
|
||||
}
|
||||
|
||||
@@ -919,6 +969,9 @@ wxgtk_tree_model_drag_data_received (GtkTreeDragDest *drag_dest,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_dest;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->drag_data_received( drag_dest, dest, selection_data );
|
||||
}
|
||||
|
||||
@@ -930,6 +983,9 @@ wxgtk_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest,
|
||||
GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_dest;
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
return wxtree_model->internal->row_drop_possible( drag_dest, dest_path, selection_data );
|
||||
}
|
||||
|
||||
@@ -943,6 +999,9 @@ wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable,
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (sortable), FALSE);
|
||||
|
||||
if ( wxtree_model->stamp == 0 )
|
||||
return FALSE;
|
||||
|
||||
if (!wxtree_model->internal->IsSorted())
|
||||
{
|
||||
if (sort_column_id)
|
||||
@@ -972,6 +1031,9 @@ wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable,
|
||||
GtkWxTreeModel *tree_model = (GtkWxTreeModel *) sortable;
|
||||
g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
|
||||
|
||||
if ( tree_model->stamp == 0 )
|
||||
return;
|
||||
|
||||
tree_model->internal->SetDataViewSortColumn( gs_lastLeftClickHeader );
|
||||
|
||||
if ((sort_column_id != (gint) tree_model->internal->GetSortColumn()) ||
|
||||
@@ -3067,6 +3129,13 @@ static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *WXUNUSED(column),
|
||||
g_return_if_fail (GTK_IS_WX_TREE_MODEL (model));
|
||||
GtkWxTreeModel *tree_model = (GtkWxTreeModel *) model;
|
||||
|
||||
if ( !tree_model->stamp )
|
||||
{
|
||||
// The model is temporarily invalid and can't be used, see the code in
|
||||
// wxDataViewCtrlInternal::UseModel().
|
||||
return;
|
||||
}
|
||||
|
||||
wxDataViewRenderer *cell = (wxDataViewRenderer*) data;
|
||||
|
||||
wxDataViewItem item( (void*) iter->user_data );
|
||||
@@ -3577,8 +3646,24 @@ void wxDataViewCtrlInternal::UseModel(bool use)
|
||||
// event handler.
|
||||
wxDataViewCtrl::SelectionEventsSuppressor noSelection(m_owner);
|
||||
|
||||
gtk_tree_view_set_model( GTK_TREE_VIEW(m_owner->GtkGetTreeView()),
|
||||
use ? GTK_TREE_MODEL(m_gtk_model) : NULL );
|
||||
if ( use )
|
||||
{
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(m_owner->GtkGetTreeView()),
|
||||
GTK_TREE_MODEL(m_gtk_model));
|
||||
}
|
||||
else // Disassociate the model from the control.
|
||||
{
|
||||
// We need to prevent wxGtkTreeCellDataFunc and other callbacks that
|
||||
// may be called from inside gtk_tree_view_set_model() from using the
|
||||
// model items not existing any longer, so change the model stamp to
|
||||
// indicate that it temporarily can't be used.
|
||||
const gint stampOrig = m_gtk_model->stamp;
|
||||
m_gtk_model->stamp = 0;
|
||||
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(m_owner->GtkGetTreeView()), NULL);
|
||||
|
||||
m_gtk_model->stamp = stampOrig;
|
||||
}
|
||||
}
|
||||
|
||||
void wxDataViewCtrlInternal::ScheduleRefresh()
|
||||
|
@@ -262,4 +262,16 @@ TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
|
||||
CHECK( rectRoot == wxRect() );
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
|
||||
"wxDVC::DeleteAllItems",
|
||||
"[wxDataViewCtrl][delete]")
|
||||
{
|
||||
const wxDataViewItem top = m_dvc->GetTopItem();
|
||||
CHECK( m_dvc->GetChildCount(top) == 1 );
|
||||
|
||||
m_dvc->DeleteAllItems();
|
||||
|
||||
CHECK( m_dvc->GetChildCount(top) == 0 );
|
||||
}
|
||||
|
||||
#endif //wxUSE_DATAVIEWCTRL
|
||||
|
Reference in New Issue
Block a user