3.7. Removing Multiple Rows

Removing multiple rows at once can be a bit tricky at times, and requires some thought on how to do this best. For example, it is not possible to traverse a store with gtk_tree_model_foreach, check in the callback function whether the given row should be removed and then just remove it by calling one of the stores' remove functions. This will not work, because the model is changed from within the foreach loop, which might suddenly invalidate formerly valid tree iters in the foreach function, and thus lead to unpredictable results.

You could traverse the store in a while loop of course, and call gtk_list_store_remove or gtk_tree_store_remove whenever you want to remove a row, and then just continue if the remove functions returns TRUE (meaning that the iter is still valid and now points to the row after the row that was removed). However, this approach will only work with Gtk+-2.2 or later and will not work if you want your programs to compile and work with Gtk+-2.0 as well, for the reasons outlined above (in Gtk+-2.0 the remove functions did not set the passed iter to the next valid row). Also, while this approach might be feasable for a list store, it gets a bit awkward for a tree store.

Here is an example for an alternative approach to removing multiple rows in one go (here we want to remove all rows from the store that contain persons that have been born after 1980, but it could just as well be all selected rows or some other criterion):


 /******************************************************************
  *
  *  Removing multiple rows in one go
  *
  ******************************************************************/

  ...

  gboolean
  foreach_func (GtkTreeModel *model,
                GtkTreePath  *path,
                GtkTreeIter  *iter,
                GList       **rowref_list)
  {
    guint  year_of_birth;

    g_assert ( rowref_list != NULL );

    gtk_tree_model_get (model, iter, COL_YEAR_BORN, &year_of_birth, -1);

    if ( year_of_birth > 1980 )
    {
      GtkTreeRowReference  *rowref;

      rowref = gtk_tree_row_reference_new(model, path);

      *rowref_list = g_list_append(*rowref_list, rowref);
    }

    return FALSE; /* do not stop walking the store, call us with next row */
  }

  void
  remove_people_born_after_1980 (void)
  {
     GList *rr_list = NULL;    /* list of GtkTreeRowReferences to remove */
     GList *node;

     gtk_tree_model_foreach(GTK_TREE_MODEL(store),
                            (GtkTreeModelForeachFunc) foreach_func,
                            &rr_list);

     for ( node = rr_list;  node != NULL;  node = node->next )
     {
        GtkTreePath *path;

        path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data);

        if (path)
        {
           GtkTreeIter  iter;

           if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
           {
             gtk_list_store_remove(store, &iter);
           }

           /* FIXME/CHECK: Do we need to free the path here? */
        }
     }

     g_list_foreach(rr_list, (GFunc) gtk_tree_row_reference_free, NULL);
     g_list_free(rr_list);
  }

  ...

gtk_list_store_clear and gtk_tree_store_clear come in handy if you want to remove all rows.