Copying data from one Gtk::ListStore to another

Imagine one has a Gtk::ListStore and want to copy it’s content to another one with the same model. How do we do it.

The natural instinct would be to do something like,

void Copy(const Glib::RefPtr <Gtk::ListStore> &refFrom,
          const Glib::RefPtr <Gtk::ListStore> &refTo)
{
  for(auto iFrom: refFrom->children())
  {
    auto iTo = refTo->append();
    auto oTo = *iTo;

    oTo = *iFrom;
  }
}

This is the closest to a compilable code that we can get but doesn’t do the job. At the end of the call we will have some blank rows in the target store.

The following one works but is mostly C (not that I have anything against C).

void Copy(const Glib::RefPtr <Gtk::ListStore> &refFrom,
          const Glib::RefPtr <Gtk::ListStore> &refTo)
{
  auto pFrom = GTK_TREE_MODEL(refFrom->gobj());
  auto pTo = refTo->gobj();

  for (auto iFrom : refFrom->children())
  {
    auto iTo = refTo->append();
    int n_columns = refFrom->get_n_columns();

    for (int i = 0; i
                    < n_columns; ++i)
    {
      GValue value = G_VALUE_INIT
      ;

      gtk_tree_model_get_value(pFrom,
                               const_cast <GtkTreeIter*>(iFrom->gobj()),
                               i,
                               &value);

      gtk_list_store_set_value(pTo,
                               const_cast <GtkTreeIter*>(iTo->gobj()),
                               i,
                               &value);

      g_value_unset(&value);
    }
  }
}

Is there a pure C++ way to do this?

A little bit about why I am doing this circus. I have a worker thread that loads data from a database. Since the Gtk::ListStore might have already been connected to a view, I can’t load the data directly into it from a non-ui thread. The thread creates a private store, loads data into it and hands it over to the main context using Glib::Dispatcher and that is where copying comes in.

Your C++ code tries to copy a whole row at a time. Your working C code contains
an inner loop that copies one column in one row at a time. I think that’s what
you have to do in C++, too. Gtkmm/C++ tries to be more type-safe than Gtk/C.
In this case it looks like you have to know the column types at compile-time,
making it impossible to loop for (int i = 0; i < n_columns; ++i).

Let’s assume your model contains these columns:

class ModelColumns : public Gtk::TreeModel::ColumnRecord
{
public:
  ModelColumns()
  { add(m_col_id); add(m_col_name); add(m_col_number);}

  Gtk::TreeModelColumn<unsigned int> m_col_id;
  Gtk::TreeModelColumn<Glib::ustring> m_col_name;
  Gtk::TreeModelColumn<short> m_col_number;
};
ModelColumns m_Columns;

Then the ListStore can be copied with:

void Copy(const Glib::RefPtr <Gtk::ListStore> &refFrom,
          const Glib::RefPtr <Gtk::ListStore> &refTo)
{
  for(auto iFrom: refFrom->children())
  {
    auto iTo = refTo->append();
    auto oTo = *iTo;
    auto oFrom = *iFrom;

    oTo[m_Columns.m_col_id] = oFrom[m_Columns.m_col_id];
    oTo[m_Columns.m_col_name] = oFrom[m_Columns.m_col_name];
    oTo[m_Columns.m_col_number] = oFrom[m_Columns.m_col_number];
  }
}

Is this better or worse than the C code? It depends, I would say. It’s less general.
The Copy() function is useful only for one specific model. If the model contains
many columns, an inner loop over the columns would be preferable.

1 Like

Thank you so much for replying.

I was looking for a more ‘general’ solution with a precondition that both models have the same column record. In my case I have this base class with three derived classes and each having a different Gtk::TreeModel::ColumnRecord. However all the three needs to perform this ‘copy’ operation. So I though why not do it in the base class and save myself from code duplication.

As I said before, I equally love C so I prefer to stick to the general solution here.

Thank you once again.