From c715bb750b06d0cbcb40cf87ded4289c25326e55 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 18:24:03 +0200 Subject: [PATCH 1/9] Use symbolic page numbers in the dataview sample Make the code a bit more readable. No real changes. --- samples/dataview/dataview.cpp | 165 +++++++++++++++++----------------- 1 file changed, 84 insertions(+), 81 deletions(-) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index 53a80d920d..8198d29cb1 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -164,8 +164,17 @@ private: wxNotebook* m_notebook; // the controls stored in the various tabs of the main notebook: + enum Page + { + Page_Music, + Page_List, + Page_ListStore, + Page_TreeStore, + Page_VarHeight, + Page_Max + }; - wxDataViewCtrl* m_ctrl[5]; + wxDataViewCtrl* m_ctrl[Page_Max]; // the models associated with the first two DVC: @@ -503,11 +512,8 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int m_log = NULL; m_col = NULL; - m_ctrl[0] = NULL; - m_ctrl[1] = NULL; - m_ctrl[2] = NULL; - m_ctrl[3] = NULL; - m_ctrl[4] = NULL; + for ( int page = 0; page < Page_Max; ++page ) + m_ctrl[page] = NULL; m_eventFromProgram = false; @@ -566,7 +572,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxPanel *firstPanel = new wxPanel( m_notebook, wxID_ANY ); - BuildDataViewCtrl(firstPanel, 0); // sets m_ctrl[0] + BuildDataViewCtrl(firstPanel, Page_Music); const wxSizerFlags border = wxSizerFlags().DoubleBorder(); @@ -587,8 +593,8 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int "Change ninth &title"), border); wxSizer *firstPanelSz = new wxBoxSizer( wxVERTICAL ); - m_ctrl[0]->SetMinSize(wxSize(-1, 200)); - firstPanelSz->Add(m_ctrl[0], 1, wxGROW|wxALL, 5); + m_ctrl[Page_Music]->SetMinSize(wxSize(-1, 200)); + firstPanelSz->Add(m_ctrl[Page_Music], 1, wxGROW|wxALL, 5); firstPanelSz->Add( new wxStaticText(firstPanel, wxID_ANY, "Most of the cells above are editable!"), 0, wxGROW|wxALL, 5); @@ -602,7 +608,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxPanel *secondPanel = new wxPanel( m_notebook, wxID_ANY ); - BuildDataViewCtrl(secondPanel, 1); // sets m_ctrl[1] + BuildDataViewCtrl(secondPanel, Page_List); wxBoxSizer *button_sizer2 = new wxBoxSizer( wxHORIZONTAL ); button_sizer2->Add( new wxButton( secondPanel, ID_PREPEND_LIST,"Prepend"), 0, wxALL, 10 ); @@ -619,7 +625,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxSizerFlags().Centre().DoubleBorder()); wxSizer *secondPanelSz = new wxBoxSizer( wxVERTICAL ); - secondPanelSz->Add(m_ctrl[1], 1, wxGROW|wxALL, 5); + secondPanelSz->Add(m_ctrl[Page_List], 1, wxGROW|wxALL, 5); secondPanelSz->Add(button_sizer2); secondPanelSz->Add(sortSizer); secondPanel->SetSizerAndFit(secondPanelSz); @@ -630,10 +636,10 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxPanel *thirdPanel = new wxPanel( m_notebook, wxID_ANY ); - BuildDataViewCtrl(thirdPanel, 2); // sets m_ctrl[2] + BuildDataViewCtrl(thirdPanel, Page_ListStore); wxSizer *thirdPanelSz = new wxBoxSizer( wxVERTICAL ); - thirdPanelSz->Add(m_ctrl[2], 1, wxGROW|wxALL, 5); + thirdPanelSz->Add(m_ctrl[Page_ListStore], 1, wxGROW|wxALL, 5); thirdPanel->SetSizerAndFit(thirdPanelSz); @@ -642,7 +648,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxPanel *fourthPanel = new wxPanel( m_notebook, wxID_ANY ); - BuildDataViewCtrl(fourthPanel, 3); // sets m_ctrl[3] + BuildDataViewCtrl(fourthPanel, Page_TreeStore); // Buttons wxBoxSizer *button_sizer4 = new wxBoxSizer( wxHORIZONTAL ); button_sizer4->Add( new wxButton( fourthPanel, ID_DELETE_TREE_ITEM, "Delete Selected"), 0, wxALL, 10 ); @@ -651,7 +657,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int button_sizer4->Add( new wxButton( fourthPanel, ID_ADD_TREE_CONTAINER_ITEM, "Add Container"), 0, wxALL, 10 ); wxSizer *fourthPanelSz = new wxBoxSizer( wxVERTICAL ); - fourthPanelSz->Add(m_ctrl[3], 1, wxGROW|wxALL, 5); + fourthPanelSz->Add(m_ctrl[Page_TreeStore], 1, wxGROW|wxALL, 5); fourthPanelSz->Add(button_sizer4); fourthPanel->SetSizerAndFit(fourthPanelSz); @@ -660,10 +666,10 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxPanel *fifthPanel = new wxPanel(m_notebook, wxID_ANY); - BuildDataViewCtrl(fifthPanel, 4); // sets m_ctrl[4] + BuildDataViewCtrl(fifthPanel, Page_VarHeight); wxSizer *fifthPanelSz = new wxBoxSizer(wxVERTICAL); - fifthPanelSz->Add(m_ctrl[4], 1, wxGROW | wxALL, 5); + fifthPanelSz->Add(m_ctrl[Page_VarHeight], 1, wxGROW | wxALL, 5); fifthPanel->SetSizerAndFit(fifthPanelSz); @@ -691,22 +697,23 @@ MyFrame::~MyFrame() void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned long style) { + wxASSERT(!m_ctrl[nPanel]); // should only be initialized once + switch (nPanel) { - case 0: + case Page_Music: { - wxASSERT(!m_ctrl[0] && !m_music_model); - m_ctrl[0] = + m_ctrl[Page_Music] = new wxDataViewCtrl( parent, ID_MUSIC_CTRL, wxDefaultPosition, wxDefaultSize, style ); - m_ctrl[0]->Bind(wxEVT_CHAR, &MyFrame::OnDataViewChar, this); + m_ctrl[Page_Music]->Bind(wxEVT_CHAR, &MyFrame::OnDataViewChar, this); m_music_model = new MyMusicTreeModel; - m_ctrl[0]->AssociateModel( m_music_model.get() ); + m_ctrl[Page_Music]->AssociateModel( m_music_model.get() ); #if wxUSE_DRAG_AND_DROP && wxUSE_UNICODE - m_ctrl[0]->EnableDragSource( wxDF_UNICODETEXT ); - m_ctrl[0]->EnableDropTarget( wxDF_UNICODETEXT ); + m_ctrl[Page_Music]->EnableDragSource( wxDF_UNICODETEXT ); + m_ctrl[Page_Music]->EnableDropTarget( wxDF_UNICODETEXT ); #endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE // column 0 of the view control: @@ -716,7 +723,7 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxDataViewColumn *column0 = new wxDataViewColumn( "title", tr, 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE ); - m_ctrl[0]->AppendColumn( column0 ); + m_ctrl[Page_Music]->AppendColumn( column0 ); #if 0 // Call this and sorting is enabled // immediately upon start up. @@ -731,7 +738,7 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE ); column1->SetMinWidth(150); // this column can't be resized to be smaller - m_ctrl[0]->AppendColumn( column1 ); + m_ctrl[Page_Music]->AppendColumn( column1 ); // column 2 of the view control: @@ -741,7 +748,7 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxDataViewColumn *column2 = new wxDataViewColumn( "year", sr, 2, 60, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE ); - m_ctrl[0]->AppendColumn( column2 ); + m_ctrl[Page_Music]->AppendColumn( column2 ); // column 3 of the view control: @@ -755,11 +762,11 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxDataViewColumn *column3 = new wxDataViewColumn( "rating", c, 3, 100, wxALIGN_LEFT, wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE ); - m_ctrl[0]->AppendColumn( column3 ); + m_ctrl[Page_Music]->AppendColumn( column3 ); // column 4 of the view control: - m_ctrl[0]->AppendProgressColumn( "popularity", 4, wxDATAVIEW_CELL_INERT, 80 ); + m_ctrl[Page_Music]->AppendProgressColumn( "popularity", 4, wxDATAVIEW_CELL_INERT, 80 ); // column 5 of the view control: @@ -768,43 +775,42 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l new wxDataViewColumn( "custom", cr, 5, -1, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE ); column5->SetBitmap(wxArtProvider::GetBitmap(wxART_INFORMATION, wxART_MENU)); - m_ctrl[0]->AppendColumn( column5 ); + m_ctrl[Page_Music]->AppendColumn( column5 ); // select initially the ninth symphony: - m_ctrl[0]->Select(m_music_model->GetNinthItem()); + m_ctrl[Page_Music]->Select(m_music_model->GetNinthItem()); } break; - case 1: + case Page_List: { - wxASSERT(!m_ctrl[1] && !m_list_model); - m_ctrl[1] = new wxDataViewCtrl( parent, ID_ATTR_CTRL, wxDefaultPosition, + m_ctrl[Page_List] = new wxDataViewCtrl( parent, ID_ATTR_CTRL, wxDefaultPosition, wxDefaultSize, style ); m_list_model = new MyListModel; - m_ctrl[1]->AssociateModel( m_list_model.get() ); + m_ctrl[Page_List]->AssociateModel( m_list_model.get() ); - m_ctrl[1]->AppendToggleColumn(L"\u2714", + m_ctrl[Page_List]->AppendToggleColumn(L"\u2714", MyListModel::Col_Toggle, wxDATAVIEW_CELL_ACTIVATABLE, wxCOL_WIDTH_AUTOSIZE); // the various columns - m_ctrl[1]->AppendTextColumn("editable string", + m_ctrl[Page_List]->AppendTextColumn("editable string", MyListModel::Col_EditableText, wxDATAVIEW_CELL_EDITABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); - m_ctrl[1]->AppendIconTextColumn("icon", + m_ctrl[Page_List]->AppendIconTextColumn("icon", MyListModel::Col_IconText, wxDATAVIEW_CELL_EDITABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE); - m_ctrl[1]->AppendDateColumn("date", + m_ctrl[Page_List]->AppendDateColumn("date", MyListModel::Col_Date); wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); @@ -818,9 +824,9 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxCOL_WIDTH_AUTOSIZE, wxALIGN_RIGHT, wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); - m_ctrl[1]->AppendColumn( m_attributes ); + m_ctrl[Page_List]->AppendColumn( m_attributes ); - m_ctrl[1]->AppendColumn( + m_ctrl[Page_List]->AppendColumn( new wxDataViewColumn("custom renderer", new MyCustomRenderer(wxDATAVIEW_CELL_EDITABLE), MyListModel::Col_Custom) @@ -828,13 +834,12 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l } break; - case 2: + case Page_ListStore: { - wxASSERT(!m_ctrl[2]); wxDataViewListCtrl* lc = new wxDataViewListCtrl( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style ); - m_ctrl[2] = lc; + m_ctrl[Page_ListStore] = lc; MyListStoreDerivedModel* page2_model = new MyListStoreDerivedModel(); lc->AssociateModel(page2_model); @@ -871,13 +876,12 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l } break; - case 3: + case Page_TreeStore: { - wxASSERT(!m_ctrl[3]); wxDataViewTreeCtrl* tc = new wxDataViewTreeCtrl( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style | wxDV_NO_HEADER ); - m_ctrl[3] = tc; + m_ctrl[Page_TreeStore] = tc; wxImageList *ilist = new wxImageList( 16, 16 ); ilist->Add( wxIcon(wx_small_xpm) ); @@ -898,15 +902,14 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l } break; - case 4: + case Page_VarHeight: { - wxASSERT(!m_ctrl[4] && !m_long_music_model); - m_ctrl[4] = + m_ctrl[Page_VarHeight] = new wxDataViewCtrl( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style | wxDV_VARIABLE_LINE_HEIGHT ); m_long_music_model = new MyLongMusicTreeModel; - m_ctrl[4]->AssociateModel(m_long_music_model.get()); + m_ctrl[Page_VarHeight]->AssociateModel(m_long_music_model.get()); // column 0 of the view control: MultiLineCustomRenderer *tr = @@ -914,7 +917,7 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxDataViewColumn *column0 = new wxDataViewColumn("title", tr, 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE); - m_ctrl[4]->AppendColumn(column0); + m_ctrl[Page_VarHeight]->AppendColumn(column0); // column 1 of the view control: tr = new MultiLineCustomRenderer(); @@ -923,7 +926,7 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE); column1->SetMinWidth(150); // this column can't be resized to be smaller - m_ctrl[4]->AppendColumn(column1); + m_ctrl[Page_VarHeight]->AppendColumn(column1); } break; } @@ -1202,7 +1205,7 @@ void MyFrame::OnAddMozart( wxCommandEvent& WXUNUSED(event) ) void MyFrame::DeleteSelectedItems() { wxDataViewItemArray items; - int len = m_ctrl[0]->GetSelections( items ); + int len = m_ctrl[Page_Music]->GetSelections( items ); for( int i = 0; i < len; i ++ ) if (items[i].IsOk()) m_music_model->Delete( items[i] ); @@ -1215,7 +1218,7 @@ void MyFrame::OnDeleteSelected( wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnDeleteYear( wxCommandEvent& WXUNUSED(event) ) { - m_ctrl[0]->DeleteColumn( m_ctrl[0]->GetColumn( 2 ) ); + m_ctrl[Page_Music]->DeleteColumn( m_ctrl[Page_Music]->GetColumn( 2 ) ); FindWindow( ID_DELETE_YEAR )->Disable(); } @@ -1227,26 +1230,26 @@ void MyFrame::OnSelectNinth( wxCommandEvent& WXUNUSED(event) ) return; } - m_ctrl[0]->Select( m_music_model->GetNinthItem() ); + m_ctrl[Page_Music]->Select( m_music_model->GetNinthItem() ); } void MyFrame::OnCollapse( wxCommandEvent& WXUNUSED(event) ) { - wxDataViewItem item = m_ctrl[0]->GetSelection(); + wxDataViewItem item = m_ctrl[Page_Music]->GetSelection(); if (item.IsOk()) - m_ctrl[0]->Collapse( item ); + m_ctrl[Page_Music]->Collapse( item ); } void MyFrame::OnExpand( wxCommandEvent& WXUNUSED(event) ) { - wxDataViewItem item = m_ctrl[0]->GetSelection(); + wxDataViewItem item = m_ctrl[Page_Music]->GetSelection(); if (item.IsOk()) - m_ctrl[0]->Expand( item ); + m_ctrl[Page_Music]->Expand( item ); } void MyFrame::OnShowCurrent(wxCommandEvent& WXUNUSED(event)) { - wxDataViewItem item = m_ctrl[0]->GetCurrentItem(); + wxDataViewItem item = m_ctrl[Page_Music]->GetCurrentItem(); if ( item.IsOk() ) { wxLogMessage("Current item: \"%s\" by %s", @@ -1258,11 +1261,11 @@ void MyFrame::OnShowCurrent(wxCommandEvent& WXUNUSED(event)) wxLogMessage("There is no current item."); } - wxDataViewColumn *col = m_ctrl[0]->GetCurrentColumn(); + wxDataViewColumn *col = m_ctrl[Page_Music]->GetCurrentColumn(); if ( col ) { wxLogMessage("Current column: %d", - m_ctrl[0]->GetColumnPosition(col)); + m_ctrl[Page_Music]->GetColumnPosition(col)); } else { @@ -1279,7 +1282,7 @@ void MyFrame::OnSetNinthCurrent(wxCommandEvent& WXUNUSED(event)) return; } - m_ctrl[0]->SetCurrentItem(item); + m_ctrl[Page_Music]->SetCurrentItem(item); } void MyFrame::OnChangeNinthTitle(wxCommandEvent& WXUNUSED(event)) @@ -1308,7 +1311,7 @@ void MyFrame::OnActivated( wxDataViewEvent &event ) wxLogMessage( "wxEVT_DATAVIEW_ITEM_ACTIVATED, Item: %s; Column: %d", title, event.GetColumn() ); - if (m_ctrl[0]->IsExpanded( event.GetItem() )) + if (m_ctrl[Page_Music]->IsExpanded( event.GetItem() )) { wxLogMessage( "Item: %s is expanded", title ); } @@ -1390,7 +1393,7 @@ void MyFrame::OnContextMenu( wxDataViewEvent &event ) menu.Append( 2, "menuitem 2" ); menu.Append( 3, "menuitem 3" ); - m_ctrl[0]->PopupMenu(&menu); + m_ctrl[Page_Music]->PopupMenu(&menu); } void MyFrame::OnAttrHeaderClick( wxDataViewEvent &event ) @@ -1399,7 +1402,7 @@ void MyFrame::OnAttrHeaderClick( wxDataViewEvent &event ) // this column when it is clicked to take place event.Skip(); - int pos = m_ctrl[1]->GetColumnPosition( event.GetDataViewColumn() ); + int pos = m_ctrl[Page_List]->GetColumnPosition( event.GetDataViewColumn() ); wxLogMessage( "wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, Column position: %d", pos ); wxLogMessage( "Column title: %s Column width: %d", event.GetDataViewColumn()->GetTitle(), event.GetDataViewColumn()->GetWidth() ); @@ -1411,7 +1414,7 @@ void MyFrame::OnHeaderClick( wxDataViewEvent &event ) // this column when it is clicked to take place event.Skip(); - int pos = m_ctrl[0]->GetColumnPosition( event.GetDataViewColumn() ); + int pos = m_ctrl[Page_Music]->GetColumnPosition( event.GetDataViewColumn() ); wxLogMessage( "wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, Column position: %d", pos ); wxLogMessage( "Column width: %d", event.GetDataViewColumn()->GetWidth() ); @@ -1419,7 +1422,7 @@ void MyFrame::OnHeaderClick( wxDataViewEvent &event ) void MyFrame::OnHeaderRightClick( wxDataViewEvent &event ) { - int pos = m_ctrl[0]->GetColumnPosition( event.GetDataViewColumn() ); + int pos = m_ctrl[Page_Music]->GetColumnPosition( event.GetDataViewColumn() ); wxLogMessage( "wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, Column position: %d", pos ); } @@ -1439,7 +1442,7 @@ void MyFrame::OnColumnReordered(wxDataViewEvent& event) void MyFrame::OnSortedList( wxDataViewEvent &/*event*/) { - wxVector const columns = m_ctrl[1]->GetSortingColumns(); + wxVector const columns = m_ctrl[Page_List]->GetSortingColumns(); wxLogMessage( "wxEVT_DATAVIEW_COLUMN_SORTED using the following columns"); for ( wxVector::const_iterator it = columns.begin(), @@ -1460,14 +1463,14 @@ void MyFrame::OnHeaderClickList( wxDataViewEvent &event ) { // Use control+click to toggle sorting by this column. if ( wxGetKeyState(WXK_CONTROL) ) - m_ctrl[1]->ToggleSortByColumn(event.GetColumn()); + m_ctrl[Page_List]->ToggleSortByColumn(event.GetColumn()); else event.Skip(); } void MyFrame::OnSorted( wxDataViewEvent &event ) { - int pos = m_ctrl[0]->GetColumnPosition( event.GetDataViewColumn() ); + int pos = m_ctrl[Page_Music]->GetColumnPosition( event.GetDataViewColumn() ); wxLogMessage( "wxEVT_DATAVIEW_COLUMN_SORTED, Column position: %d", pos ); } @@ -1492,7 +1495,7 @@ void MyFrame::OnPrependList( wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnDeleteList( wxCommandEvent& WXUNUSED(event) ) { wxDataViewItemArray items; - int len = m_ctrl[1]->GetSelections( items ); + int len = m_ctrl[Page_List]->GetSelections( items ); if (len > 0) m_list_model->DeleteItems( items ); } @@ -1500,7 +1503,7 @@ void MyFrame::OnDeleteList( wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnGoto(wxCommandEvent& WXUNUSED(event)) { wxDataViewItem item = m_list_model->GetItem( 50 ); - m_ctrl[1]->EnsureVisible(item,m_col); + m_ctrl[Page_List]->EnsureVisible(item,m_col); } void MyFrame::OnAddMany(wxCommandEvent& WXUNUSED(event)) @@ -1531,7 +1534,7 @@ void MyFrame::OnListValueChanged(wxDataViewEvent& event) return; } - wxDataViewListCtrl* const lc = static_cast(m_ctrl[2]); + wxDataViewListCtrl* const lc = static_cast(m_ctrl[Page_ListStore]); const int columnToggle = 1; @@ -1566,7 +1569,7 @@ void MyFrame::OnListValueChanged(wxDataViewEvent& event) void MyFrame::OnDeleteTreeItem(wxCommandEvent& WXUNUSED(event)) { - wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[3]; + wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[Page_TreeStore]; wxDataViewItem selected = ctrl->GetSelection(); if (!selected.IsOk()) return; @@ -1576,13 +1579,13 @@ void MyFrame::OnDeleteTreeItem(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnDeleteAllTreeItems(wxCommandEvent& WXUNUSED(event)) { - wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[3]; + wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[Page_TreeStore]; ctrl->DeleteAllItems(); } void MyFrame::OnAddTreeItem(wxCommandEvent& WXUNUSED(event)) { - wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[3]; + wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[Page_TreeStore]; wxDataViewItem selected = ctrl->GetSelection(); if (ctrl->IsContainer(selected)) { wxDataViewItem newitem = ctrl->AppendItem( selected, "Item", 0 ); @@ -1593,7 +1596,7 @@ void MyFrame::OnAddTreeItem(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnAddTreeContainerItem(wxCommandEvent& WXUNUSED(event)) { - wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[3]; + wxDataViewTreeCtrl* ctrl = (wxDataViewTreeCtrl*) m_ctrl[Page_TreeStore]; wxDataViewItem selected = ctrl->GetSelection(); if (ctrl->IsContainer(selected)) ctrl->AppendContainer(selected, "Container", 0 ); @@ -1601,13 +1604,13 @@ void MyFrame::OnAddTreeContainerItem(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnMultipleSort( wxCommandEvent &event ) { - if ( !m_ctrl[1]->AllowMultiColumnSort(event.IsChecked()) ) + if ( !m_ctrl[Page_List]->AllowMultiColumnSort(event.IsChecked()) ) wxLogMessage("Sorting by multiple columns not supported"); } void MyFrame::OnSortByFirstColumn(wxCommandEvent& event) { - wxDataViewColumn* const col = m_ctrl[1]->GetColumn(0); + wxDataViewColumn* const col = m_ctrl[Page_List]->GetColumn(0); if ( event.IsChecked() ) col->SetSortOrder(true /* ascending */); else From eca006da06293727f99f6c529f4c55b306d886af Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 18:31:42 +0200 Subject: [PATCH 2/9] Indent "case Page_VarHeight" properly No real changes, just reindent the code. --- samples/dataview/dataview.cpp | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index 8198d29cb1..8c5065aed8 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -903,32 +903,32 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l break; case Page_VarHeight: - { - m_ctrl[Page_VarHeight] = - new wxDataViewCtrl( parent, wxID_ANY, wxDefaultPosition, - wxDefaultSize, style | wxDV_VARIABLE_LINE_HEIGHT ); + { + m_ctrl[Page_VarHeight] = + new wxDataViewCtrl( parent, wxID_ANY, wxDefaultPosition, + wxDefaultSize, style | wxDV_VARIABLE_LINE_HEIGHT ); - m_long_music_model = new MyLongMusicTreeModel; - m_ctrl[Page_VarHeight]->AssociateModel(m_long_music_model.get()); + m_long_music_model = new MyLongMusicTreeModel; + m_ctrl[Page_VarHeight]->AssociateModel(m_long_music_model.get()); - // column 0 of the view control: - MultiLineCustomRenderer *tr = - new MultiLineCustomRenderer(); - wxDataViewColumn *column0 = - new wxDataViewColumn("title", tr, 0, 200, wxALIGN_LEFT, - wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE); - m_ctrl[Page_VarHeight]->AppendColumn(column0); + // column 0 of the view control: + MultiLineCustomRenderer *tr = + new MultiLineCustomRenderer(); + wxDataViewColumn *column0 = + new wxDataViewColumn("title", tr, 0, 200, wxALIGN_LEFT, + wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE); + m_ctrl[Page_VarHeight]->AppendColumn(column0); - // column 1 of the view control: - tr = new MultiLineCustomRenderer(); - wxDataViewColumn *column1 = - new wxDataViewColumn("artist", tr, 1, 150, wxALIGN_LEFT, - wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE | - wxDATAVIEW_COL_RESIZABLE); - column1->SetMinWidth(150); // this column can't be resized to be smaller - m_ctrl[Page_VarHeight]->AppendColumn(column1); - } - break; + // column 1 of the view control: + tr = new MultiLineCustomRenderer(); + wxDataViewColumn *column1 = + new wxDataViewColumn("artist", tr, 1, 150, wxALIGN_LEFT, + wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE | + wxDATAVIEW_COL_RESIZABLE); + column1->SetMinWidth(150); // this column can't be resized to be smaller + m_ctrl[Page_VarHeight]->AppendColumn(column1); + } + break; } } From f99b80612af934f090d358ce00f7c342557d76c4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 18:51:36 +0200 Subject: [PATCH 3/9] Add a simple test of wxDataViewIndexListModel to the sample This is useful to investigate inconsistencies in its behaviour between the generic and the native GTK versions that can't be easily checked in the automatic unit tests. --- samples/dataview/dataview.cpp | 73 +++++++++++++++++++++++++++++++++-- samples/dataview/mymodels.h | 35 +++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index 8c5065aed8..cb6023bd58 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -118,6 +118,9 @@ private: void OnAddTreeItem(wxCommandEvent& event); void OnAddTreeContainerItem(wxCommandEvent& event); + void OnIndexListUseEnglish(wxCommandEvent&) { FillIndexList(Lang_English); } + void OnIndexListUseFrench(wxCommandEvent&) { FillIndexList(Lang_French); } + void OnValueChanged( wxDataViewEvent &event ); void OnActivated( wxDataViewEvent &event ); @@ -160,6 +163,11 @@ private: // helper used by both OnDeleteSelected() and OnDataViewChar() void DeleteSelectedItems(); + // helper for the index list model fills the model with the weekday names + // in the specified language + enum Lang { Lang_English, Lang_French }; + void FillIndexList(Lang lang); + wxNotebook* m_notebook; @@ -171,16 +179,18 @@ private: Page_ListStore, Page_TreeStore, Page_VarHeight, + Page_IndexList, Page_Max }; wxDataViewCtrl* m_ctrl[Page_Max]; - // the models associated with the first two DVC: + // Some of the models associated with the controls: wxObjectDataPtr m_music_model; wxObjectDataPtr m_long_music_model; wxObjectDataPtr m_list_model; + wxObjectDataPtr m_index_list_model; // other data: @@ -427,7 +437,11 @@ enum ID_DELETE_TREE_ITEM = 400, ID_DELETE_ALL_TREE_ITEMS = 401, ID_ADD_TREE_ITEM = 402, - ID_ADD_TREE_CONTAINER_ITEM = 403 + ID_ADD_TREE_CONTAINER_ITEM = 403, + + // Index list model page + ID_INDEX_LIST_USE_ENGLISH = 500, + ID_INDEX_LIST_USE_FRENCH }; wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) @@ -474,6 +488,9 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_BUTTON( ID_ADD_TREE_ITEM, MyFrame::OnAddTreeItem ) EVT_BUTTON( ID_ADD_TREE_CONTAINER_ITEM, MyFrame::OnAddTreeContainerItem ) + EVT_BUTTON( ID_INDEX_LIST_USE_ENGLISH, MyFrame::OnIndexListUseEnglish ) + EVT_BUTTON( ID_INDEX_LIST_USE_FRENCH, MyFrame::OnIndexListUseFrench ) + EVT_DATAVIEW_ITEM_VALUE_CHANGED( ID_MUSIC_CTRL, MyFrame::OnValueChanged ) EVT_DATAVIEW_ITEM_ACTIVATED(ID_MUSIC_CTRL, MyFrame::OnActivated ) @@ -672,6 +689,24 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int fifthPanelSz->Add(m_ctrl[Page_VarHeight], 1, wxGROW | wxALL, 5); fifthPanel->SetSizerAndFit(fifthPanelSz); + // page showing the indexed list model + // ----------------------------------- + + wxPanel* sixthPanel = new wxPanel(m_notebook, wxID_ANY); + + BuildDataViewCtrl(sixthPanel, Page_IndexList); + + wxBoxSizer *button_sizer6 = new wxBoxSizer(wxHORIZONTAL); + button_sizer6->Add(new wxButton(sixthPanel, ID_INDEX_LIST_USE_ENGLISH, "&English"), + wxSizerFlags().DoubleBorder()); + button_sizer6->Add(new wxButton(sixthPanel, ID_INDEX_LIST_USE_FRENCH, "&French"), + wxSizerFlags().DoubleBorder()); + + wxSizer *sixthPanelSz = new wxBoxSizer(wxVERTICAL); + sixthPanelSz->Add(m_ctrl[Page_IndexList], wxSizerFlags(1).Expand().Border()); + sixthPanelSz->Add(button_sizer6); + sixthPanel->SetSizerAndFit(sixthPanelSz); + // complete GUI // ------------ @@ -680,7 +715,8 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int m_notebook->AddPage(secondPanel, "MyListModel"); m_notebook->AddPage(thirdPanel, "wxDataViewListCtrl"); m_notebook->AddPage(fourthPanel, "wxDataViewTreeCtrl"); - m_notebook->AddPage(fifthPanel, "wxDataViewTreeCtrl Variable line height"); + m_notebook->AddPage(fifthPanel, "Variable line height"); + m_notebook->AddPage(sixthPanel, "MyIndexListModel"); wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); @@ -929,6 +965,21 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l m_ctrl[Page_VarHeight]->AppendColumn(column1); } break; + + case Page_IndexList: + { + m_ctrl[Page_IndexList] = new wxDataViewCtrl(parent, wxID_ANY, + wxDefaultPosition, + wxDefaultSize, + style); + + m_index_list_model = new MyIndexListModel; + m_ctrl[Page_IndexList]->AssociateModel(m_index_list_model.get()); + m_ctrl[Page_IndexList]->AppendTextColumn("String", 0); + + FillIndexList(Lang_English); + } + break; } } @@ -1616,3 +1667,19 @@ void MyFrame::OnSortByFirstColumn(wxCommandEvent& event) else col->UnsetAsSortKey(); } + +// ---------------------------------------------------------------------------- +// Index list model page +// ---------------------------------------------------------------------------- + +void MyFrame::FillIndexList(Lang lang) +{ + const int DAYS_PER_WEEK = 7; + const wxString weekdays[2][DAYS_PER_WEEK] = + { + { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }, + { "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim" }, + }; + + m_index_list_model->Fill(wxArrayString(DAYS_PER_WEEK, weekdays[lang])); +} diff --git a/samples/dataview/mymodels.h b/samples/dataview/mymodels.h index db089197a9..3a7f5735da 100644 --- a/samples/dataview/mymodels.h +++ b/samples/dataview/mymodels.h @@ -269,3 +269,38 @@ class MyListStoreDerivedModel : public wxDataViewListStore public: virtual bool IsEnabledByRow(unsigned int row, unsigned int col) const wxOVERRIDE; }; + +// ---------------------------------------------------------------------------- +// MyIndexListModel +// ---------------------------------------------------------------------------- + +class MyIndexListModel : public wxDataViewIndexListModel +{ +public: + MyIndexListModel() { } + + void Fill(const wxArrayString& strings) + { + m_strings = strings; + + Reset(m_strings.size()); + } + + // Implement base class pure virtual methods. + unsigned GetColumnCount() const wxOVERRIDE { return 1; } + wxString GetColumnType(unsigned) const wxOVERRIDE { return "string"; } + unsigned GetCount() const wxOVERRIDE { return m_strings.size(); } + void GetValueByRow(wxVariant& val, unsigned row, unsigned) const wxOVERRIDE + { + val = m_strings[row]; + } + bool SetValueByRow(const wxVariant&, unsigned, unsigned) wxOVERRIDE + { + return false; + } + +private: + wxArrayString m_strings; + + wxDECLARE_NO_COPY_CLASS(MyIndexListModel); +}; From 0d005bdf0224f76c546353ca45711acd8120a7af Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 22:58:51 +0200 Subject: [PATCH 4/9] Add wxEVT_DATAVIEW_SELECTION_CHANGED test handler to the sample Verify that the sample is not invoked unexpectedly. --- samples/dataview/dataview.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index cb6023bd58..58255a0c18 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -120,6 +120,7 @@ private: void OnIndexListUseEnglish(wxCommandEvent&) { FillIndexList(Lang_English); } void OnIndexListUseFrench(wxCommandEvent&) { FillIndexList(Lang_French); } + void OnIndexListSelectionChanged(wxDataViewEvent& event); void OnValueChanged( wxDataViewEvent &event ); @@ -978,6 +979,10 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l m_ctrl[Page_IndexList]->AppendTextColumn("String", 0); FillIndexList(Lang_English); + + m_ctrl[Page_IndexList]->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, + &MyFrame::OnIndexListSelectionChanged, + this); } break; } @@ -1683,3 +1688,24 @@ void MyFrame::FillIndexList(Lang lang) m_index_list_model->Fill(wxArrayString(DAYS_PER_WEEK, weekdays[lang])); } + +void MyFrame::OnIndexListSelectionChanged(wxDataViewEvent& event) +{ + // We don't expect any events during the control destruction. + wxASSERT( !m_ctrl[Page_IndexList]->IsBeingDeleted() ); + + wxString weekday; + wxDataViewItem item = event.GetItem(); + if ( !item ) + { + weekday = "[none]"; + } + else + { + wxVariant val; + m_index_list_model->GetValue(val, item, 0); + weekday = val.GetString(); + } + + wxLogMessage("Selected week day: %s", weekday); +} From 5403ec4e086c4fbc3d9ee9bf4b38964a48357826 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 23:08:10 +0200 Subject: [PATCH 5/9] Make Cleared() behave consistently with the other ports in wxGTK Instead of actually deleting all the items from the control, just refresh it by resetting the model, as this is what Cleared() does in both the generic and native macOS versions of wxDataViewCtrl, so calling it there resulted in very different results from doing it under wxGTK, where instead of refreshing the control contents it raelly cleared it. The name of this method is unfortunately confusing, but it seems better to change its behaviour in wxGTK, even if this doesn't match the name, rather than change it in the other ports to make them do the same thing, as this could break some currently working code. Also, this change results in a welcome code simplification. --- src/gtk/dataview.cpp | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index c46551b9ca..9aadc4c418 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -1895,35 +1895,7 @@ bool wxGtkDataViewModelNotifier::AfterReset() bool wxGtkDataViewModelNotifier::Cleared() { - GtkWxTreeModel *wxgtk_model = m_internal->GetGtkModel(); - - // There is no call to tell the model that everything - // has been deleted so call row_deleted() for every - // child of root... - - // It is important to avoid selection changed events being generated from - // here as they would reference the already deleted model items, which - // would result in crashes in any code attempting to handle these events. - wxDataViewCtrl::SelectionEventsSuppressor noSelection(m_internal->GetOwner()); - - // We also need to prevent wxGtkTreeCellDataFunc 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 = wxgtk_model->stamp; - wxgtk_model->stamp = 0; - - { - wxGtkTreePath path(gtk_tree_path_new_first()); // points to root - const int count = m_internal->iter_n_children( NULL ); // number of children of root - for (int i = 0; i < count; i++) - gtk_tree_model_row_deleted( GTK_TREE_MODEL(wxgtk_model), path ); - } - - wxgtk_model->stamp = stampOrig; - - m_internal->Cleared(); - - return true; + return BeforeReset() && AfterReset(); } // --------------------------------------------------------- From 18594afe76efdadd1dd5d8a84fa7cafc9004ce62 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 23:13:11 +0200 Subject: [PATCH 6/9] Remove checks for model stamp validity not needed any longer The code setting the model stamp to 0 was removed in the last commit, so also remove the code checking for stamp being 0. --- src/gtk/dataview.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 9aadc4c418..79d534c1bd 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -754,13 +754,6 @@ wxgtk_tree_model_get_path (GtkTreeModel *tree_model, g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (tree_model), NULL); GtkWxTreeModel *wxtree_model = GTK_WX_TREE_MODEL (tree_model); - if ( wxtree_model->stamp == 0 ) - { - // The model is temporarily invalid and can't be used, see Cleared(), - // but we need to return some valid path from here -- just return an - // empty one. - return gtk_tree_path_new(); - } g_return_val_if_fail (iter->stamp == wxtree_model->stamp, NULL); @@ -799,9 +792,6 @@ wxgtk_tree_model_iter_next (GtkTreeModel *tree_model, { GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model; - // This happens when clearing the view by calling .._set_model( NULL ); - if (iter->stamp == 0) return FALSE; - g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE); g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE); @@ -3076,13 +3066,6 @@ 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 - // wxGtkDataViewModelNotifier::Cleared(). - return; - } - wxDataViewRenderer *cell = (wxDataViewRenderer*) data; wxDataViewItem item( (void*) iter->user_data ); From 063b6edf683a5cd08f19afb41df8a331bcc958c1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 23:20:20 +0200 Subject: [PATCH 7/9] Add a check for resetting the associated model to the sample Verify that resetting the model doesn't result in selection change events. --- samples/dataview/dataview.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index 58255a0c18..c15d582cca 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -120,6 +120,7 @@ private: void OnIndexListUseEnglish(wxCommandEvent&) { FillIndexList(Lang_English); } void OnIndexListUseFrench(wxCommandEvent&) { FillIndexList(Lang_French); } + void OnIndexListResetModel(wxCommandEvent& event); void OnIndexListSelectionChanged(wxDataViewEvent& event); void OnValueChanged( wxDataViewEvent &event ); @@ -442,7 +443,8 @@ enum // Index list model page ID_INDEX_LIST_USE_ENGLISH = 500, - ID_INDEX_LIST_USE_FRENCH + ID_INDEX_LIST_USE_FRENCH, + ID_INDEX_LIST_RESET_MODEL }; wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) @@ -491,6 +493,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_BUTTON( ID_INDEX_LIST_USE_ENGLISH, MyFrame::OnIndexListUseEnglish ) EVT_BUTTON( ID_INDEX_LIST_USE_FRENCH, MyFrame::OnIndexListUseFrench ) + EVT_BUTTON( ID_INDEX_LIST_RESET_MODEL, MyFrame::OnIndexListResetModel ) EVT_DATAVIEW_ITEM_VALUE_CHANGED( ID_MUSIC_CTRL, MyFrame::OnValueChanged ) @@ -702,6 +705,8 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int wxSizerFlags().DoubleBorder()); button_sizer6->Add(new wxButton(sixthPanel, ID_INDEX_LIST_USE_FRENCH, "&French"), wxSizerFlags().DoubleBorder()); + button_sizer6->Add(new wxButton(sixthPanel, ID_INDEX_LIST_RESET_MODEL, "Reset &model"), + wxSizerFlags().DoubleBorder()); wxSizer *sixthPanelSz = new wxBoxSizer(wxVERTICAL); sixthPanelSz->Add(m_ctrl[Page_IndexList], wxSizerFlags(1).Expand().Border()); @@ -1689,6 +1694,12 @@ void MyFrame::FillIndexList(Lang lang) m_index_list_model->Fill(wxArrayString(DAYS_PER_WEEK, weekdays[lang])); } +void MyFrame::OnIndexListResetModel(wxCommandEvent&) +{ + m_ctrl[Page_IndexList]->AssociateModel(NULL); + m_ctrl[Page_IndexList]->AssociateModel(m_index_list_model.get()); +} + void MyFrame::OnIndexListSelectionChanged(wxDataViewEvent& event) { // We don't expect any events during the control destruction. From 54a8d1d8f370c6bc8516aca06da26933c9c2819e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 23:36:20 +0200 Subject: [PATCH 8/9] Don't send events when changing model in wxGTK wxDataViewCtrl Sending events from e.g. AssociateModel(NULL) made the GTK version inconsistent with the other ones, neither of which sent any events in this case, and could result in a lot of grief in the user code if it didn't expect the event handler to be called at this moment (e.g. during the destruction). Make wxGTK compatible with the other ports and safer by always disabling the selection changed events before calling gtk_tree_view_set_model(). Note that it's still incompatible with the other ports because they also preserve the selection even after the change of model, but wxGTK loses it. Ideally this would be fixed too, but for now live with this as the lesser evil. --- src/gtk/dataview.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 79d534c1bd..62ebd65121 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -250,6 +250,11 @@ public: } + // Associate our model with the tree view or disassociate it from it + // without generating any selection changed events, unlike + // gtk_tree_view_set_model() that this function wraps. + void UseModel(bool use); + // accessors wxDataViewModel* GetDataViewModel() { return m_wx_model; } const wxDataViewModel* GetDataViewModel() const { return m_wx_model; } @@ -1865,20 +1870,16 @@ bool wxGtkDataViewModelNotifier::ValueChanged( const wxDataViewItem &item, unsig bool wxGtkDataViewModelNotifier::BeforeReset() { - GtkWidget *treeview = m_internal->GetOwner()->GtkGetTreeView(); - gtk_tree_view_set_model( GTK_TREE_VIEW(treeview), NULL ); + m_internal->UseModel(false); return true; } bool wxGtkDataViewModelNotifier::AfterReset() { - GtkWidget *treeview = m_internal->GetOwner()->GtkGetTreeView(); - GtkWxTreeModel *wxgtk_model = m_internal->GetGtkModel(); - m_internal->Cleared(); - gtk_tree_view_set_model( GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(wxgtk_model) ); + m_internal->UseModel(true); return true; } @@ -3551,7 +3552,7 @@ wxDataViewCtrlInternal::wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataVie if (!m_wx_model->IsVirtualListModel()) InitTree(); - gtk_tree_view_set_model( GTK_TREE_VIEW(m_owner->GtkGetTreeView()), GTK_TREE_MODEL(m_gtk_model) ); + UseModel(true); } wxDataViewCtrlInternal::~wxDataViewCtrlInternal() @@ -3559,7 +3560,7 @@ wxDataViewCtrlInternal::~wxDataViewCtrlInternal() m_wx_model->RemoveNotifier( m_notifier ); // remove the model from the GtkTreeView before it gets destroyed - gtk_tree_view_set_model( GTK_TREE_VIEW( m_owner->GtkGetTreeView() ), NULL ); + UseModel(false); g_object_unref( m_gtk_model ); @@ -3568,6 +3569,18 @@ wxDataViewCtrlInternal::~wxDataViewCtrlInternal() delete m_dropDataObject; } +void wxDataViewCtrlInternal::UseModel(bool use) +{ + // Avoid any selection changed events from gtk_tree_view_set_model() call + // below as they don't happen under the other platforms and can be + // unexpected with the possibly fatal consequences for the user-defined + // 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 ); +} + void wxDataViewCtrlInternal::ScheduleRefresh() { m_dirty = true; From 59e9da28f5cecc0d3b77d1312ff4dae77a965989 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Sep 2019 23:43:42 +0200 Subject: [PATCH 9/9] Improve wxDataViewModel::Cleared() documentation Explain that this method can be useful not only when the model is cleared. --- interface/wx/dataview.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index 90bb1c2aaf..70c59e7c49 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -52,6 +52,10 @@ - wxDataViewModel::ItemsDeleted, - wxDataViewModel::ItemsChanged. + Note that Cleared() can be called for all changes involving many, or all, + of the model items and not only for deleting all of them (i.e. clearing the + model). + This class maintains a list of wxDataViewModelNotifier which link this class to the specific implementations on the supported platforms so that e.g. calling wxDataViewModel::ValueChanged on this model will just call @@ -129,8 +133,16 @@ public: unsigned int col); /** - Called to inform the model that all data has been cleared. - The control will reread the data from the model again. + Called to inform the model that all of its data has been changed. + + This method should be called if so many of the model items have + changed, that the control should just reread all of them, repopulating + itself entirely. + + Note that, contrary to the name of the method, it doesn't necessarily + indicate that model has become empty -- although this is the right + method to call, rather than ItemsDeleted(), if it was indeed cleared, + which explains the origin of its name. */ bool Cleared();