1/* Tree View/Editable Cells
2 *
3 * This demo demonstrates the use of editable cells in a GtkTreeView. If
4 * you're new to the GtkTreeView widgets and associates, look into
5 * the GtkListStore example first. It also shows how to use the
6 * GtkCellRenderer::editing-started signal to do custom setup of the
7 * editable widget.
8 *
9 * The cell renderers used in this demo are GtkCellRendererText,
10 * GtkCellRendererCombo and GtkCellRendererProgress.
11 */
12
13#include <gtk/gtk.h>
14#include <string.h>
15#include <stdlib.h>
16
17typedef struct
18{
19 int number;
20 char *product;
21 int yummy;
22}
23Item;
24
25enum
26{
27 COLUMN_ITEM_NUMBER,
28 COLUMN_ITEM_PRODUCT,
29 COLUMN_ITEM_YUMMY,
30 NUM_ITEM_COLUMNS
31};
32
33enum
34{
35 COLUMN_NUMBER_TEXT,
36 NUM_NUMBER_COLUMNS
37};
38
39static GArray *articles = NULL;
40
41static void
42add_items (void)
43{
44 Item foo;
45
46 g_return_if_fail (articles != NULL);
47
48 foo.number = 3;
49 foo.product = g_strdup (str: "bottles of coke");
50 foo.yummy = 20;
51 g_array_append_vals (array: articles, data: &foo, len: 1);
52
53 foo.number = 5;
54 foo.product = g_strdup (str: "packages of noodles");
55 foo.yummy = 50;
56 g_array_append_vals (array: articles, data: &foo, len: 1);
57
58 foo.number = 2;
59 foo.product = g_strdup (str: "packages of chocolate chip cookies");
60 foo.yummy = 90;
61 g_array_append_vals (array: articles, data: &foo, len: 1);
62
63 foo.number = 1;
64 foo.product = g_strdup (str: "can vanilla ice cream");
65 foo.yummy = 60;
66 g_array_append_vals (array: articles, data: &foo, len: 1);
67
68 foo.number = 6;
69 foo.product = g_strdup (str: "eggs");
70 foo.yummy = 10;
71 g_array_append_vals (array: articles, data: &foo, len: 1);
72}
73
74static GtkTreeModel *
75create_items_model (void)
76{
77 int i = 0;
78 GtkListStore *model;
79 GtkTreeIter iter;
80
81 /* create array */
82 articles = g_array_sized_new (FALSE, FALSE, element_size: sizeof (Item), reserved_size: 1);
83
84 add_items ();
85
86 /* create list store */
87 model = gtk_list_store_new (n_columns: NUM_ITEM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
88 G_TYPE_INT, G_TYPE_BOOLEAN);
89
90 /* add items */
91 for (i = 0; i < articles->len; i++)
92 {
93 gtk_list_store_append (list_store: model, iter: &iter);
94
95 gtk_list_store_set (list_store: model, iter: &iter,
96 COLUMN_ITEM_NUMBER,
97 g_array_index (articles, Item, i).number,
98 COLUMN_ITEM_PRODUCT,
99 g_array_index (articles, Item, i).product,
100 COLUMN_ITEM_YUMMY,
101 g_array_index (articles, Item, i).yummy,
102 -1);
103 }
104
105 return GTK_TREE_MODEL (model);
106}
107
108static GtkTreeModel *
109create_numbers_model (void)
110{
111#define N_NUMBERS 10
112 int i = 0;
113 GtkListStore *model;
114 GtkTreeIter iter;
115
116 /* create list store */
117 model = gtk_list_store_new (n_columns: NUM_NUMBER_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
118
119 /* add numbers */
120 for (i = 0; i < N_NUMBERS; i++)
121 {
122 char str[2];
123
124 str[0] = '0' + i;
125 str[1] = '\0';
126
127 gtk_list_store_append (list_store: model, iter: &iter);
128
129 gtk_list_store_set (list_store: model, iter: &iter,
130 COLUMN_NUMBER_TEXT, str,
131 -1);
132 }
133
134 return GTK_TREE_MODEL (model);
135
136#undef N_NUMBERS
137}
138
139static void
140add_item (GtkWidget *button, gpointer data)
141{
142 Item foo;
143 GtkTreeIter current, iter;
144 GtkTreePath *path;
145 GtkTreeModel *model;
146 GtkTreeViewColumn *column;
147 GtkTreeView *treeview = (GtkTreeView *)data;
148
149 g_return_if_fail (articles != NULL);
150
151 foo.number = 0;
152 foo.product = g_strdup (str: "Description here");
153 foo.yummy = 50;
154 g_array_append_vals (array: articles, data: &foo, len: 1);
155
156 /* Insert a new row below the current one */
157 gtk_tree_view_get_cursor (tree_view: treeview, path: &path, NULL);
158 model = gtk_tree_view_get_model (tree_view: treeview);
159 if (path)
160 {
161 gtk_tree_model_get_iter (tree_model: model, iter: &current, path);
162 gtk_tree_path_free (path);
163 gtk_list_store_insert_after (GTK_LIST_STORE (model), iter: &iter, sibling: &current);
164 }
165 else
166 {
167 gtk_list_store_insert (GTK_LIST_STORE (model), iter: &iter, position: -1);
168 }
169
170 /* Set the data for the new row */
171 gtk_list_store_set (GTK_LIST_STORE (model), iter: &iter,
172 COLUMN_ITEM_NUMBER, foo.number,
173 COLUMN_ITEM_PRODUCT, foo.product,
174 COLUMN_ITEM_YUMMY, foo.yummy,
175 -1);
176
177 /* Move focus to the new row */
178 path = gtk_tree_model_get_path (tree_model: model, iter: &iter);
179 column = gtk_tree_view_get_column (tree_view: treeview, n: 0);
180 gtk_tree_view_set_cursor (tree_view: treeview, path, focus_column: column, FALSE);
181
182 gtk_tree_path_free (path);
183}
184
185static void
186remove_item (GtkWidget *widget, gpointer data)
187{
188 GtkTreeIter iter;
189 GtkTreeView *treeview = (GtkTreeView *)data;
190 GtkTreeModel *model = gtk_tree_view_get_model (tree_view: treeview);
191 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view: treeview);
192
193 if (gtk_tree_selection_get_selected (selection, NULL, iter: &iter))
194 {
195 int i;
196 GtkTreePath *path;
197
198 path = gtk_tree_model_get_path (tree_model: model, iter: &iter);
199 i = gtk_tree_path_get_indices (path)[0];
200 gtk_list_store_remove (GTK_LIST_STORE (model), iter: &iter);
201
202 g_array_remove_index (array: articles, index_: i);
203
204 gtk_tree_path_free (path);
205 }
206}
207
208static gboolean
209separator_row (GtkTreeModel *model,
210 GtkTreeIter *iter,
211 gpointer data)
212{
213 GtkTreePath *path;
214 int idx;
215
216 path = gtk_tree_model_get_path (tree_model: model, iter);
217 idx = gtk_tree_path_get_indices (path)[0];
218
219 gtk_tree_path_free (path);
220
221 return idx == 5;
222}
223
224static void
225editing_started (GtkCellRenderer *cell,
226 GtkCellEditable *editable,
227 const char *path,
228 gpointer data)
229{
230 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (editable),
231 func: separator_row, NULL, NULL);
232}
233
234static void
235cell_edited (GtkCellRendererText *cell,
236 const char *path_string,
237 const char *new_text,
238 gpointer data)
239{
240 GtkTreeModel *model = (GtkTreeModel *)data;
241 GtkTreePath *path = gtk_tree_path_new_from_string (path: path_string);
242 GtkTreeIter iter;
243
244 int column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
245
246 gtk_tree_model_get_iter (tree_model: model, iter: &iter, path);
247
248 switch (column)
249 {
250 case COLUMN_ITEM_NUMBER:
251 {
252 int i;
253
254 i = gtk_tree_path_get_indices (path)[0];
255 g_array_index (articles, Item, i).number = atoi (nptr: new_text);
256
257 gtk_list_store_set (GTK_LIST_STORE (model), iter: &iter, column,
258 g_array_index (articles, Item, i).number, -1);
259 }
260 break;
261
262 case COLUMN_ITEM_PRODUCT:
263 {
264 int i;
265 char *old_text;
266
267 gtk_tree_model_get (tree_model: model, iter: &iter, column, &old_text, -1);
268 g_free (mem: old_text);
269
270 i = gtk_tree_path_get_indices (path)[0];
271 g_free (g_array_index (articles, Item, i).product);
272 g_array_index (articles, Item, i).product = g_strdup (str: new_text);
273
274 gtk_list_store_set (GTK_LIST_STORE (model), iter: &iter, column,
275 g_array_index (articles, Item, i).product, -1);
276 }
277 break;
278
279 default:
280 g_assert_not_reached ();
281 }
282
283 gtk_tree_path_free (path);
284}
285
286static void
287add_columns (GtkTreeView *treeview,
288 GtkTreeModel *items_model,
289 GtkTreeModel *numbers_model)
290{
291 GtkCellRenderer *renderer;
292
293 /* number column */
294 renderer = gtk_cell_renderer_combo_new ();
295 g_object_set (object: renderer,
296 first_property_name: "model", numbers_model,
297 "text-column", COLUMN_NUMBER_TEXT,
298 "has-entry", FALSE,
299 "editable", TRUE,
300 NULL);
301 g_signal_connect (renderer, "edited",
302 G_CALLBACK (cell_edited), items_model);
303 g_signal_connect (renderer, "editing-started",
304 G_CALLBACK (editing_started), NULL);
305 g_object_set_data (G_OBJECT (renderer), key: "column", GINT_TO_POINTER (COLUMN_ITEM_NUMBER));
306
307 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
308 position: -1, title: "Number", cell: renderer,
309 "text", COLUMN_ITEM_NUMBER,
310 NULL);
311
312 /* product column */
313 renderer = gtk_cell_renderer_text_new ();
314 g_object_set (object: renderer,
315 first_property_name: "editable", TRUE,
316 NULL);
317 g_signal_connect (renderer, "edited",
318 G_CALLBACK (cell_edited), items_model);
319 g_object_set_data (G_OBJECT (renderer), key: "column", GINT_TO_POINTER (COLUMN_ITEM_PRODUCT));
320
321 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
322 position: -1, title: "Product", cell: renderer,
323 "text", COLUMN_ITEM_PRODUCT,
324 NULL);
325
326 /* yummy column */
327 renderer = gtk_cell_renderer_progress_new ();
328 g_object_set_data (G_OBJECT (renderer), key: "column", GINT_TO_POINTER (COLUMN_ITEM_YUMMY));
329
330 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
331 position: -1, title: "Yummy", cell: renderer,
332 "value", COLUMN_ITEM_YUMMY,
333 NULL);
334}
335
336GtkWidget *
337do_editable_cells (GtkWidget *do_widget)
338{
339 static GtkWidget *window = NULL;
340
341 if (!window)
342 {
343 GtkWidget *vbox;
344 GtkWidget *hbox;
345 GtkWidget *sw;
346 GtkWidget *treeview;
347 GtkWidget *button;
348 GtkTreeModel *items_model;
349 GtkTreeModel *numbers_model;
350
351 window = gtk_window_new ();
352 gtk_window_set_display (GTK_WINDOW (window),
353 display: gtk_widget_get_display (widget: do_widget));
354 gtk_window_set_title (GTK_WINDOW (window), title: "Editable Cells");
355 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window);
356
357 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 5);
358 gtk_widget_set_margin_start (widget: vbox, margin: 5);
359 gtk_widget_set_margin_end (widget: vbox, margin: 5);
360 gtk_widget_set_margin_top (widget: vbox, margin: 5);
361 gtk_widget_set_margin_bottom (widget: vbox, margin: 5);
362 gtk_window_set_child (GTK_WINDOW (window), child: vbox);
363
364 gtk_box_append (GTK_BOX (vbox),
365 child: gtk_label_new (str: "Shopping list (you can edit the cells!)"));
366
367 sw = gtk_scrolled_window_new ();
368 gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
369 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
370 hscrollbar_policy: GTK_POLICY_AUTOMATIC,
371 vscrollbar_policy: GTK_POLICY_AUTOMATIC);
372 gtk_box_append (GTK_BOX (vbox), child: sw);
373
374 /* create models */
375 items_model = create_items_model ();
376 numbers_model = create_numbers_model ();
377
378 /* create tree view */
379 treeview = gtk_tree_view_new_with_model (model: items_model);
380 gtk_widget_set_vexpand (widget: treeview, TRUE);
381 gtk_tree_selection_set_mode (selection: gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
382 type: GTK_SELECTION_SINGLE);
383
384 add_columns (GTK_TREE_VIEW (treeview), items_model, numbers_model);
385
386 g_object_unref (object: numbers_model);
387 g_object_unref (object: items_model);
388
389 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: treeview);
390
391 /* some buttons */
392 hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 4);
393 gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
394 gtk_box_append (GTK_BOX (vbox), child: hbox);
395
396 button = gtk_button_new_with_label (label: "Add item");
397 g_signal_connect (button, "clicked",
398 G_CALLBACK (add_item), treeview);
399 gtk_box_append (GTK_BOX (hbox), child: button);
400
401 button = gtk_button_new_with_label (label: "Remove item");
402 g_signal_connect (button, "clicked",
403 G_CALLBACK (remove_item), treeview);
404 gtk_box_append (GTK_BOX (hbox), child: button);
405
406 gtk_window_set_default_size (GTK_WINDOW (window), width: 320, height: 200);
407 }
408
409 if (!gtk_widget_get_visible (widget: window))
410 gtk_widget_show (widget: window);
411 else
412 gtk_window_destroy (GTK_WINDOW (window));
413
414 return window;
415}
416

source code of gtk/demos/gtk-demo/editable_cells.c