Search code examples
cgtkgtk3

How to "check" GtkCellRendererToggle?


I would like a simple checkbox to "check" when I click it but am finding this very tricky, and there are almost no examples on the internet!

GtkTreeView *tree_view;
GtkListStore *list_store;
GtkTreeModelFilter *filter_model;

static int populate_row_callback (void *data, int colCount, char *values[], char *colNames[]) {
  GtkTreeIter row_iter;
  gtk_list_store_append (list_store, &row_iter);
  gtk_list_store_set (list_store, &row_iter, COL_DOWNLOAD, TRUE,
                                             ... // more cols
                                             -1);
  return 0;
}

void populate_grid () {
  gtk_list_store_clear (list_store);
  syn_get_data (populate_row_callback); // gets data from sqlite
}


static void activate (GtkApplication* app, gpointer user_data) {
  GtkBuilder *builder = gtk_builder_new_from_file ("window.ui");
  window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
  search_toggle = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "search_toggle"));
  search_bar = GTK_SEARCH_BAR (gtk_builder_get_object (builder, "search_bar"));
  search_entry = GTK_SEARCH_ENTRY (gtk_builder_get_object (builder, "search_entry"));
  tree_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "tree_view"));
  list_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "list_store"));
  filter_model = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (list_store), NULL));

  gtk_builder_connect_signals (builder, NULL);
  g_object_unref (builder);
  gtk_widget_show_all (window);

  populate_grid ();
  gtk_tree_model_filter_set_visible_func (filter_model, search_filter_func, NULL, NULL);
  gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (filter_model));

  gtk_main ();
}

void download_toggled (GtkCellRendererToggle *cell, gchar *path_string, gpointer user_data) {
  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);

  GtkTreeIter rowIter;
  if (gtk_tree_model_get_iter (model, &rowIter, path)) {
    gboolean checked = gtk_cell_renderer_toggle_get_active (cell);
    // gtk_cell_renderer_toggle_set_active (cell, !checked); // effects every row!

    gtk_list_store_set (list_store, &rowIter, 0, !checked, -1); // causes runtime critical warning
  }
}

I also tried using the GtkListStore inside the same handler:

gtk_list_store_set (list_store, &rowIter, 0, !checked, -1);

But that does not work and I get: Gtk-CRITICAL **: gtk_list_store_set_valist: assertion 'iter_is_valid (iter, list_store)' failed

This is because the Iter rowIter is not fetched using the row_store, but then again, I cannot fetch it using that because it does not have a get method...


Solution

  • Your GtkListStore is embedded in a GtkTreeModelFilter, so any GtkTreePaths and GtkTreeIters you make will be specific to the GtkTreeModelFilter. In order to be able to modify the underlying GtkTreeModel, you will have to turn the GtkTreeIter for the GtkTreeModelFilter into a GtkTreeIter for the underlying GtkListStore using the gtk_tree_model_filter_convert_iter_to_child_iter() function. Call it inside the if block in your original code's download_toggled().

    The same will apply if you ever add a GtkTreeModelSort into the equation. If you use both, you'll have to call the equivalent iter_to_child_iter function for both, in the correct order — it's like peeling an onion, layer by layer.