Chapter 8. Editable Cells

8.1. Editable Text Cells

With GtkCellRendererText you can not only display text, but you can also allow the user to edit a single cell's text right in the tree view by double-clicking on a cell.

To make this work you need to tell the cell renderer that a cell is editable, which you can do by setting the "editable" property of the text cell renderer in question to TRUE. You can either do this on a per-row basis (which allows you to set each single cell either editable or not) by connecting the "editable" property to a boolean type column in your tree model using attributes; or you can just do a ...


  g_object_set(renderer, "editable", TRUE, NULL);

... when you create the renderer, which sets all rows in that particular renderer column to be editable.

Now that our cells are editable, we also want to be notified when a cell has been edited. This can be achieved by connecting to the cell renderer's "edited" signal:


  g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, NULL);

This callback is then called whenever a cell has been edited. Instead of NULL we could have passed a pointer to the model as user data for convenience, as we probably want to store the new value in the model.

The callback for the "edited" signal looks like this (the API reference is a bit lacking in this particular case):


void        cell_edited_callback (GtkCellRendererText *cell,
                                  gchar               *path_string,
                                  gchar               *new_text,
                                  gpointer             user_data);

The tree path is passed to the "edited" signal callback in string form. You can convert this into a GtkTreePath with gtk_tree_path_new_from_string, or convert it into an iter with gtk_tree_model_get_iter_from_string.

Note that the cell renderer will not change the data for you in the store. After a cell has been edited, you will only receive an "edited" signal. If you do not change the data in the store, the old text will be rendered again as if nothing had happened.

If you have multiple (renderer) columns with editable cells, it is not necessary to have a different callback for each renderer, you can use the same callback for all renderers, and attach some data to each renderer, which you can later retrieve again in the callback to know which renderer/column has been edited. This is done like this, for example:


  renderer = gtk_cell_renderer_text_new();
  ...
  g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER(COLUMN_NAME));

  ...

  renderer = gtk_cell_renderer_text_new();
  ...
  g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER(COLUMN_YEAR_OF_BIRTH));

  ...

where COLUMN_NAME and COLUMN_YEAR_OF_BIRTH are enum values. In your callback you can then get the column number with


  guint column_number = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(renderer), "my_column_num"));

You can use this mechanism to attach all kinds of custom data to any object or widget, with a string identifier to your liking.

A good example for editable cells is in gtk-demo, which is part of the Gtk+ source code tree (in gtk+-2.x.y/demos/gtk-demo).

8.1.1. Setting the cursor to a specific cell

You can move the cursor to a specific cell in a tree view with gtk_tree_view_set_cursor (or gtk_tree_view_set_cursor_on_cell if you have multiple editable cell renderers packed into one tree view column), and start editing the cell if you want to. Similarly, you can get the current row and focus column with gtk_tree_view_get_cursor. Use gtk_widget_grab_focus(treeview) will make sure that the tree view has the keyboard focus.

As the API reference points out, the tree view needs to be realised for cell editing to happen. In other words: If you want to start editing a specific cell right at program startup, you need to set up an idle timeout with g_idle_add that does this for you as soon as the window and everything else has been realised (return FALSE in the timeout to make it run only once). Alternatively you could connect to the "realize" signal of the treeview with g_signal_connect_after to achieve the same thing.

Connect to the tree view's "cursor-changed" and/or "move-cursor" signals to keep track of the current position of the cursor.