Last active
July 2, 2023 14:03
-
-
Save aamirglb/8a597dcf48b4bc773746fb9c5a0b08f1 to your computer and use it in GitHub Desktop.
A sample treeview using wxDataViewCtrl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} | |
} | |
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) | |
{ | |
return GetChildren(wxDataViewItem(m_Root), array); | |
// array.Add(wxDataViewItem((void *)m_Root)); | |
// return 1; | |
} | |
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()); | |
m_DataViewCtrl->Expand(item); | |
} | |
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]); | |
} | |
} | |
} | |
void OnDeleteSelected(wxCommandEvent &WXUNUSED(event)) | |
{ | |
DeleteSelectedItems(); | |
} | |
void OnAddToSelected(wxCommandEvent &event) | |
{ | |
auto item{m_DataViewCtrl->GetSelection()}; | |
auto newItem = m_DataModel->AddNewChildItem(item); | |
m_DataViewCtrl->Expand(newItem); | |
// m_DataViewCtrl->Select( 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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment