-
-
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.