Merge branch 'dvc-inconsist-fixes'

Fix several inconsistencies in wxDVC between wxGTK and the other
implementations.

See https://github.com/wxWidgets/wxWidgets/pull/1547
This commit is contained in:
Vadim Zeitlin
2019-09-18 14:33:34 +02:00
4 changed files with 281 additions and 159 deletions

View File

@@ -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();

View File

@@ -118,6 +118,11 @@ private:
void OnAddTreeItem(wxCommandEvent& event);
void OnAddTreeContainerItem(wxCommandEvent& event);
void OnIndexListUseEnglish(wxCommandEvent&) { FillIndexList(Lang_English); }
void OnIndexListUseFrench(wxCommandEvent&) { FillIndexList(Lang_French); }
void OnIndexListResetModel(wxCommandEvent& event);
void OnIndexListSelectionChanged(wxDataViewEvent& event);
void OnValueChanged( wxDataViewEvent &event );
void OnActivated( wxDataViewEvent &event );
@@ -160,18 +165,34 @@ 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;
// the controls stored in the various tabs of the main notebook:
enum Page
{
Page_Music,
Page_List,
Page_ListStore,
Page_TreeStore,
Page_VarHeight,
Page_IndexList,
Page_Max
};
wxDataViewCtrl* m_ctrl[5];
wxDataViewCtrl* m_ctrl[Page_Max];
// the models associated with the first two DVC:
// Some of the models associated with the controls:
wxObjectDataPtr<MyMusicTreeModel> m_music_model;
wxObjectDataPtr<MyLongMusicTreeModel> m_long_music_model;
wxObjectDataPtr<MyListModel> m_list_model;
wxObjectDataPtr<MyIndexListModel> m_index_list_model;
// other data:
@@ -418,7 +439,12 @@ 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,
ID_INDEX_LIST_RESET_MODEL
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
@@ -465,6 +491,10 @@ 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_BUTTON( ID_INDEX_LIST_RESET_MODEL, MyFrame::OnIndexListResetModel )
EVT_DATAVIEW_ITEM_VALUE_CHANGED( ID_MUSIC_CTRL, MyFrame::OnValueChanged )
EVT_DATAVIEW_ITEM_ACTIVATED(ID_MUSIC_CTRL, MyFrame::OnActivated )
@@ -503,11 +533,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 +593,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 +614,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 +629,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 +646,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 +657,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 +669,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 +678,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,12 +687,32 @@ 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);
// 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());
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());
sixthPanelSz->Add(button_sizer6);
sixthPanel->SetSizerAndFit(sixthPanelSz);
// complete GUI
// ------------
@@ -674,7 +721,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);
@@ -691,22 +739,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 +765,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 +780,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 +790,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 +804,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 +817,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 +866,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 +876,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 +918,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,34 +944,52 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l
}
break;
case 4:
{
wxASSERT(!m_ctrl[4] && !m_long_music_model);
m_ctrl[4] =
new wxDataViewCtrl( parent, wxID_ANY, wxDefaultPosition,
wxDefaultSize, style | wxDV_VARIABLE_LINE_HEIGHT );
case Page_VarHeight:
{
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_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[4]->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[4]->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;
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);
m_ctrl[Page_IndexList]->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED,
&MyFrame::OnIndexListSelectionChanged,
this);
}
break;
}
}
@@ -1202,7 +1266,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 +1279,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 +1291,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 +1322,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 +1343,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 +1372,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 +1454,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 +1463,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 +1475,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 +1483,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 +1503,7 @@ void MyFrame::OnColumnReordered(wxDataViewEvent& event)
void MyFrame::OnSortedList( wxDataViewEvent &/*event*/)
{
wxVector<wxDataViewColumn *> const columns = m_ctrl[1]->GetSortingColumns();
wxVector<wxDataViewColumn *> const columns = m_ctrl[Page_List]->GetSortingColumns();
wxLogMessage( "wxEVT_DATAVIEW_COLUMN_SORTED using the following columns");
for ( wxVector<wxDataViewColumn *>::const_iterator it = columns.begin(),
@@ -1460,14 +1524,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 +1556,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 +1564,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 +1595,7 @@ void MyFrame::OnListValueChanged(wxDataViewEvent& event)
return;
}
wxDataViewListCtrl* const lc = static_cast<wxDataViewListCtrl*>(m_ctrl[2]);
wxDataViewListCtrl* const lc = static_cast<wxDataViewListCtrl*>(m_ctrl[Page_ListStore]);
const int columnToggle = 1;
@@ -1566,7 +1630,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 +1640,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 +1657,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,15 +1665,58 @@ 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
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]));
}
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.
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);
}

View File

@@ -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);
};

View File

@@ -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; }
@@ -754,13 +759,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 +797,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);
@@ -1875,55 +1870,23 @@ 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;
}
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();
}
// ---------------------------------------------------------
@@ -3104,13 +3067,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 );
@@ -3596,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()
@@ -3604,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 );
@@ -3613,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;