This example has a Gtk::TreeView
widget, with a
Gtk::ListStore
model.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_button_quit(); //Tree model columns: class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns() { add(m_col_id); add(m_col_name); add(m_col_number); add(m_col_percentage);} Gtk::TreeModelColumn<unsigned int> m_col_id; Gtk::TreeModelColumn<Glib::ustring> m_col_name; Gtk::TreeModelColumn<short> m_col_number; Gtk::TreeModelColumn<int> m_col_percentage; }; ModelColumns m_Columns; //Child widgets: Gtk::Box m_VBox; Gtk::ScrolledWindow m_ScrolledWindow; Gtk::TreeView m_TreeView; Glib::RefPtr<Gtk::ListStore> m_refTreeModel; Gtk::ButtonBox m_ButtonBox; Gtk::Button m_Button_Quit; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#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); }
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include <iostream> #include "examplewindow.h" ExampleWindow::ExampleWindow() : m_VBox(Gtk::ORIENTATION_VERTICAL), m_Button_Quit("Quit") { set_title("Gtk::TreeView (ListStore) example"); set_border_width(5); set_default_size(400, 200); add(m_VBox); //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_VBox.pack_start(m_ScrolledWindow); m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK); m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK); m_ButtonBox.set_border_width(5); m_ButtonBox.set_layout(Gtk::BUTTONBOX_END); m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this, &ExampleWindow::on_button_quit) ); //Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); m_TreeView.set_model(m_refTreeModel); //Fill the TreeView's model Gtk::TreeModel::Row row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 1; row[m_Columns.m_col_name] = "Billy Bob"; row[m_Columns.m_col_number] = 10; row[m_Columns.m_col_percentage] = 15; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 2; row[m_Columns.m_col_name] = "Joey Jojo"; row[m_Columns.m_col_number] = 20; row[m_Columns.m_col_percentage] = 40; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 3; row[m_Columns.m_col_name] = "Rob McRoberts"; row[m_Columns.m_col_number] = 30; row[m_Columns.m_col_percentage] = 70; //Add the TreeView's view columns: //This number will be shown with the default numeric formatting. m_TreeView.append_column("ID", m_Columns.m_col_id); m_TreeView.append_column("Name", m_Columns.m_col_name); m_TreeView.append_column_numeric("Formatted number", m_Columns.m_col_number, "%010d" /* 10 digits, using leading zeroes. */); //Display a progress bar instead of a decimal number: auto cell = Gtk::make_managed<Gtk::CellRendererProgress>(); int cols_count = m_TreeView.append_column("Some percentage", *cell); auto pColumn = m_TreeView.get_column(cols_count - 1); if(pColumn) { pColumn->add_attribute(cell->property_value(), m_Columns.m_col_percentage); } //Make all the columns reorderable: //This is not necessary, but it's nice to show the feature. //You can use TreeView::set_column_drag_function() to more //finely control column drag and drop. for(guint i = 0; i < 2; i++) { auto column = m_TreeView.get_column(i); column->set_reorderable(); } show_all_children(); } ExampleWindow::~ExampleWindow() { } void ExampleWindow::on_button_quit() { hide(); }
This example is very similar to the ListStore
example,
but uses a Gtk::TreeStore
model instead, and adds
children to the rows.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_button_quit(); void on_treeview_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column); //Tree model columns: class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns() { add(m_col_id); add(m_col_name); } Gtk::TreeModelColumn<int> m_col_id; Gtk::TreeModelColumn<Glib::ustring> m_col_name; }; ModelColumns m_Columns; //Child widgets: Gtk::Box m_VBox; Gtk::ScrolledWindow m_ScrolledWindow; Gtk::TreeView m_TreeView; Glib::RefPtr<Gtk::TreeStore> m_refTreeModel; Gtk::ButtonBox m_ButtonBox; Gtk::Button m_Button_Quit; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#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); }
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include <iostream> #include "examplewindow.h" ExampleWindow::ExampleWindow() : m_VBox(Gtk::ORIENTATION_VERTICAL), m_Button_Quit("Quit") { set_title("Gtk::TreeView (TreeStore) example"); set_border_width(5); set_default_size(400, 200); add(m_VBox); //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_VBox.pack_start(m_ScrolledWindow); m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK); m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK); m_ButtonBox.set_border_width(5); m_ButtonBox.set_layout(Gtk::BUTTONBOX_END); m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_quit) ); //Create the Tree model: m_refTreeModel = Gtk::TreeStore::create(m_Columns); m_TreeView.set_model(m_refTreeModel); //All the items to be reordered with drag-and-drop: m_TreeView.set_reorderable(); //Fill the TreeView's model Gtk::TreeModel::Row row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 1; row[m_Columns.m_col_name] = "Billy Bob"; Gtk::TreeModel::Row childrow = *(m_refTreeModel->append(row.children())); childrow[m_Columns.m_col_id] = 11; childrow[m_Columns.m_col_name] = "Billy Bob Junior"; childrow = *(m_refTreeModel->append(row.children())); childrow[m_Columns.m_col_id] = 12; childrow[m_Columns.m_col_name] = "Sue Bob"; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 2; row[m_Columns.m_col_name] = "Joey Jojo"; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 3; row[m_Columns.m_col_name] = "Rob McRoberts"; childrow = *(m_refTreeModel->append(row.children())); childrow[m_Columns.m_col_id] = 31; childrow[m_Columns.m_col_name] = "Xavier McRoberts"; //Add the TreeView's view columns: m_TreeView.append_column("ID", m_Columns.m_col_id); m_TreeView.append_column("Name", m_Columns.m_col_name); //Connect signal: m_TreeView.signal_row_activated().connect(sigc::mem_fun(*this, &ExampleWindow::on_treeview_row_activated) ); show_all_children(); } ExampleWindow::~ExampleWindow() { } void ExampleWindow::on_button_quit() { hide(); } void ExampleWindow::on_treeview_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* /* column */) { Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path); if(iter) { Gtk::TreeModel::Row row = *iter; std::cout << "Row activated: ID=" << row[m_Columns.m_col_id] << ", Name=" << row[m_Columns.m_col_name] << std::endl; } }
This example is identical to the ListStore
example, but
it uses TreeView::append_column_editable()
instead of
TreeView::append_column()
.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_button_quit(); void treeviewcolumn_validated_on_cell_data(Gtk::CellRenderer* renderer, const Gtk::TreeModel::iterator& iter); void cellrenderer_validated_on_editing_started(Gtk::CellEditable* cell_editable, const Glib::ustring& path); void cellrenderer_validated_on_edited(const Glib::ustring& path_string, const Glib::ustring& new_text); //Tree model columns: class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns() { add(m_col_id); add(m_col_name); add(m_col_foo); add(m_col_number); add(m_col_number_validated); } Gtk::TreeModelColumn<unsigned int> m_col_id; Gtk::TreeModelColumn<Glib::ustring> m_col_name; Gtk::TreeModelColumn<bool> m_col_foo; Gtk::TreeModelColumn<int> m_col_number; Gtk::TreeModelColumn<int> m_col_number_validated; }; ModelColumns m_Columns; //Child widgets: Gtk::Box m_VBox; Gtk::ScrolledWindow m_ScrolledWindow; Gtk::TreeView m_TreeView; Glib::RefPtr<Gtk::ListStore> m_refTreeModel; Gtk::ButtonBox m_ButtonBox; Gtk::Button m_Button_Quit; //For the validated column: //You could also use a CellRendererSpin or a CellRendererProgress: Gtk::CellRendererText m_cellrenderer_validated; Gtk::TreeView::Column m_treeviewcolumn_validated; bool m_validate_retry; Glib::ustring m_invalid_text_for_retry; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#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); }
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include <iostream> #include <cstdio> #include <cstdlib> #include "examplewindow.h" using std::sprintf; using std::strtol; ExampleWindow::ExampleWindow() : m_VBox(Gtk::ORIENTATION_VERTICAL), m_Button_Quit("Quit"), m_validate_retry(false) { set_title("Gtk::TreeView Editable Cells example"); set_border_width(5); set_default_size(400, 200); add(m_VBox); //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_VBox.pack_start(m_ScrolledWindow); m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK); m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK); m_ButtonBox.set_border_width(5); m_ButtonBox.set_layout(Gtk::BUTTONBOX_END); m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this, &ExampleWindow::on_button_quit) ); //Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); m_TreeView.set_model(m_refTreeModel); //Fill the TreeView's model Gtk::TreeModel::Row row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 1; row[m_Columns.m_col_name] = "Billy Bob"; row[m_Columns.m_col_foo] = true; row[m_Columns.m_col_number] = 10; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 2; row[m_Columns.m_col_name] = "Joey Jojo"; row[m_Columns.m_col_foo] = true; row[m_Columns.m_col_number] = 20; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 3; row[m_Columns.m_col_name] = "Rob McRoberts"; row[m_Columns.m_col_foo] = false; row[m_Columns.m_col_number] = 30; //Add the TreeView's view columns: //We use the *_editable convenience methods for most of these, //because the default functionality is enough: m_TreeView.append_column_editable("ID", m_Columns.m_col_id); m_TreeView.append_column_editable("Name", m_Columns.m_col_name); m_TreeView.append_column_editable("foo", m_Columns.m_col_foo); m_TreeView.append_column_numeric_editable("foo", m_Columns.m_col_number, "%010d"); //For this column, we create the CellRenderer ourselves, and connect our own //signal handlers, so that we can validate the data that the user enters, and //control how it is displayed. m_treeviewcolumn_validated.set_title("validated (<10)"); m_treeviewcolumn_validated.pack_start(m_cellrenderer_validated); m_TreeView.append_column(m_treeviewcolumn_validated); //Tell the view column how to render the model values: m_treeviewcolumn_validated.set_cell_data_func(m_cellrenderer_validated, sigc::mem_fun(*this, &ExampleWindow::treeviewcolumn_validated_on_cell_data) ); //Make the CellRenderer editable, and handle its editing signals: m_cellrenderer_validated.property_editable() = true; m_cellrenderer_validated.signal_editing_started().connect( sigc::mem_fun(*this, &ExampleWindow::cellrenderer_validated_on_editing_started) ); m_cellrenderer_validated.signal_edited().connect( sigc::mem_fun(*this, &ExampleWindow::cellrenderer_validated_on_edited) ); //If this was a CellRendererSpin then you would have to set the adjustment: //m_cellrenderer_validated.property_adjustment() = m_spin_adjustment; show_all_children(); } ExampleWindow::~ExampleWindow() { } void ExampleWindow::on_button_quit() { hide(); } void ExampleWindow::treeviewcolumn_validated_on_cell_data( Gtk::CellRenderer* /* renderer */, const Gtk::TreeModel::iterator& iter) { //Get the value from the model and show it appropriately in the view: if(iter) { Gtk::TreeModel::Row row = *iter; int model_value = row[m_Columns.m_col_number_validated]; //This is just an example. //In this case, it would be easier to use append_column_editable() or //append_column_numeric_editable() char buffer[32]; sprintf(buffer, "%d", model_value); Glib::ustring view_text = buffer; m_cellrenderer_validated.property_text() = view_text; } } void ExampleWindow::cellrenderer_validated_on_editing_started( Gtk::CellEditable* cell_editable, const Glib::ustring& /* path */) { //Start editing with previously-entered (but invalid) text, //if we are allowing the user to correct some invalid data. if(m_validate_retry) { //This is the CellEditable inside the CellRenderer. auto celleditable_validated = cell_editable; //It's usually an Entry, at least for a CellRendererText: auto pEntry = dynamic_cast<Gtk::Entry*>(celleditable_validated); if(pEntry) { pEntry->set_text(m_invalid_text_for_retry); m_validate_retry = false; m_invalid_text_for_retry.clear(); } } } void ExampleWindow::cellrenderer_validated_on_edited( const Glib::ustring& path_string, const Glib::ustring& new_text) { Gtk::TreePath path(path_string); //Convert the inputed text to an integer, as needed by our model column: char* pchEnd = nullptr; int new_value = strtol(new_text.c_str(), &pchEnd, 10); if(new_value > 10) { //Prevent entry of numbers higher than 10. //Tell the user: Gtk::MessageDialog dialog(*this, "The number must be less than 10. Please try again.", false, Gtk::MESSAGE_ERROR); dialog.run(); //Start editing again, with the bad text, so that the user can correct it. //A real application should probably allow the user to revert to the //previous text. //Set the text to be used in the start_editing signal handler: m_invalid_text_for_retry = new_text; m_validate_retry = true; //Start editing again: m_TreeView.set_cursor(path, m_treeviewcolumn_validated, m_cellrenderer_validated, true /* start_editing */); } else { //Get the row from the path: Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path); if(iter) { Gtk::TreeModel::Row row = *iter; //Put the new value in the model: row[m_Columns.m_col_number_validated] = new_value; } } }
This example is much like the TreeStore
example, but has
2 extra columns to indicate whether the row can be dragged, and whether it can
receive drag-and-dropped rows. It uses a derived
Gtk::TreeStore
which overrides the virtual functions as
described in the TreeView Drag and
Drop section.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm.h> #include "treemodel_dnd.h" class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_button_quit(); //Child widgets: Gtk::Box m_VBox; Gtk::ScrolledWindow m_ScrolledWindow; Gtk::TreeView m_TreeView; Glib::RefPtr<TreeModel_Dnd> m_refTreeModel; Gtk::ButtonBox m_ButtonBox; Gtk::Button m_Button_Quit; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: treemodel_dnd.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLE_TREEMODEL_DND_H #define GTKMM_EXAMPLE_TREEMODEL_DND_H #include <gtkmm.h> class TreeModel_Dnd : public Gtk::TreeStore { protected: TreeModel_Dnd(); public: //Tree model columns: class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns() { add(m_col_id); add(m_col_name); add(m_col_draggable); add(m_col_receivesdrags); } Gtk::TreeModelColumn<int> m_col_id; Gtk::TreeModelColumn<Glib::ustring> m_col_name; Gtk::TreeModelColumn<bool> m_col_draggable; Gtk::TreeModelColumn<bool> m_col_receivesdrags; }; ModelColumns m_Columns; static Glib::RefPtr<TreeModel_Dnd> create(); protected: //Overridden virtual functions: bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override; bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const override; }; #endif //GTKMM_EXAMPLE_TREEMODEL_DND_H
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#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); }
File: treemodel_dnd.cc
(For use with gtkmm 3, not gtkmm 2)
#include "treemodel_dnd.h" #include <iostream> TreeModel_Dnd::TreeModel_Dnd() { //We can't just call Gtk::TreeModel(m_Columns) in the initializer list //because m_Columns does not exist when the base class constructor runs. //And we can't have a static m_Columns instance, because that would be //instantiated before the gtkmm type system. //So, we use this method, which should only be used just after creation: set_column_types(m_Columns); } Glib::RefPtr<TreeModel_Dnd> TreeModel_Dnd::create() { return Glib::RefPtr<TreeModel_Dnd>( new TreeModel_Dnd() ); } bool TreeModel_Dnd::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const { // Make the value of the "draggable" column determine whether this row can // be dragged: //TODO: Add a const version of get_iter to TreeModel: auto unconstThis = const_cast<TreeModel_Dnd*>(this); const_iterator iter = unconstThis->get_iter(path); //const_iterator iter = get_iter(path); if(iter) { Row row = *iter; bool is_draggable = row[m_Columns.m_col_draggable]; return is_draggable; } return Gtk::TreeStore::row_draggable_vfunc(path); } bool TreeModel_Dnd::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const { //Make the value of the "receives drags" column determine whether a row can be //dragged into it: //dest is the path that the row would have after it has been dropped: //But in this case we are more interested in the parent row: Gtk::TreeModel::Path dest_parent = dest; bool dest_is_not_top_level = dest_parent.up(); if(!dest_is_not_top_level || dest_parent.empty()) { //The user wants to move something to the top-level. //Let's always allow that. } else { //Get an iterator for the row at this path: //We must unconst this. This should not be necessary with a future version //of gtkmm. //TODO: Add a const version of get_iter to TreeModel: auto unconstThis = const_cast<TreeModel_Dnd*>(this); const_iterator iter_dest_parent = unconstThis->get_iter(dest_parent); //const_iterator iter_dest_parent = get_iter(dest); if(iter_dest_parent) { Row row = *iter_dest_parent; bool receives_drags = row[m_Columns.m_col_receivesdrags]; return receives_drags; } } //You could also examine the row being dragged (via selection_data) //if you must look at both rows to see whether a drop should be allowed. //You could use //TODO: Add const version of get_from_selection_data(): Glib::RefPtr<const //Gtk::TreeModel> refThis = Glib::RefPtr<const Gtk::TreeModel>(this); // //auto refThis = //Glib::RefPtr<Gtk::TreeModel>(const_cast<TreeModel_Dnd*>(this)); //refThis->reference(); //, true /* take_copy */) //Gtk::TreeModel::Path path_dragged_row; //Gtk::TreeModel::Path::get_from_selection_data(selection_data, refThis, //path_dragged_row); return Gtk::TreeStore::row_drop_possible_vfunc(dest, selection_data); }
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include <iostream> #include "examplewindow.h" ExampleWindow::ExampleWindow() : m_VBox(Gtk::ORIENTATION_VERTICAL), m_Button_Quit("_Quit", true) { set_title("Gtk::TreeView (Drag and Drop) example"); set_border_width(5); set_default_size(400, 200); add(m_VBox); //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_VBox.pack_start(m_ScrolledWindow); m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK); m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK); m_ButtonBox.set_border_width(5); m_ButtonBox.set_layout(Gtk::BUTTONBOX_END); m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_quit) ); //Create the Tree model: //Use our derived model, which overrides some Gtk::TreeDragDest and //Gtk::TreeDragSource virtual functions: //The columns are declared in the overridden TreeModel. m_refTreeModel = TreeModel_Dnd::create(); m_TreeView.set_model(m_refTreeModel); //Enable Drag-and-Drop of TreeView rows: //See also the derived TreeModel's *_vfunc overrides. m_TreeView.enable_model_drag_source(); m_TreeView.enable_model_drag_dest(); //Fill the TreeView's model Gtk::TreeModel::Row row = *(m_refTreeModel->append()); row[m_refTreeModel->m_Columns.m_col_id] = 1; row[m_refTreeModel->m_Columns.m_col_name] = "Billy Bob"; row[m_refTreeModel->m_Columns.m_col_draggable] = true; row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true; Gtk::TreeModel::Row childrow = *(m_refTreeModel->append(row.children())); childrow[m_refTreeModel->m_Columns.m_col_id] = 11; childrow[m_refTreeModel->m_Columns.m_col_name] = "Billy Bob Junior"; childrow[m_refTreeModel->m_Columns.m_col_draggable] = true; childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true; childrow = *(m_refTreeModel->append(row.children())); childrow[m_refTreeModel->m_Columns.m_col_id] = 12; childrow[m_refTreeModel->m_Columns.m_col_name] = "Sue Bob"; childrow[m_refTreeModel->m_Columns.m_col_draggable] = true; childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true; row = *(m_refTreeModel->append()); row[m_refTreeModel->m_Columns.m_col_id] = 2; row[m_refTreeModel->m_Columns.m_col_name] = "Joey Jojo"; row[m_refTreeModel->m_Columns.m_col_draggable] = true; row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true; row = *(m_refTreeModel->append()); row[m_refTreeModel->m_Columns.m_col_id] = 3; row[m_refTreeModel->m_Columns.m_col_name] = "Rob McRoberts"; row[m_refTreeModel->m_Columns.m_col_draggable] = true; row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true; childrow = *(m_refTreeModel->append(row.children())); childrow[m_refTreeModel->m_Columns.m_col_id] = 31; childrow[m_refTreeModel->m_Columns.m_col_name] = "Xavier McRoberts"; childrow[m_refTreeModel->m_Columns.m_col_draggable] = true; childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true; //Add the TreeView's view columns: m_TreeView.append_column("ID", m_refTreeModel->m_Columns.m_col_id); m_TreeView.append_column("Name", m_refTreeModel->m_Columns.m_col_name); m_TreeView.append_column_editable("Draggable", m_refTreeModel->m_Columns.m_col_draggable); m_TreeView.append_column_editable("Receives Drags", m_refTreeModel->m_Columns.m_col_receivesdrags); show_all_children(); } ExampleWindow::~ExampleWindow() { } void ExampleWindow::on_button_quit() { hide(); }
This example is much like the ListStore
example, but
derives a custom TreeView
in order to override the
button_press_event
, and also to encapsulate the tree model
code in our derived class. See the TreeView Popup Context Menu
section.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm.h> #include "treeview_withpopup.h" class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_button_quit(); //Child widgets: Gtk::Box m_VBox; Gtk::ScrolledWindow m_ScrolledWindow; TreeView_WithPopup m_TreeView; Gtk::ButtonBox m_ButtonBox; Gtk::Button m_Button_Quit; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: treeview_withpopup.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H #define GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H #include <gtkmm.h> class TreeView_WithPopup : public Gtk::TreeView { public: TreeView_WithPopup(); virtual ~TreeView_WithPopup(); protected: // Override Signal handler: // Alternatively, use signal_button_press_event().connect_notify() bool on_button_press_event(GdkEventButton* button_event) override; //Signal handler for popup menu items: void on_menu_file_popup_generic(); //Tree model columns: class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns() { add(m_col_id); add(m_col_name); } Gtk::TreeModelColumn<unsigned int> m_col_id; Gtk::TreeModelColumn<Glib::ustring> m_col_name; }; ModelColumns m_Columns; //The Tree model: Glib::RefPtr<Gtk::ListStore> m_refTreeModel; Gtk::Menu m_Menu_Popup; }; #endif //GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#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); }
File: treeview_withpopup.cc
(For use with gtkmm 3, not gtkmm 2)
#include "treeview_withpopup.h" #include <iostream> TreeView_WithPopup::TreeView_WithPopup() { //Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); set_model(m_refTreeModel); //Fill the TreeView's model Gtk::TreeModel::Row row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 1; row[m_Columns.m_col_name] = "right-click on this"; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 2; row[m_Columns.m_col_name] = "or this"; row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = 3; row[m_Columns.m_col_name] = "or this, for a popup context menu"; //Add the TreeView's view columns: append_column("ID", m_Columns.m_col_id); append_column("Name", m_Columns.m_col_name); //Fill popup menu: auto item = Gtk::make_managed<Gtk::MenuItem>("_Edit", true); item->signal_activate().connect( sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic) ); m_Menu_Popup.append(*item); item = Gtk::make_managed<Gtk::MenuItem>("_Process", true); item->signal_activate().connect( sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic) ); m_Menu_Popup.append(*item); item = Gtk::make_managed<Gtk::MenuItem>("_Remove", true); item->signal_activate().connect( sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic) ); m_Menu_Popup.append(*item); m_Menu_Popup.accelerate(*this); m_Menu_Popup.show_all(); //Show all menu items when the menu pops up } TreeView_WithPopup::~TreeView_WithPopup() { } bool TreeView_WithPopup::on_button_press_event(GdkEventButton* button_event) { bool return_value = false; //Call base class, to allow normal handling, //such as allowing the row to be selected by the right-click: return_value = TreeView::on_button_press_event(button_event); //Then do our custom stuff: if( (button_event->type == GDK_BUTTON_PRESS) && (button_event->button == 3) ) { m_Menu_Popup.popup_at_pointer((GdkEvent*)button_event); // Menu::popup_at_pointer() is new in gtkmm 3.22. // If you have an older revision, try this: //m_Menu_Popup.popup(button_event->button, button_event->time); } return return_value; } void TreeView_WithPopup::on_menu_file_popup_generic() { std::cout << "A popup menu item was selected." << std::endl; auto refSelection = get_selection(); if(refSelection) { Gtk::TreeModel::iterator iter = refSelection->get_selected(); if(iter) { int id = (*iter)[m_Columns.m_col_id]; std::cout << " Selected ID=" << id << std::endl; } } }
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include <iostream> #include "examplewindow.h" ExampleWindow::ExampleWindow() : m_VBox(Gtk::ORIENTATION_VERTICAL), m_Button_Quit("Quit") { set_title("Gtk::TreeView (ListStore) example"); set_border_width(5); set_default_size(400, 200); add(m_VBox); //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_VBox.pack_start(m_ScrolledWindow); m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK); m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK); m_ButtonBox.set_border_width(5); m_ButtonBox.set_layout(Gtk::BUTTONBOX_END); m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this, &ExampleWindow::on_button_quit) ); show_all_children(); } ExampleWindow::~ExampleWindow() { } void ExampleWindow::on_button_quit() { hide(); }