]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/editable_cells.c
Accept dashes in demo titles.
[~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] = { '0' + i, '\0' };
125
126       gtk_list_store_append (model, &iter);
127
128       gtk_list_store_set (model, &iter,
129                           COLUMN_NUMBER_TEXT, str,
130                           -1);
131     }
132
133   return GTK_TREE_MODEL (model);
134
135 #undef N_NUMBERS
136 }
137
138 static void
139 add_item (GtkWidget *button, gpointer data)
140 {
141   Item foo;
142   GtkTreeIter iter;
143   GtkTreeModel *model = (GtkTreeModel *)data;
144
145   g_return_if_fail (articles != NULL);
146
147   foo.number = 0;
148   foo.product = g_strdup ("Description here");
149   foo.yummy = 50;
150   g_array_append_vals (articles, &foo, 1);
151
152   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
153   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
154                       COLUMN_ITEM_NUMBER, foo.number,
155                       COLUMN_ITEM_PRODUCT, foo.product,
156                       COLUMN_ITEM_YUMMY, foo.yummy,
157                       -1);
158 }
159
160 static void
161 remove_item (GtkWidget *widget, gpointer data)
162 {
163   GtkTreeIter iter;
164   GtkTreeView *treeview = (GtkTreeView *)data;
165   GtkTreeModel *model = gtk_tree_view_get_model (treeview);
166   GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
167
168   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
169     {
170       gint i;
171       GtkTreePath *path;
172
173       path = gtk_tree_model_get_path (model, &iter);
174       i = gtk_tree_path_get_indices (path)[0];
175       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
176
177       g_array_remove_index (articles, i);
178
179       gtk_tree_path_free (path);
180     }
181 }
182
183 static gboolean
184 separator_row (GtkTreeModel *model,
185                GtkTreeIter  *iter,
186                gpointer      data)
187 {
188   GtkTreePath *path;
189   gint idx;
190
191   path = gtk_tree_model_get_path (model, iter);
192   idx = gtk_tree_path_get_indices (path)[0];
193
194   gtk_tree_path_free (path);
195
196   return idx == 5;
197 }
198
199 static void
200 editing_started (GtkCellRenderer *cell,
201                  GtkCellEditable *editable,
202                  const gchar     *path,
203                  gpointer         data)
204 {
205   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (editable), 
206                                         separator_row, NULL, NULL);
207 }
208
209 static void
210 cell_edited (GtkCellRendererText *cell,
211              const gchar         *path_string,
212              const gchar         *new_text,
213              gpointer             data)
214 {
215   GtkTreeModel *model = (GtkTreeModel *)data;
216   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
217   GtkTreeIter iter;
218
219   gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
220
221   gtk_tree_model_get_iter (model, &iter, path);
222
223   switch (column)
224     {
225     case COLUMN_ITEM_NUMBER:
226       {
227         gint i;
228
229         i = gtk_tree_path_get_indices (path)[0];
230         g_array_index (articles, Item, i).number = atoi (new_text);
231
232         gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
233                             g_array_index (articles, Item, i).number, -1);
234       }
235       break;
236
237     case COLUMN_ITEM_PRODUCT:
238       {
239         gint i;
240         gchar *old_text;
241
242         gtk_tree_model_get (model, &iter, column, &old_text, -1);
243         g_free (old_text);
244
245         i = gtk_tree_path_get_indices (path)[0];
246         g_free (g_array_index (articles, Item, i).product);
247         g_array_index (articles, Item, i).product = g_strdup (new_text);
248
249         gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
250                             g_array_index (articles, Item, i).product, -1);
251       }
252       break;
253     }
254
255   gtk_tree_path_free (path);
256 }
257
258 static void
259 add_columns (GtkTreeView  *treeview, 
260              GtkTreeModel *items_model,
261              GtkTreeModel *numbers_model)
262 {
263   GtkCellRenderer *renderer;
264
265   /* number column */
266   renderer = gtk_cell_renderer_combo_new ();
267   g_object_set (renderer,
268                 "model", numbers_model,
269                 "text-column", COLUMN_NUMBER_TEXT,
270                 "has-entry", FALSE,
271                 "editable", TRUE,
272                 NULL);
273   g_signal_connect (renderer, "edited",
274                     G_CALLBACK (cell_edited), items_model);
275   g_signal_connect (renderer, "editing-started",
276                     G_CALLBACK (editing_started), NULL);
277   g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_NUMBER));
278
279   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
280                                                -1, "Number", renderer,
281                                                "text", COLUMN_ITEM_NUMBER,
282                                                NULL);
283
284   /* product column */
285   renderer = gtk_cell_renderer_text_new ();
286   g_object_set (renderer,
287                 "editable", TRUE,
288                 NULL);
289   g_signal_connect (renderer, "edited",
290                     G_CALLBACK (cell_edited), items_model);
291   g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_PRODUCT));
292
293   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
294                                                -1, "Product", renderer,
295                                                "text", COLUMN_ITEM_PRODUCT,
296                                                NULL);
297
298   /* yummy column */
299   renderer = gtk_cell_renderer_progress_new ();
300   g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_YUMMY));
301
302   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
303                                                -1, "Yummy", renderer,
304                                                "value", COLUMN_ITEM_YUMMY,
305                                                NULL);
306   
307
308 }
309
310 GtkWidget *
311 do_editable_cells (GtkWidget *do_widget)
312 {
313   if (!window)
314     {
315       GtkWidget *vbox;
316       GtkWidget *hbox;
317       GtkWidget *sw;
318       GtkWidget *treeview;
319       GtkWidget *button;
320       GtkTreeModel *items_model;
321       GtkTreeModel *numbers_model;
322
323       /* create window, etc */
324       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
325       gtk_window_set_screen (GTK_WINDOW (window),
326                              gtk_widget_get_screen (do_widget));
327       gtk_window_set_title (GTK_WINDOW (window), "Shopping list");
328       gtk_container_set_border_width (GTK_CONTAINER (window), 5);
329       g_signal_connect (window, "destroy",
330                         G_CALLBACK (gtk_widget_destroyed), &window);
331
332       vbox = gtk_vbox_new (FALSE, 5);
333       gtk_container_add (GTK_CONTAINER (window), vbox);
334
335       gtk_box_pack_start (GTK_BOX (vbox),
336                           gtk_label_new ("Shopping list (you can edit the cells!)"),
337                           FALSE, FALSE, 0);
338
339       sw = gtk_scrolled_window_new (NULL, NULL);
340       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
341                                            GTK_SHADOW_ETCHED_IN);
342       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
343                                       GTK_POLICY_AUTOMATIC,
344                                       GTK_POLICY_AUTOMATIC);
345       gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
346
347       /* create models */
348       items_model = create_items_model ();
349       numbers_model = create_numbers_model ();
350
351       /* create tree view */
352       treeview = gtk_tree_view_new_with_model (items_model);
353       gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
354       gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
355                                    GTK_SELECTION_SINGLE);
356
357       add_columns (GTK_TREE_VIEW (treeview), items_model, numbers_model);
358
359       g_object_unref (numbers_model);
360       g_object_unref (items_model);
361
362       gtk_container_add (GTK_CONTAINER (sw), treeview);
363
364       /* some buttons */
365       hbox = gtk_hbox_new (TRUE, 4);
366       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
367
368       button = gtk_button_new_with_label ("Add item");
369       g_signal_connect (button, "clicked",
370                         G_CALLBACK (add_item), items_model);
371       gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
372
373       button = gtk_button_new_with_label ("Remove item");
374       g_signal_connect (button, "clicked",
375                         G_CALLBACK (remove_item), treeview);
376       gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
377
378       gtk_window_set_default_size (GTK_WINDOW (window), 320, 200);
379     }
380
381   if (!GTK_WIDGET_VISIBLE (window))
382     gtk_widget_show_all (window);
383   else
384     {
385       gtk_widget_destroy (window);
386       window = NULL;
387     }
388
389   return window;
390 }