GTK+ By Example/Tree View/Editable Cells
Editable Cells
[edit | edit source]Editable Text Cells
[edit | edit source]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).
Setting the cursor to a specific cell
[edit | edit source]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.
Editable Toggle and Radio Button Cells
[edit | edit source]Just like you can set a GtkCellRendererText editable, you can specify whether a GtkCellRendererToggle should change its state when clicked by setting the "activatable" property - either when you create the renderer (in which case all cells in that column will be clickable) or by connecting the renderer property to a model column of boolean type via attributes.
Connect to the "toggled" signal of the toggle cell renderer to be notified when the user clicks on a toggle button (or radio button). The user click will not change the value in the store, or the appearance of the value rendered. The toggle button will only change state when you update the value in the store. Until then it will be in an "inconsistent" state, which is also why you should read the current value of that cell from the model, and not from the cell renderer.
The callback for the "toggled" signal looks like this (the API reference is a bit lacking in this particular case):
void cell_toggled_callback (GtkCellRendererToggle *cell,
gchar *path_string,
gpointer user_data);
Just like with the "edited" signal of the text cell renderer, the tree path is passed to the "toggled" 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.
Editable Spin Button Cells
[edit | edit source]Even though GtkSpinButton implements the GtkCellEditable interface (as does GtkEntry), there is no easy way to get a cell renderer that uses a spin button instead of a normal entry when in editing mode.
To get this functionality, you need to either write a new cell renderer that works very similar to GtkCellRendererText, or you need to write a new cell renderer class that derives from the text cell renderer and changes the behaviour in editing mode.
The cleanest solution would probably be to write a 'CellRendererNumeric' that does everything that the text cell renderer does, only that it has a float type property instead of the "text" property, and an additional digits property. However, no one seems to have done this yet, so you need to either write one, or find another solution to get spin buttons in editing mode.
Among this tutorial's code examples there is a hackish CellRendererSpin implementation which is based on GtkCellRendererText and shows spin buttons in editing mode. The implementation is not very refined though, so you need to make sure it works in your particular context, and modify it as needed.