Sorting
| Chapter 10. ListView, GridView, ColumnView |

## Sorting

The list can be sorted by wrapping it in a `SortListModel`. There are two ways to do this.

- In a `ColumnView`, get the `ColumnViewSorter` from the `ColumnView` and set it to the `SortListModel`. Set a `Sorter` to each `ColumnViewColumn`. Then the user of your app can sort the items by clicking on a column heading.

- In any view, set a `Sorter` such as a `StringSorter` or a `NumericSorter` to the `SortListModel`.

SortListModel Reference

StringSorter Reference

NumericSorter Reference

### Example

Figure 10.4. SortListModel

Source Code

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      ~ExampleWindow() override;

    protected:
      // Signal handlers:
      void on_button_nosort();
      void on_button_sortheadings();
      void on_button_sortlength();
      void on_button_quit();
      void on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign);
      void on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item);
      void on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item);

      // A Gio::ListStore item.
      class ModelColumns : public Glib::Object
      {
      public:
        unsigned int m_col_id;
        Glib::ustring m_col_name;

        static Glib::RefPtr<ModelColumns> create(int col_id, const Glib::ustring& col_name)
        {
          return Glib::make_refptr_for_instance<ModelColumns>(
            new ModelColumns(col_id, col_name));
        }

      protected:
        ModelColumns(int col_id, const Glib::ustring& col_name)
        : m_col_id(col_id), m_col_name(col_name)
        {}
      }; // ModelColumns

      // Child widgets:
      Gtk::Box m_VBox;
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::ColumnView m_ColumnView;
      Gtk::Box m_ButtonBox;
      Gtk::ToggleButton m_Button_NoSort;
      Gtk::ToggleButton m_Button_SortHeadings;
      Gtk::ToggleButton m_Button_SortLength;
      Gtk::Button m_Button_Quit;

      Glib::RefPtr<Gtk::SingleSelection> m_SelectionModel;
      Glib::RefPtr<Gio::ListStore<ModelColumns>> m_ListStore;
      Glib::RefPtr<Gtk::SortListModel> m_SortListModel;
      Glib::RefPtr<Gtk::ColumnViewColumn> m_IdColumn;
      Glib::RefPtr<Gtk::ColumnViewColumn> m_NameColumn;
      Glib::RefPtr<Gtk::NumericSorter<Glib::ustring::size_type>> m_LengthSorter;
      Glib::RefPtr<Gtk::NumericSorter<unsigned int>> m_IdSorter;
      Glib::RefPtr<Gtk::StringSorter> m_NameSorter;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::VERTICAL),
      m_Button_NoSort("Don't Sort"),
      m_Button_SortHeadings("Sort Headings"),
      m_Button_SortLength("Sort Length"),
      m_Button_Quit("Quit")
    {
      set_title("Gtk::ColumnView (Gtk::SortListModel) example");
      set_default_size(400, 350);

      m_VBox.set_margin(5);
      set_child(m_VBox);

      // Add the ColumnView, inside a ScrolledWindow, with the buttons underneath:
      m_ScrolledWindow.set_child(m_ColumnView);

      // Only show the scrollbars when they are necessary:
      m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
      m_ScrolledWindow.set_expand();

      m_VBox.append(m_ScrolledWindow);
      m_VBox.append(m_ButtonBox);

      m_ButtonBox.set_margin(5);
      m_ButtonBox.set_spacing(10);
      m_ButtonBox.append(m_Button_NoSort);
      m_ButtonBox.append(m_Button_SortHeadings);
      m_ButtonBox.append(m_Button_SortLength);
      m_ButtonBox.append(m_Button_Quit);
      m_Button_NoSort.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_nosort));
      m_Button_SortHeadings.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_sortheadings));
      m_Button_SortLength.signal_toggled().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_sortlength));
      m_Button_Quit.set_hexpand(true);
      m_Button_Quit.set_halign(Gtk::Align::END);
      m_Button_Quit.signal_clicked().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_button_quit));

      // Gtk::CheckButton and Gtk::ToggleButton have set_group() methods.
      // They act as radio buttons, if they are included in a group.
      m_Button_SortHeadings.set_group(m_Button_NoSort);
      m_Button_SortLength.set_group(m_Button_NoSort);

      // Create the List model.
      m_ListStore = Gio::ListStore<ModelColumns>::create();
      m_ListStore->append(ModelColumns::create(1, "Billy Bob"));
      m_ListStore->append(ModelColumns::create(11, "Billy Bob Junior"));
      m_ListStore->append(ModelColumns::create(12, "Sue Bob"));
      m_ListStore->append(ModelColumns::create(2, "Joey Jojo"));
      m_ListStore->append(ModelColumns::create(3, "Rob McRoberts"));
      m_ListStore->append(ModelColumns::create(31, "Xavier McRoberts"));

      // Create the sort model.
      auto size_expression = Gtk::ClosureExpression<Glib::ustring::size_type>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->Glib::ustring::size_type
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_name.size() : 0;
        });
      m_LengthSorter = Gtk::NumericSorter<Glib::ustring::size_type>::create(size_expression);
      m_LengthSorter->set_sort_order(Gtk::SortType::ASCENDING);
      m_SortListModel = Gtk::SortListModel::create(m_ListStore, m_LengthSorter);

      // Set list model and selection model.
      m_SelectionModel = Gtk::SingleSelection::create(m_ListStore);
      m_SelectionModel->set_autoselect(false);
      m_SelectionModel->set_can_unselect(true);
      m_ColumnView.set_model(m_SelectionModel);
      m_ColumnView.add_css_class("data-table"); // high density table

      // Make the columns reorderable.
      // This is not necessary, but it's nice to show the feature.
      m_ColumnView.set_reorderable(true);

      // Add the ColumnView's columns:

      // Id column
      auto factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::END));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_id));
      m_IdColumn = Gtk::ColumnViewColumn::create("ID", factory);
      m_ColumnView.append_column(m_IdColumn);
      auto uint_expression = Gtk::ClosureExpression<unsigned int>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->unsigned int
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_id : 0;
        });
      m_IdSorter = Gtk::NumericSorter<unsigned int>::create(uint_expression);

      // Name column
      factory = Gtk::SignalListItemFactory::create();
      factory->signal_setup().connect(sigc::bind(sigc::mem_fun(*this,
        &ExampleWindow::on_setup_label), Gtk::Align::START));
      factory->signal_bind().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_bind_name));
      m_NameColumn = Gtk::ColumnViewColumn::create("Name", factory);
      m_ColumnView.append_column(m_NameColumn);
      auto ustring_expression = Gtk::ClosureExpression<Glib::ustring>::create(
        [](const Glib::RefPtr<Glib::ObjectBase>& item)->Glib::ustring
        {
          const auto col = std::dynamic_pointer_cast<ModelColumns>(item);
          return col ? col->m_col_name : "";
        });
      m_NameSorter = Gtk::StringSorter::create(ustring_expression);

      // Show the list model without sorting.
      m_Button_NoSort.set_active(true);
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_nosort()
    {
      if (m_Button_NoSort.get_active())
      {
        m_SelectionModel->set_model(m_ListStore);
        m_IdColumn->set_sorter({});
        m_NameColumn->set_sorter({});
      }
    }

    void ExampleWindow::on_button_sortheadings()
    {
      if (m_Button_SortHeadings.get_active())
      {
        m_SortListModel->set_sorter(m_ColumnView.get_sorter());
        m_SelectionModel->set_model(m_SortListModel);
        m_IdColumn->set_sorter(m_IdSorter);
        m_NameColumn->set_sorter(m_NameSorter);
      }
    }

    void ExampleWindow::on_button_sortlength()
    {
      if (m_Button_SortLength.get_active())
      {
        m_SortListModel->set_sorter(m_LengthSorter);
        m_SelectionModel->set_model(m_SortListModel);
        m_IdColumn->set_sorter({});
        m_NameColumn->set_sorter({});
      }
    }

    void ExampleWindow::on_button_quit()
    {
      close();
    }

    void ExampleWindow::on_setup_label(
      const Glib::RefPtr<Gtk::ListItem>& list_item, Gtk::Align halign)
    {
      list_item->set_child(*Gtk::make_managed<Gtk::Label>("", halign));
    }

    void ExampleWindow::on_bind_id(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(Glib::ustring::format(col->m_col_id));
    }

    void ExampleWindow::on_bind_name(const Glib::RefPtr<Gtk::ListItem>& list_item)
    {
      auto col = std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
      if (!col)
        return;
      auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
      if (!label)
        return;
      label->set_text(col->m_col_name);
    }

File: `main.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char* argv[])
    {
      auto app = Gtk::Application::create("org.gtkmm.example");

      // Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

The View |  | Filtering