]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/editable_cells.c
1975e06b2ebd8ba91d5ccd641eb1be338eb76575
[~andy/gtk] / demos / gtk-demo / editable_cells.c
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
17 static GtkWidget *window = NULL;
18
19 typedef struct
20 {
21   gint   number;
22   gchar *product;
23   gint   yummy;
24 }
25 Item;
26
27 enum
28 {
29   COLUMN_ITEM_NUMBER,
30   COLUMN_ITEM_PRODUCT,
31   COLUMN_ITEM_YUMMY,
32   NUM_ITEM_COLUMNS
33 };
34
35 enum
36 {
37   COLUMN_NUMBER_TEXT,
38   NUM_NUMBER_COLUMNS
39 };
40
41 static GArray *articles = NULL;
42
43 static void
44 add_items (void)
45 {
46   Item foo;
47
48   g_return_if_fail (articles != NULL);
49
50   foo.number = 3;
51   foo.product = g_strdup ("bottles of coke");
52   foo.yummy = 20;
53   g_array_append_vals (articles, &foo, 1);
54
55   foo.number = 5;
56   foo.product = g_strdup ("packages of noodles");
57   foo.yummy = 50;
58   g_array_append_vals (articles, &foo, 1);
59
60   foo.number = 2;
61   foo.product = g_strdup ("packages of chocolate chip cookies");
62   foo.yummy = 90;
63   g_array_append_vals (articles, &foo, 1);
64
65   foo.number = 1;
66   foo.product = g_strdup ("can vanilla ice cream");
67   foo.yummy = 60;
68   g_array_append_vals (articles, &foo, 1);
69
70   foo.number = 6;
71   foo.product = g_strdup ("eggs");
72   foo.yummy = 10;
73   g_array_append_vals (articles, &foo, 1);
74 }
75
76 static GtkTreeModel *
77 create_items_model (void)
78 {
79   gint i = 0;
80   GtkListStore *model;
81   GtkTreeIter iter;
82
83   /* create array */
84   articles = g_array_sized_new (FALSE, FALSE, sizeof (Item), 1);
85
86   add_items ();
87
88   /* create list store */
89   model = gtk_list_store_new (NUM_ITEM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
90                               G_TYPE_INT, G_TYPE_BOOLEAN);
91
92   /* add items */
93   for (i = 0; i < articles->len; i++)
94     {
95       gtk_list_store_append (model, &iter);
96
97       gtk_list_store_set (model, &iter,
98                           COLUMN_ITEM_NUMBER,
99                           g_array_index (articles, Item, i).number,
100                           COLUMN_ITEM_PRODUCT,
101                           g_array_index (articles, Item, i).product,
102                           COLUMN_ITEM_YUMMY,
103                           g_array_index (articles, Item, i).yummy,
104                           -1);
105     }
106
107   return GTK_TREE_MODEL (model);
108 }
109
110 static GtkTreeModel *
111 create_numbers_model (void)
112 {
113 #define N_NUMBERS 10
114   gint i = 0;
115   GtkListStore *model;
116   GtkTreeIter iter;
117
118   /* create list store */
119   model = gtk_list_store_new (NUM_NUMBER_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
120
121   /* add numbers */
122   for (i = 0; i < N_NUMBERS; i++)
123     {
124       char str[2];
125
126       str[0] = '0' + i;
127       str[1] = '\0';
128
129       gtk_list_store_append (model, &iter);
130
131       gtk_list_store_set (model, &iter,
132                           COLUMN_NUMBER_TEXT, str,
133                           -1);
134     }
135
136   return GTK_TREE_MODEL (model);
137
138 #undef N_NUMBERS
139 }
140
141 static void
142 add_item (GtkWidget *button, gpointer data)
143 {
144   Item foo;
145   GtkTreeIter iter;
146   GtkTreeModel *model = (GtkTreeModel *)data;
147
148   g_return_if_fail (articles != NULL);
149
150   foo.number = 0;
151   foo.product = g_strdup ("Description here");
152   foo.yummy = 50;
153   g_array_append_vals (articles, &foo, 1);
154
155   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
156   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
157                       COLUMN_ITEM_NUMBER, foo.number,
158                       COLUMN_ITEM_PRODUCT, foo.product,
159                       COLUMN_ITEM_YUMMY, foo.yummy,
160                       -1);
161 }
162
163 static void
164 remove_item (GtkWidget *widget, gpointer data)
165 {
166   GtkTreeIter iter;
167   GtkTreeView *treeview = (GtkTreeView *)data;
168   GtkTreeModel *model = gtk_tree_view_get_model (treeview);
169   GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
170
171   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
172     {
173       gint i;
174       GtkTreePath *path;
175
176       path = gtk_tree_model_get_path (model, &iter);
177       i = gtk_tree_path_get_indices (path)[0];
178       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
179
180       g_array_remove_index (articles, i);
181
182       gtk_tree_path_free (path);
183     }
184 }
185
186 static gboolean
187 separator_row (GtkTreeModel *model,
188                GtkTreeIter  *iter,
189                gpointer      data)
190 {
191   GtkTreePath *path;
192   gint idx;
193
194   path = gtk_tree_model_get_path (model, iter);
195   idx = gtk_tree_path_get_indices (path)[0];
196
197   gtk_tree_path_free (path);
198
199   return idx == 5;
200 }
201
202 static void
203 editing_started (GtkCellRenderer *cell,
204                  GtkCellEditable *editable,
205                  const gchar     *path,
206                  gpointer         data)
207 {
208   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (editable), 
209                                         separator_row, NULL, NULL);
210 }
211
212 static void
213 cell_edited (GtkCellRendererText *cell,
214              const gchar         *path_string,
215              const gchar         *new_text,
216              gpointer             data)
217 {
218   GtkTreeModel *model = (GtkTreeModel *)data;
219   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
220   GtkTreeIter iter;
221
222   gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
223
224   gtk_tree_model_get_iter (model, &iter, path);
225
226   switch (column)
227     {
228     case COLUMN_ITEM_NUMBER:
229       {
230         gint i;
231
232         i = gtk_tree_path_get_indices (path)[0];
233         g_array_index (articles, Item, i).number = atoi (new_text);
234
235         gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
236                             g_array_index (articles, Item, i).number, -1);
237       }
238       break;
239
240     case COLUMN_ITEM_PRODUCT:
241       {
242         gint i;
243         gchar *old_text;
244
245         gtk_tree_model_get (model, &iter, column, &old_text, -1);
246         g_free (old_text);
247
248         i = gtk_tree_path_get_indices (path)[0];
249         g_free (g_array_index (articles, Item, i).product);
250         g_array_index (articles, Item, i).product = g_strdup (new_text);
251
252         gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
253                             g_array_index (articles, Item, i).product, -1);
254       }
255       break;
256     }
257
258   gtk_tree_path_free (path);
259 }
260
261 static void
262 add_columns (GtkTreeView  *treeview, 
263              GtkTreeModel *items_model,
264              GtkTreeModel *numbers_model)
265 {
266   GtkCellRenderer *renderer;
267
268   /* number column */
269   renderer = gtk_cell_renderer_combo_new ();
270   g_object_set (renderer,
271                 "model", numbers_model,
272                 "text-column", COLUMN_NUMBER_TEXT,
273                 "has-entry", FALSE,
274                 "editable", TRUE,
275                 NULL);
276   g_signal_connect (renderer, "edited",
277                     G_CALLBACK (cell_edited), items_model);
278   g_signal_connect (renderer, "editing-started",
279                     G_CALLBACK (editing_started), NULL);
280   g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_NUMBER));
281
282   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
283                                                -1, "Number", renderer,
284                                                "text", COLUMN_ITEM_NUMBER,
285                                                NULL);
286
287   /* product column */
288   renderer = gtk_cell_renderer_text_new ();
289   g_object_set (renderer,
290                 "editable", TRUE,
291                 NULL);
292   g_signal_connect (renderer, "edited",
293                     G_CALLBACK (cell_edited), items_model);
294   g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_PRODUCT));
295
296   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
297                                                -1, "Product", renderer,
298                                                "text", COLUMN_ITEM_PRODUCT,
299                                                NULL);
300
301   /* yummy column */
302   renderer = gtk_cell_renderer_progress_new ();
303   g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_YUMMY));
304
305   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
306                                                -1, "Yummy", renderer,
307                                                "value", COLUMN_ITEM_YUMMY,
308                                                NULL);
309   
310
311 }
312
313 GtkWidget *
314 do_editable_cells (GtkWidget *do_widget)
315 {
316   if (!window)
317     {
318       GtkWidget *vbox;
319       GtkWidget *hbox;
320       GtkWidget *sw;
321       GtkWidget *treeview;
322       GtkWidget *button;
323       GtkTreeModel *items_model;
324       GtkTreeModel *numbers_model;
325
326       /* create window, etc */
327       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
328       gtk_window_set_screen (GTK_WINDOW (window),
329                              gtk_widget_get_screen (do_widget));
330       gtk_window_set_title (GTK_WINDOW (window), "Shopping list");
331       gtk_container_set_border_width (GTK_CONTAINER (window), 5);
332       g_signal_connect (window, "destroy",
333                         G_CALLBACK (gtk_widget_destroyed), &window);
334
335       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 5);
336       gtk_container_add (GTK_CONTAINER (window), vbox);
337
338       gtk_box_pack_start (GTK_BOX (vbox),
339                           gtk_label_new ("Shopping list (you can edit the cells!)"),
340                           FALSE, FALSE, 0);
341
342       sw = gtk_scrolled_window_new (NULL, NULL);
343       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
344                                            GTK_SHADOW_ETCHED_IN);
345       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
346                                       GTK_POLICY_AUTOMATIC,
347                                       GTK_POLICY_AUTOMATIC);
348       gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
349
350       /* create models */
351       items_model = create_items_model ();
352       numbers_model = create_numbers_model ();
353
354       /* create tree view */
355       treeview = gtk_tree_view_new_with_model (items_model);
356       gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
357       gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
358                                    GTK_SELECTION_SINGLE);
359
360       add_columns (GTK_TREE_VIEW (treeview), items_model, numbers_model);
361
362       g_object_unref (numbers_model);
363       g_object_unref (items_model);
364
365       gtk_container_add (GTK_CONTAINER (sw), treeview);
366
367       /* some buttons */
368       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, TRUE, 4);
369       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
370
371       button = gtk_button_new_with_label ("Add item");
372       g_signal_connect (button, "clicked",
373                         G_CALLBACK (add_item), items_model);
374       gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
375
376       button = gtk_button_new_with_label ("Remove item");
377       g_signal_connect (button, "clicked",
378                         G_CALLBACK (remove_item), treeview);
379       gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
380
381       gtk_window_set_default_size (GTK_WINDOW (window), 320, 200);
382     }
383
384   if (!gtk_widget_get_visible (window))
385     gtk_widget_show_all (window);
386   else
387     {
388       gtk_widget_destroy (window);
389       window = NULL;
390     }
391
392   return window;
393 }