[gtkmm-documentation/gtkmm-3-24] Add examples/book/treeview/custom_treemodel



commit 1aa9607a35b395a80c033e1002b3960eb9a3de3e
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Tue Jan 7 14:47:54 2020 +0100

    Add examples/book/treeview/custom_treemodel
    
    See https://mail.gnome.org/archives/gtkmm-list/2019-December/msg00014.html
    Fixes #9 (Danny van Elsen)

 docs/tutorial/C/index-in.docbook                   |  15 +-
 examples/Makefile.am                               |   8 +
 .../treeview/custom_treemodel/exampletreemodel.cc  | 355 +++++++++++++++++++++
 .../treeview/custom_treemodel/exampletreemodel.h   | 133 ++++++++
 .../treeview/custom_treemodel/examplewindow.cc     | 107 +++++++
 .../book/treeview/custom_treemodel/examplewindow.h |  54 ++++
 examples/book/treeview/custom_treemodel/main.cc    |  28 ++
 examples/book/treeview/meson.build                 |   1 +
 8 files changed, 696 insertions(+), 5 deletions(-)
---
diff --git a/docs/tutorial/C/index-in.docbook b/docs/tutorial/C/index-in.docbook
index f9e0f92..e036818 100644
--- a/docs/tutorial/C/index-in.docbook
+++ b/docs/tutorial/C/index-in.docbook
@@ -2898,13 +2898,22 @@ so that the right-click will cause the row to be selected first.
 
 <sect1 id="sec-treeview-examples"><title>Examples</title>
 
+<para>Some <classname>TreeView</classname> examples are shown here. There are
+more examples in the <ulink url="&url_examples_base;treeview/">treeview directory</ulink>
+in <application>gtkmm-documentation</application>'s examples.</para>
+
+<para>If neither <classname>ListStore</classname> nor <classname>TreeStore</classname>
+is suitable for your application, look at the
+<ulink url="&url_examples_base;treeview/custom_treemodel">custom TreeModel</ulink>
+example. It shows how you can make your own implementation of the <classname>TreeModel</classname>
+interface.</para>
+
 <sect2 id="liststore-example"><title>ListStore</title>
 <para>
 This example has a <classname>Gtk::TreeView</classname> widget, with a
 <classname>Gtk::ListStore</classname> model.
 </para>
 
-
 <figure id="figure-treeview-liststore">
   <title>TreeView - ListStore</title>
   <screenshot>
@@ -2997,13 +3006,9 @@ section.
 <para><ulink url="&url_examples_base;treeview/popup/">Source Code</ulink></para>
 
 </sect2>
-
-
 </sect1>
-
 </chapter>
 
-
 <chapter id="chapter-combobox">
 <title>Combo Boxes</title>
 
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 8348bef..6ec1a5e 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -107,6 +107,7 @@ check_PROGRAMS =                                    \
        book/toolpalette/example                        \
        book/tooltips/example                           \
        book/treeview/combo_renderer/example            \
+       book/treeview/custom_treemodel/example  \
        book/treeview/drag_and_drop/example             \
        book/treeview/editable_cells/example            \
        book/treeview/filter/example                    \
@@ -666,6 +667,13 @@ book_treeview_combo_renderer_example_SOURCES =             \
        book/treeview/combo_renderer/examplewindow.h    \
        book/treeview/combo_renderer/main.cc
 
+book_treeview_custom_treemodel_example_SOURCES = \
+       book/treeview/custom_treemodel/examplewindow.cc \
+       book/treeview/custom_treemodel/examplewindow.h \
+       book/treeview/custom_treemodel/main.cc \
+       book/treeview/custom_treemodel/exampletreemodel.cc \
+       book/treeview/custom_treemodel/exampletreemodel.h
+
 book_treeview_drag_and_drop_example_SOURCES =          \
        book/treeview/drag_and_drop/examplewindow.cc    \
        book/treeview/drag_and_drop/examplewindow.h     \
