-
-
Save aamirglb/bc50b7bb794a84f0a2c871e7e961e993 to your computer and use it in GitHub Desktop.
| #include "wx/wx.h" | |
| #include "wx/dataview.h" | |
| ///////// | |
| // Implement a simple model with 2 columns | |
| // FileName FileSize | |
| ///////// | |
| class DataModelNode; | |
| WX_DEFINE_ARRAY_PTR(DataModelNode *, DataModelNodePtrArray); | |
| class DataModelNode | |
| { | |
| public: | |
| DataModelNode( | |
| DataModelNode *parent, | |
| const wxString &name, | |
| const wxString &size) : m_Parent{parent}, | |
| m_FileName{name}, | |
| m_FileSize{size}, | |
| m_IsContainer{false} | |
| { | |
| } | |
| DataModelNode( | |
| DataModelNode *parent, | |
| const wxString &branch) | |
| : m_Parent{parent}, | |
| m_FileName{branch}, | |
| m_IsContainer{true} | |
| { | |
| } | |
| ~DataModelNode() | |
| { | |
| size_t count = m_Children.GetCount(); | |
| for (size_t i = 0; i < count; ++i) | |
| { | |
| DataModelNode *child = m_Children[i]; | |
| delete child; | |
| } | |
| } | |
| bool IsContainer() const { return m_IsContainer; } | |
| DataModelNode *GetParent() { return m_Parent; } | |
| DataModelNodePtrArray &GetChildren() { return m_Children; } | |
| DataModelNode *GetNthChild(size_t n) { return m_Children.Item(n); } | |
| void Insert(DataModelNode *child, size_t n) { m_Children.Insert(child, n); } | |
| void Append(DataModelNode *child) { m_Children.Add(child); } | |
| size_t GetChildCount() const { return m_Children.GetCount(); } | |
| public: | |
| wxString m_FileName; | |
| wxString m_FileSize; | |
| bool m_IsContainer; | |
| private: | |
| DataModelNode *m_Parent; | |
| DataModelNodePtrArray m_Children; | |
| }; | |
| class DataModel : public wxDataViewModel | |
| { | |
| public: | |
| DataModel() | |
| { | |
| m_Root = new DataModelNode(nullptr, "Root"); | |
| // Add child nodes | |
| AddNewEntry(); | |
| } | |
| ~DataModel() { delete m_Root; } | |
| wxDataViewItem GetRootItem() const | |
| { | |
| wxDataViewItem rootItem{static_cast<void *>(m_Root)}; | |
| return rootItem; | |
| } | |
| const wxDataViewItemArray &GetAllItems() const | |
| { | |
| return m_DataItems; | |
| } | |
| unsigned int GetColumnCount() const override { return 2; }; | |
| wxString GetColumnType(unsigned int col) const override { return "string"; } | |
| wxDataViewItem AddNewEntry(int childCount = 3) | |
| { | |
| wxDateTime now = wxDateTime::Now(); | |
| std::cout << now.Format("%F %T") << std::endl; | |
| wxDataViewItemArray items; | |
| auto *parentNode = new DataModelNode(m_Root, wxString::Format("Directory-%d", ++m_DirCount)); | |
| m_Root->Append(parentNode); | |
| for (int i = 0; i < childCount; ++i) | |
| { | |
| auto *childNode = new DataModelNode(parentNode, | |
| wxString::Format("File-%d", i + 1), | |
| wxString::Format("%d.%d", m_DirCount, i + 1)); | |
| parentNode->Append(childNode); | |
| // items.Add(childNode); | |
| wxDataViewItem child{static_cast<void *>(childNode)}; | |
| wxDataViewItem parent{static_cast<void *>(parentNode)}; | |
| } | |
| Cleared(); | |
| wxDataViewItem parent{static_cast<void *>(parentNode)}; | |
| ItemAdded(GetRootItem(), parent); | |
| m_DataItems.Add(parent); | |
| return parent; | |
| } | |
| wxDataViewItem AddNewChildItem(const wxDataViewItem &selectedItem) | |
| { | |
| wxDataViewItem returnItem{nullptr}; | |
| if (selectedItem.IsOk()) | |
| { | |
| auto modelItem = static_cast<DataModelNode *>(selectedItem.GetID()); | |
| // Can't add child to root item | |
| if (modelItem == m_Root) | |
| { | |
| return wxDataViewItem{nullptr}; | |
| ; | |
| } | |
| auto now = wxDateTime::Now(); | |
| if (modelItem->GetParent() == m_Root) | |
| { | |
| // Add a child to selected root | |
| auto childItem = new DataModelNode(modelItem, wxString::Format("File-%d", m_DirCount), | |
| wxString::Format("%d.%d", m_DirCount, 1)); | |
| modelItem->Append(childItem); | |
| wxDataViewItem child{static_cast<void *>(childItem)}; | |
| ItemAdded(selectedItem, child); | |
| returnItem = wxDataViewItem{modelItem}; | |
| } | |
| else | |
| { | |
| // Add a sibling item | |
| auto parentItem{modelItem->GetParent()}; | |
| auto childItem = new DataModelNode(parentItem, wxString::Format("File-%d", m_DirCount), | |
| wxString::Format("%d.%d", m_DirCount, 1)); | |
| parentItem->Append(childItem); | |
| wxDataViewItem child{static_cast<void *>(childItem)}; | |
| wxDataViewItem parent{static_cast<void *>(parentItem)}; | |
| ItemAdded(parent, child); | |
| returnItem = parent; | |
| } | |
| } | |
| Cleared(); | |
| return returnItem; | |
| } | |
| wxString GetFileName(const wxDataViewItem &item) const | |
| { | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| if (node == nullptr) | |
| return wxEmptyString; | |
| return node->m_FileName; | |
| } | |
| wxString GetFileSize(const wxDataViewItem &item) const | |
| { | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| if (node == nullptr) | |
| return wxEmptyString; | |
| return node->m_FileSize; | |
| } | |
| // Delete Selected item | |
| void Delete(const wxDataViewItem &item) | |
| { | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| wxDataViewItem nodeItem{node}; | |
| if (node == nullptr) | |
| return; | |
| wxDataViewItem parent{node->GetParent()}; | |
| DataModelNode *parentNode = node->GetParent(); | |
| if (!parent.IsOk()) | |
| { | |
| assert(node == m_Root); | |
| std::cout << "Cannot remove root item!!" << std::endl; | |
| return; | |
| } | |
| if (node->GetParent() == m_Root) | |
| { | |
| m_DataItems.Remove(nodeItem); | |
| } | |
| node->GetParent()->GetChildren().Remove(node); | |
| delete node; | |
| ItemDeleted(parent, item); | |
| if (parentNode->GetChildCount() == 0 && parentNode != m_Root) | |
| { | |
| m_Root->GetChildren().Remove(parentNode); | |
| delete parentNode; | |
| wxDataViewItem rootItem{static_cast<void *>(m_Root)}; | |
| m_DataItems.Remove(parent); | |
| ItemDeleted(rootItem, parent); | |
| } | |
| Cleared(); | |
| } | |
| void Clear() | |
| { | |
| while (!m_Root->GetChildren().IsEmpty()) | |
| { | |
| DataModelNode *node = m_Root->GetNthChild(0); | |
| m_Root->GetChildren().Remove(node); | |
| delete node; | |
| } | |
| m_DataItems.Clear(); | |
| Cleared(); | |
| } | |
| int Compare(const wxDataViewItem &item1, const wxDataViewItem &item2, | |
| unsigned int column, bool ascending) const wxOVERRIDE | |
| { | |
| assert(item1.IsOk() && item2.IsOk()); | |
| if (IsContainer(item1) && IsContainer(item2)) | |
| { | |
| wxVariant value1, value2; | |
| GetValue(value1, item1, 0); | |
| GetValue(value2, item2, 0); | |
| wxString str1 = value1.GetString(); | |
| wxString str2 = value2.GetString(); | |
| int result = str1.Cmp(str2); | |
| if (result > 0) | |
| { | |
| return result; | |
| } | |
| wxUIntPtr litem1 = (wxUIntPtr)item1.GetID(); | |
| wxUIntPtr litem2 = (wxUIntPtr)item2.GetID(); | |
| return litem1 - litem2; | |
| } | |
| return wxDataViewModel::Compare(item1, item2, column, ascending); | |
| } | |
| virtual void GetValue(wxVariant &variant, | |
| const wxDataViewItem &item, unsigned int col) const wxOVERRIDE | |
| { | |
| assert(item.IsOk()); | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| switch (col) | |
| { | |
| case 0: | |
| variant = node->m_FileName; | |
| break; | |
| case 1: | |
| variant = node->m_FileSize; | |
| break; | |
| default: | |
| std::cout << "GetValue: wrong column " << col << std::endl; | |
| } | |
| } | |
| virtual bool SetValue(const wxVariant &variant, | |
| const wxDataViewItem &item, unsigned int col) wxOVERRIDE | |
| { | |
| assert(item.IsOk()); | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| switch (col) | |
| { | |
| case 0: | |
| node->m_FileName = variant.GetString(); | |
| break; | |
| case 1: | |
| node->m_FileSize = variant.GetString(); | |
| break; | |
| default: | |
| std::cout << "SetValue: wrong column " << col << std::endl; | |
| } | |
| return false; | |
| } | |
| virtual bool IsEnabled(const wxDataViewItem &item, | |
| unsigned int col) const wxOVERRIDE | |
| { | |
| assert(item.IsOk()); | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| return true; | |
| } | |
| virtual wxDataViewItem GetParent(const wxDataViewItem &item) const wxOVERRIDE | |
| { | |
| if (!item.IsOk()) | |
| return wxDataViewItem(0); | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| if (node == m_Root) | |
| return wxDataViewItem(0); | |
| return wxDataViewItem(static_cast<void *>(node->GetParent())); | |
| } | |
| virtual bool IsContainer(const wxDataViewItem &item) const wxOVERRIDE | |
| { | |
| if (!item.IsOk()) | |
| return true; | |
| DataModelNode *node = static_cast<DataModelNode *>(item.GetID()); | |
| return node->IsContainer(); | |
| } | |
| virtual unsigned int GetChildren(const wxDataViewItem &parent, | |
| wxDataViewItemArray &array) const wxOVERRIDE | |
| { | |
| DataModelNode *node = static_cast<DataModelNode *>(parent.GetID()); | |
| if (node == nullptr) | |
| { | |
| int count = m_Root->GetChildCount(); | |
| for (int i = 0; i < count; ++i) | |
| { | |
| array.Add(wxDataViewItem(static_cast<void *>(m_Root->GetNthChild(i)))); | |
| } | |
| return count; | |
| } | |
| if (node->GetChildCount() == 0) | |
| { | |
| return 0; | |
| } | |
| size_t count = node->GetChildren().GetCount(); | |
| for (size_t i = 0; i < count; ++i) | |
| { | |
| DataModelNode *child = node->GetChildren().Item(i); | |
| array.Add(wxDataViewItem(static_cast<void *>(child))); | |
| } | |
| return count; | |
| } | |
| private: | |
| int m_DirCount{}; | |
| DataModelNode *m_Root{}; | |
| wxDataViewItemArray m_DataItems; | |
| }; | |
| class MyFrame : public wxFrame | |
| { | |
| public: | |
| MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size) | |
| : wxFrame(nullptr, wxID_ANY, title, pos, size) | |
| { | |
| m_MainSizer = new wxBoxSizer(wxVERTICAL); | |
| SetSizer(m_MainSizer); | |
| SetBackgroundColour(wxColour(*wxWHITE)); | |
| auto *buttonSizer = new wxBoxSizer(wxHORIZONTAL); | |
| m_AddButton = new wxButton(this, wxID_ANY, "Add", wxDefaultPosition, wxSize(100, 24)); | |
| m_AddToSelectedButton = new wxButton(this, wxID_ANY, "Add To Selected", wxDefaultPosition, wxSize(120, 24)); | |
| m_DeleteButton = new wxButton(this, wxID_ANY, "Delete", wxDefaultPosition, wxSize(100, 24)); | |
| m_ClearButton = new wxButton(this, wxID_ANY, "Clear", wxDefaultPosition, wxSize(100, 24)); | |
| m_ExpandAllButton = new wxButton(this, wxID_ANY, "Expand All", wxDefaultPosition, wxSize(100, 24)); | |
| m_CollapseAllButton = new wxButton(this, wxID_ANY, "Collapse All", wxDefaultPosition, wxSize(100, 24)); | |
| buttonSizer->Add(m_AddButton, 0, wxEXPAND | wxALL, 4); | |
| buttonSizer->Add(m_AddToSelectedButton, 0, wxEXPAND | wxALL, 4); | |
| buttonSizer->Add(m_DeleteButton, 0, wxEXPAND | wxALL, 4); | |
| buttonSizer->Add(m_ClearButton, 0, wxEXPAND | wxALL, 4); | |
| buttonSizer->Add(m_ExpandAllButton, 0, wxEXPAND | wxALL, 4); | |
| buttonSizer->Add(m_CollapseAllButton, 0, wxEXPAND | wxALL, 4); | |
| m_AddButton->Bind(wxEVT_BUTTON, &MyFrame::OnAddNewEntry, this); | |
| m_AddToSelectedButton->Bind(wxEVT_BUTTON, &MyFrame::OnAddToSelected, this); | |
| m_DeleteButton->Bind(wxEVT_BUTTON, &MyFrame::OnDeleteSelected, this); | |
| m_ClearButton->Bind(wxEVT_BUTTON, &MyFrame::OnClearDataModel, this); | |
| m_ExpandAllButton->Bind(wxEVT_BUTTON, &MyFrame::OnExpandAll, this); | |
| m_CollapseAllButton->Bind(wxEVT_BUTTON, &MyFrame::OnCollapseAll, this); | |
| m_DataModel = new DataModel; | |
| m_DataViewCtrl = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); | |
| m_DataViewCtrl->AssociateModel(m_DataModel.get()); | |
| m_DataViewCtrl->Expand(m_DataModel->GetRootItem()); | |
| m_MainSizer->Add(m_DataViewCtrl, 1, wxEXPAND | wxALL, 5); | |
| m_MainSizer->Add(buttonSizer, 0, wxEXPAND | wxALL, 5); | |
| Layout(); | |
| AddColumns(); | |
| } | |
| void AddColumns() | |
| { | |
| wxDataViewTextRenderer *tr = new wxDataViewTextRenderer("string", wxDATAVIEW_CELL_INERT); | |
| wxDataViewColumn *column0 = | |
| new wxDataViewColumn("File Name", tr, 0, FromDIP(150), wxALIGN_LEFT, | |
| wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE); | |
| m_DataViewCtrl->AppendColumn(column0); | |
| tr = new wxDataViewTextRenderer("string", wxDATAVIEW_CELL_EDITABLE); | |
| wxDataViewColumn *column1 = | |
| new wxDataViewColumn("File Size", tr, 1, FromDIP(150), wxALIGN_LEFT, | |
| wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE); | |
| column1->SetMinWidth(FromDIP(120)); | |
| m_DataViewCtrl->AppendColumn(column1); | |
| } | |
| private: | |
| void OnClearDataModel(wxCommandEvent &WXUNUSED(event)) | |
| { | |
| m_DataModel->Clear(); | |
| } | |
| void OnAddNewEntry(wxCommandEvent &WXUNUSED(event)) | |
| { | |
| auto item = m_DataModel->AddNewEntry(2); | |
| m_DataViewCtrl->Expand(m_DataModel->GetRootItem()); | |
| wxCommandEvent e; | |
| OnExpandAll(e); | |
| } | |
| void DeleteSelectedItems() | |
| { | |
| wxDataViewItemArray items; | |
| int count = m_DataViewCtrl->GetSelections(items); | |
| for (int i = 0; i < count; ++i) | |
| { | |
| if (items[i].IsOk()) | |
| { | |
| m_DataModel->Delete(items[i]); | |
| } | |
| } | |
| wxCommandEvent e; | |
| OnExpandAll(e); | |
| } | |
| void OnDeleteSelected(wxCommandEvent &WXUNUSED(event)) | |
| { | |
| DeleteSelectedItems(); | |
| } | |
| void OnAddToSelected(wxCommandEvent &event) | |
| { | |
| auto item{m_DataViewCtrl->GetSelection()}; | |
| auto newItem = m_DataModel->AddNewChildItem(item); | |
| wxCommandEvent e; | |
| OnExpandAll(e); | |
| // m_DataViewCtrl->Expand(newItem); | |
| } | |
| void OnCollapseAll(wxCommandEvent &WXUNUSED(event)) | |
| { | |
| wxDataViewItemArray items = m_DataModel->GetAllItems(); | |
| for (int i = 0; i < items.GetCount(); ++i) | |
| { | |
| if (items[i].IsOk()) | |
| { | |
| m_DataViewCtrl->Collapse(items[i]); | |
| } | |
| } | |
| } | |
| void OnExpandAll(wxCommandEvent &WXUNUSED(event)) | |
| { | |
| wxDataViewItemArray items = m_DataModel->GetAllItems(); | |
| for (int i = 0; i < items.GetCount(); ++i) | |
| { | |
| if (items[i].IsOk()) | |
| { | |
| m_DataViewCtrl->Expand(items[i]); | |
| } | |
| } | |
| } | |
| private: | |
| wxObjectDataPtr<DataModel> m_DataModel; | |
| wxDataViewCtrl *m_DataViewCtrl; | |
| wxButton *m_AddButton; | |
| wxButton *m_AddToSelectedButton; | |
| wxButton *m_DeleteButton; | |
| wxButton *m_ClearButton; | |
| wxButton *m_ExpandAllButton; | |
| wxButton *m_CollapseAllButton; | |
| wxBoxSizer *m_MainSizer; | |
| }; | |
| class MyApp : public wxApp | |
| { | |
| public: | |
| bool OnInit() override | |
| { | |
| MyFrame *frame = new MyFrame("wxDataViewCtrl Test", wxDefaultPosition, wxSize(640, 480)); | |
| frame->Show(); | |
| return true; | |
| } | |
| }; | |
| wxIMPLEMENT_APP(MyApp); |
Hi @asmwarrior, Thanks for the input. This is a demo app, I created to demonstrate my issues. In my real application, I do return the newly created item.
Hi @asmwarrior, Thanks for the input. This is a demo app, I created to demonstrate my issues. In my real application, I do return the newly created item.
OK, thanks.
Another issue I see is that you have many Cleared(); in your model's member functions. But compared with the wxWidgets' sample dataview, the later one doesn't have Cleared(); function called in the item add member functions. If I remove the Cleared(); function call, I just see the item are not added correctly. Can you explain why? Thanks.
Another issue I see is that you have many
Cleared();in your model's member functions. But compared with the wxWidgets' sample dataview, the later one doesn't haveCleared();function called in the item add member functions. If I remove theCleared();function call, I just see the item are not added correctly. Can you explain why? Thanks.
I think this goes to the original discussion here: Add/Remove node to wxDataViewCtrl
I'm still not sure why.
Hi, in this function:
wxDataViewItem AddNewChildItem(const wxDataViewItem &selectedItem)Why you are returning the parent item of the created item? I think you should just return the new created item.