diff --git a/examples/book/treeview/custom_treemodel/exampletreemodel.cc 
b/examples/book/treeview/custom_treemodel/exampletreemodel.cc
new file mode 100644
index 0000000..2490994
--- /dev/null
+++ b/examples/book/treeview/custom_treemodel/exampletreemodel.cc
@@ -0,0 +1,355 @@
+/* gtkmm example Copyright (C) 2020 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "exampletreemodel.h"
+
+namespace
+{
+  enum
+  {
+    COL_INT,
+    COL_STRING
+  };
+}
+
+//////////////// MemoryItem
+
+MemoryItem::MemoryItem(int i)
+: mi_1(i),      mi_2(Glib::ustring::format("item ", mi_1)),
+  mi_A1(11*i),  mi_A2(Glib::ustring::format("item A", mi_A1)),
+  mi_B1(111*i), mi_B2(Glib::ustring::format("item B", mi_B1))
+{
+}
+
+//////////////// Memory
+
+void Memory::push_back(const MemoryItem& item)
+{
+  m_item.push_back(item);
+
+  if (m_model)
+    m_model->memory_row_inserted(m_item.size()-1);
+}
+
+void Memory::pop_back()
+{
+  if (m_item.size() > 0)
+  {
+    m_item.pop_back();
+
+    if (m_model)
+      m_model->memory_row_deleted(m_item.size());
+  }
+}
+
+void Memory::set_model(const Glib::RefPtr<MemoryTreeModel>& model)
+{
+  if (m_model)
+    m_model->set_memory(nullptr);
+
+  m_model = model;
+  if (m_model)
+    m_model->set_memory(this);
+}
+
+const MemoryItem& Memory::operator[](int i) const
+{
+  return m_item[i];
+}
+
+std::size_t Memory::size() const
+{
+  return m_item.size();
+}
+
+//////////////// MemoryColumn
+
+MemoryColumns::MemoryColumns()
+{
+  add(col_i);
+  add(col_s);
+}
+
+MemoryColumns::~MemoryColumns()
+{
+}
+
+//////////////// MemoryTreeModel
+
+MemoryTreeModel::MemoryTreeModel()
+: Glib::ObjectBase(typeid(MemoryTreeModel)), // Register a custom GType.
+  Glib::Object() // The custom GType is actually registered here.
+{
+ m_ic.init(IntColumn::ValueType::value_type());
+ m_sc.init(StringColumn::ValueType::value_type());
+}
+
+MemoryTreeModel::~MemoryTreeModel()
+{
+}
+
+Glib::RefPtr<MemoryTreeModel> MemoryTreeModel::create()
+{
+  return Glib::RefPtr<MemoryTreeModel>(new MemoryTreeModel);
+}
+
+void MemoryTreeModel::set_memory(const Memory* m)
+{
+  m_Memory = m;
+  ++m_stamp;
+}
+
+void MemoryTreeModel::memory_row_inserted(unsigned long i)
+{
+  if (!m_Memory)
+    return;
+
+  // Inform TreeView that a new node has been inserted, including two subnodes.
+  ++m_stamp;
+  iterator iter;
+  iter.set_stamp(m_stamp);
+  iter.gobj()->user_data = (void*)i; // row index
+  for (unsigned long depth = 1; depth <= 3; ++depth)
+  {
+    iter.gobj()->user_data2 = (void*)depth; // sublevel
+    row_inserted(get_path(iter), iter);
+  }
+}
+
+void MemoryTreeModel::memory_row_deleted(unsigned long i)
+{
+  if (!m_Memory)
+    return;
+
+  // Inform TreeView that a node has been deleted, including two subnodes.
+  ++m_stamp;
+  Path path(1, i);
+  for (unsigned long depth = 1; depth <= 3; ++depth)
+  {
+    row_deleted(path);
+    path.push_back(0); // Only one child node per level, index 0
+  }
+}
+
+Gtk::TreeModelFlags MemoryTreeModel::get_flags_vfunc() const
+{
+   return Gtk::TreeModelFlags(0);
+}
+
+int MemoryTreeModel::get_n_columns_vfunc() const
+{
+   return m_Columns.size();
+}
+
+// What information does one row contain?
+void MemoryTreeModel::get_value_vfunc(const iterator& iter, int column, Glib::ValueBase& value) const
+{
+  // user_data contains the row number
+  const unsigned long row = (unsigned long)iter.gobj()->user_data;
+
+  if (!is_valid(iter) || row >= m_Memory->size())
+    return;
+
+  // user_data2 contains the depth of the node
+  const unsigned long depth = (unsigned long)iter.gobj()->user_data2;
+
+  const MemoryItem& mi = (*m_Memory)[row];
+
+  switch (column)
+  {
+  case COL_INT:
+  {
+    int result_int = 0;
+    if (depth <= 1) // top level node
+      result_int = mi.mi_1;
+    else if (depth == 2) // first subnode level
+      result_int = mi.mi_A1;
+    else // second subnode level
+      result_int = mi.mi_B1;
+
+    m_ic.set(result_int);
+    value.init(m_ic.value_type());
+    value = m_ic;
+    break;
+  }
+  case COL_STRING:
+  {
+    Glib::ustring result_string;
+    // Same partitioning as for the ints
+    if (depth <= 1)
+      result_string = mi.mi_2;
+    else if (depth == 2)
+      result_string = mi.mi_A2;
+    else
+      result_string = mi.mi_B2;
+
+    m_sc.set(result_string);
+    value.init(m_sc.value_type());
+    value = m_sc;
+    break;
+  }
+  }
+}
+
+// What is the next node from this one?
+bool MemoryTreeModel::iter_next_vfunc(const iterator& iter, iterator& iter_next) const
+{
+  const unsigned long row = (unsigned long)iter.gobj()->user_data;
+  const unsigned long depth = (unsigned long)iter.gobj()->user_data2;
+
+  if (!is_valid(iter)
+      || row + 1 >= m_Memory->size() // beyond the last row
+      || depth > 1) // the sublevels have just one child, no next node
+  {
+    iter_next = iterator(); // There is no next row.
+    return false;
+  }
+
+  // Set markers for next node
+  iter_next.set_stamp(m_stamp);
+  iter_next.gobj()->user_data = (void*)(row + 1);
+  iter_next.gobj()->user_data2 = (void*)depth;
+  return true;
+}
+
+// Get the first child of this parent node
+bool MemoryTreeModel::iter_children_vfunc(const iterator& parent, iterator& iter) const
+{
+  return iter_nth_child_vfunc(parent, 0, iter);
+}
+
+bool MemoryTreeModel::iter_has_child_vfunc(const iterator& iter) const
+{
+  return iter_n_children_vfunc(iter) > 0;
+}
+
+int MemoryTreeModel::iter_n_children_vfunc(const iterator& iter) const
+{
+  const unsigned long depth = (unsigned long)iter.gobj()->user_data2;
+
+  if (!is_valid(iter) || depth == 0) // invalid or uninitialized node
+    return 0;
+  if (depth <= 2) // only two sublevels of nodes
+    return 1;
+  return 0; // no nodes
+}
+
+int MemoryTreeModel::iter_n_root_children_vfunc() const
+{
+  return m_Memory->size(); // number of rows
+}
+
+// Get the nth child of this parent node
+bool MemoryTreeModel::iter_nth_child_vfunc(const iterator& parent, int n , iterator& iter) const
+{
+  const unsigned long row = (unsigned long)parent.gobj()->user_data;
+  const unsigned long depth = (unsigned long)parent.gobj()->user_data2;
+
+  if (!is_valid(parent)
+      || depth > 2 // only two sublevels of nodes
+      || n > 0) // and one child node only
+  {
+    iter = iterator();
+    return false; // There are no children.
+  }
+
+  iter.set_stamp(m_stamp);
+  iter.gobj()->user_data = (void*)row; // row index
+  iter.gobj()->user_data2 = (void*)(depth + 1); // sublevel
+  return true;
+}
+
+// Get the nth row
+bool MemoryTreeModel::iter_nth_root_child_vfunc(int n, iterator& iter) const
+{
+  const unsigned long row = n;
+
+  if (row >= m_Memory->size())
+  {
+    iter = iterator();
+    return false; // No such row
+  }
+
+  iter.set_stamp(m_stamp);
+  iter.gobj()->user_data = (void*)row; // row index
+  iter.gobj()->user_data2 = (void*)1; // top level node
+  return true; // n < available rows
+}
+
+// Get the parent for this child
+bool MemoryTreeModel::iter_parent_vfunc(const iterator& child, iterator& iter) const
+{
+  const unsigned long row = (unsigned long)child.gobj()->user_data;
+  const unsigned long depth = (unsigned long)child.gobj()->user_data2;
+
+  if (!is_valid(child)
+      || depth <= 1) // top level
+  {
+    iter = iterator();
+    return false; // There is no parent.
+  }
+
+  iter.set_stamp(m_stamp);
+  iter.gobj()->user_data = (void*)row; // row index
+  iter.gobj()->user_data2 = (void*)(depth - 1); // one level up
+  return true;
+}
+
+// Define a node
+bool MemoryTreeModel::get_iter_vfunc(const Path& path, iterator& iter) const
+{
+  const unsigned long row = path[0]; // row_index
+  const unsigned long depth = path.size();
+
+  if (row >= m_Memory->size()
+      || depth == 0 || depth > 3) // only two levels of subnodes
+  {
+    iter = iterator();
+    return false;
+  }
+
+  iter.set_stamp(m_stamp);
+  iter.gobj()->user_data = (void*)row; // row index
+  iter.gobj()->user_data2 = (void*)depth; // sublevel
+  iter.gobj()->user_data3 = (void*)-1; // just to show its presence, really
+  return true;
+}
+
+Gtk::TreeModel::Path MemoryTreeModel::get_path_vfunc(const iterator& iter) const
+{
+  const unsigned long row = (unsigned long)iter.gobj()->user_data;
+  const unsigned long depth = (unsigned long)iter.gobj()->user_data2;
+
+  Path path(1, row);
+  for (unsigned long i = 2; i <= depth; ++i)
+    path.push_back(0); // Only one child node per level, index 0
+  return path;
+}
+
+GType MemoryTreeModel::get_column_type_vfunc(int index) const
+{
+  if (index < 0 || static_cast<unsigned int>(index) >= m_Columns.size())
+    return 0;
+
+  return m_Columns.types()[index];
+}
+
+bool MemoryTreeModel::is_valid(const iterator& iter) const
+{
+  // Anything that modifies the model's structure should change the model's stamp,
+  // so that old iterators are ignored.
+  return m_stamp == iter.get_stamp();
+}
diff --git a/examples/book/treeview/custom_treemodel/exampletreemodel.h 
b/examples/book/treeview/custom_treemodel/exampletreemodel.h
new file mode 100644
index 0000000..d9c7631
--- /dev/null
+++ b/examples/book/treeview/custom_treemodel/exampletreemodel.h
@@ -0,0 +1,133 @@
+/* gtkmm example Copyright (C) 2020 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GTKMM_EXAMPLETREEMODEL_H
+#define GTKMM_EXAMPLETREEMODEL_H
+
+#include <gtkmm.h>
+#include <vector>
+#include <utility>
+
+class MemoryTreeModel;
+
+// One MemoryItem will represent one chunk of information to
+// be shown in the Treeview, i.e. one row with subnodes.
+struct MemoryItem
+{
+  MemoryItem(int i);
+
+  // data for top level nodes
+  int mi_1 = 0;
+  Glib::ustring mi_2;
+
+  // for first level of subnodes
+  int mi_A1 = 0;
+  Glib::ustring mi_A2;
+
+  // for second level of subnodes
+  int mi_B1 = 0;
+  Glib::ustring mi_B2;
+};
+
+class Memory
+{
+public:
+  template <typename... Args>
+  void emplace_back(Args&&... args);
+
+  void push_back(const MemoryItem& item);
+  void pop_back();
+  void set_model(const Glib::RefPtr<MemoryTreeModel>& model);
+  const MemoryItem& operator[](int i) const;
+  std::size_t size() const;
+
+private:
+  // These we will show in the TreeView
+  std::vector<MemoryItem> m_item;
+
+  Glib::RefPtr<MemoryTreeModel> m_model;
+};
+
+class MemoryColumns: public Gtk::TreeModel::ColumnRecord
+{
+public:
+  MemoryColumns();
+  virtual ~MemoryColumns();
+
+  Gtk::TreeModelColumn<int>           col_i; // A column for the ints
+  Gtk::TreeModelColumn<Glib::ustring> col_s; // A column for the strings
+};
+
+class MemoryTreeModel : public Gtk::TreeModel, public Glib::Object
+{
+public:
+  MemoryTreeModel();
+  virtual ~MemoryTreeModel();
+
+  static Glib::RefPtr<MemoryTreeModel> create();
+
+  void set_memory(const Memory* m = nullptr);
+  void memory_row_inserted(unsigned long i);
+  void memory_row_deleted(unsigned long i);
+
+protected:
+  // Overrides:
+  Gtk::TreeModelFlags get_flags_vfunc() const override;
+  int get_n_columns_vfunc() const override;
+  GType get_column_type_vfunc(int index) const override;
+  void get_value_vfunc(const iterator& iter, int column, Glib::ValueBase& value) const override;
+  bool iter_next_vfunc(const iterator& iter, iterator& iter_next) const override;
+  bool iter_children_vfunc(const iterator& parent, iterator& iter) const override;
+  bool iter_has_child_vfunc(const iterator& iter) const override;
+  int iter_n_children_vfunc(const iterator& iter) const override;
+  int iter_n_root_children_vfunc() const override;
+  bool iter_nth_child_vfunc(const iterator& parent, int n, iterator& iter) const override;
+  bool iter_nth_root_child_vfunc(int n, iterator& iter) const override;
+  bool iter_parent_vfunc(const iterator& child, iterator& iter) const override;
+  Path get_path_vfunc(const iterator& iter) const override;
+  bool get_iter_vfunc(const Path& path, iterator& iter) const override;
+
+  // These vfuncs are optional to implement.
+  // void ref_node_vfunc(const iterator& iter) const override;
+  // void unref_node_vfunc(const iterator& iter) const override;
+
+  bool is_valid(const iterator& iter) const;
+
+private:
+  using IntColumn    = Gtk::TreeModelColumn<int>;
+  using StringColumn = Gtk::TreeModelColumn<Glib::ustring>;
+
+  mutable IntColumn::ValueType    m_ic;
+  mutable StringColumn::ValueType m_sc;
+
+  MemoryColumns m_Columns;
+
+  // The gate for the model to the information to be shown.
+  // The MemoryTreeModel does not own the data. 
+  const Memory* m_Memory = nullptr;
+  // When the model's stamp and the TreeIter's stamp are equal, the TreeIter is valid.
+  int m_stamp = 0;
+};
+
+template <typename... Args>
+void Memory::emplace_back(Args&&... args)
+{
+  m_item.emplace_back(std::forward<Args>(args)...);
+  if (m_model)
+    m_model->memory_row_inserted(m_item.size()-1);
+}
+
+#endif // GTKMM_EXAMPLETREEMODEL_H
diff --git a/examples/book/treeview/custom_treemodel/examplewindow.cc 
b/examples/book/treeview/custom_treemodel/examplewindow.cc
new file mode 100644
index 0000000..5962887
--- /dev/null
+++ b/examples/book/treeview/custom_treemodel/examplewindow.cc
@@ -0,0 +1,107 @@
+/* gtkmm example Copyright (C) 2020 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include "examplewindow.h"
+
+ExampleWindow::ExampleWindow()
+: m_VBox(Gtk::ORIENTATION_VERTICAL, 8),
+  m_Label("Analysis Sheet"),
+  m_Button_Add("Add Node"),
+  m_Button_Remove("Remove Node"),
+  m_Button_Quit("Quit")
+{
+  set_title("Gtk::TreeView (custom TreeModel) example");
+  set_border_width(8);
+  set_default_size(650, 400);
+
+  add(m_VBox);
+  m_VBox.pack_start(m_Label, Gtk::PACK_SHRINK);
+
+  // Add the TreeView, inside a ScrolledWindow, with the button underneath:
+  m_ScrolledWindow.add(m_TreeView);
+
+  // Only show the scrollbars when they are necessary:
+  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+  m_ScrolledWindow.set_shadow_type(Gtk::SHADOW_ETCHED_IN);
+
+  m_VBox.pack_start(m_ScrolledWindow);
+  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);
+
+  m_ButtonBox.pack_start(m_Button_Add, Gtk::PACK_SHRINK);
+  m_ButtonBox.pack_start(m_Button_Remove, Gtk::PACK_SHRINK);
+  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
+  m_ButtonBox.set_border_width(5);
+  m_ButtonBox.set_spacing(5);
+  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
+  m_Button_Add.signal_clicked().connect(sigc::mem_fun(*this,
+              &ExampleWindow::on_button_add));
+  m_Button_Remove.signal_clicked().connect(sigc::mem_fun(*this,
+              &ExampleWindow::on_button_remove));
+  m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
+              &ExampleWindow::on_button_quit));
+
+  // Fill memory.
+  fill_memory(2);
+
+  // Create the Tree model:
+  m_refTreeModel = MemoryTreeModel::create();
+  m_Memory.set_model(m_refTreeModel);
+
+  // TreeView
+  m_TreeView.append_column("Integer", m_Columns.col_i);
+  m_TreeView.append_column("String", m_Columns.col_s);
+  m_TreeView.set_model(m_refTreeModel);
+
+  m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
+
+  show_all();
+}
+
+ExampleWindow::~ExampleWindow()
+{
+}
+
+void ExampleWindow::on_button_add()
+{
+  m_Memory.emplace_back(m_Memory.size()+1);
+  // Expand the appended row.
+  m_TreeView.expand_row(Gtk::TreeModel::Path(1, m_Memory.size()-1), true);
+}
+
+void ExampleWindow::on_button_remove()
+{
+  m_Memory.pop_back();
+}
+
+void ExampleWindow::on_button_quit()
+{
+  hide();
+}
+
+void ExampleWindow::fill_memory(int n_top_level_rows)
+{
+  for (int i = 1; i <= n_top_level_rows; ++i)
+    m_Memory.emplace_back(i);
+}
+
+void ExampleWindow::on_realize()
+{
+  m_TreeView.expand_all();
+
+  // Call base class:
+  Window::on_realize();
+}
diff --git a/examples/book/treeview/custom_treemodel/examplewindow.h 
b/examples/book/treeview/custom_treemodel/examplewindow.h
new file mode 100644
index 0000000..66add22
--- /dev/null
+++ b/examples/book/treeview/custom_treemodel/examplewindow.h
@@ -0,0 +1,54 @@
+/* gtkmm example Copyright (C) 2020 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GTKMM_EXAMPLEWINDOW_H
+#define GTKMM_EXAMPLEWINDOW_H
+
+#include <gtkmm.h>
+#include "exampletreemodel.h"
+
+class ExampleWindow : public Gtk::Window
+{
+public:
+  ExampleWindow();
+  virtual ~ExampleWindow();
+
+protected:
+  // Signal handlers:
+  void on_realize() override;
+  void on_button_add();
+  void on_button_remove();
+  void on_button_quit();
+
+  void fill_memory(int n_top_level_rows);
+
+  // Child widgets:
+  Gtk::Box m_VBox;
+  Gtk::ScrolledWindow m_ScrolledWindow;
+  Gtk::Label m_Label;
+  Gtk::TreeView m_TreeView;
+  Gtk::ButtonBox m_ButtonBox;
+  Gtk::Button m_Button_Add;
+  Gtk::Button m_Button_Remove;
+  Gtk::Button m_Button_Quit;
+
+  MemoryColumns m_Columns;
+  Glib::RefPtr<MemoryTreeModel> m_refTreeModel;
+
+  Memory m_Memory;
+};
+
+#endif // GTKMM_EXAMPLEWINDOW_H
diff --git a/examples/book/treeview/custom_treemodel/main.cc b/examples/book/treeview/custom_treemodel/main.cc
new file mode 100644
index 0000000..84aaccc
--- /dev/null
+++ b/examples/book/treeview/custom_treemodel/main.cc
@@ -0,0 +1,28 @@
+/* gtkmm example Copyright (C) 2020 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "examplewindow.h"
+#include <gtkmm/application.h>
+
+int main(int argc, char* argv[])
+{
+  auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
+
+  ExampleWindow window;
+
+  // Shows the window and returns when it is closed.
+  return app->run(window);
+}
diff --git a/examples/book/treeview/meson.build b/examples/book/treeview/meson.build
index c154a96..3cc665d 100644
--- a/examples/book/treeview/meson.build
+++ b/examples/book/treeview/meson.build
@@ -9,6 +9,7 @@ exwindow_main = ['examplewindow.cc', 'main.cc']
 examples_book_treeview = [
 # [[dir-name], exe-name, [sources]]
   [['combo_renderer'], 'example', exwindow_main],
+  [['custom_treemodel'], 'example', exwindow_main + ['exampletreemodel.cc']],
   [['drag_and_drop'], 'example', exwindow_main + ['treemodel_dnd.cc']],
   [['editable_cells'], 'example', exwindow_main],
   [['filter'], 'example', exwindow_main],


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]