]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/combobox.c
Use gtk_box_new() instead gtk_[v|h]box_new()
[~andy/gtk] / demos / gtk-demo / combobox.c
1 /* Combo boxes 
2  *
3  * The ComboBox widget allows to select one option out of a list.
4  * The ComboBoxEntry additionally allows the user to enter a value
5  * that is not in the list of options. 
6  *
7  * How the options are displayed is controlled by cell renderers.
8  */
9
10 #include <gtk/gtk.h>
11
12 enum 
13 {
14   PIXBUF_COL,
15   TEXT_COL
16 };
17
18 static gchar *
19 strip_underscore (const gchar *text)
20 {
21   gchar *p, *q;
22   gchar *result;
23   
24   result = g_strdup (text);
25   p = q = result;
26   while (*p) 
27     {
28       if (*p != '_')
29         {
30           *q = *p;
31           q++;
32         }
33       p++;
34     }
35   *q = '\0';
36
37   return result;
38 }
39
40 static GtkTreeModel *
41 create_stock_icon_store (void)
42 {
43   gchar *stock_id[6] = {
44     GTK_STOCK_DIALOG_WARNING,
45     GTK_STOCK_STOP,
46     GTK_STOCK_NEW,
47     GTK_STOCK_CLEAR,
48     NULL,
49     GTK_STOCK_OPEN    
50   };
51
52   GtkStockItem item;
53   GdkPixbuf *pixbuf;
54   GtkWidget *cellview;
55   GtkTreeIter iter;
56   GtkListStore *store;
57   gchar *label;
58   gint i;
59
60   cellview = gtk_cell_view_new ();
61   
62   store = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
63
64   for (i = 0; i < G_N_ELEMENTS (stock_id); i++)
65     {
66       if (stock_id[i])
67         {
68           pixbuf = gtk_widget_render_icon (cellview, stock_id[i],
69                                            GTK_ICON_SIZE_BUTTON, NULL);
70           gtk_stock_lookup (stock_id[i], &item);
71           label = strip_underscore (item.label);
72           gtk_list_store_append (store, &iter);
73           gtk_list_store_set (store, &iter,
74                               PIXBUF_COL, pixbuf,
75                               TEXT_COL, label,
76                               -1);
77           g_object_unref (pixbuf);
78           g_free (label);
79         }
80       else
81         {
82           gtk_list_store_append (store, &iter);
83           gtk_list_store_set (store, &iter,
84                               PIXBUF_COL, NULL,
85                               TEXT_COL, "separator",
86                               -1);
87         }
88     }
89
90   gtk_widget_destroy (cellview);
91   
92   return GTK_TREE_MODEL (store);
93 }
94
95 /* A GtkCellLayoutDataFunc that demonstrates how one can control
96  * sensitivity of rows. This particular function does nothing 
97  * useful and just makes the second row insensitive.
98  */
99 static void
100 set_sensitive (GtkCellLayout   *cell_layout,
101                GtkCellRenderer *cell,
102                GtkTreeModel    *tree_model,
103                GtkTreeIter     *iter,
104                gpointer         data)
105 {
106   GtkTreePath *path;
107   gint *indices;
108   gboolean sensitive;
109
110   path = gtk_tree_model_get_path (tree_model, iter);
111   indices = gtk_tree_path_get_indices (path);
112   sensitive = indices[0] != 1;
113   gtk_tree_path_free (path);
114
115   g_object_set (cell, "sensitive", sensitive, NULL);
116 }
117
118 /* A GtkTreeViewRowSeparatorFunc that demonstrates how rows can be
119  * rendered as separators. This particular function does nothing 
120  * useful and just turns the fourth row into a separator.
121  */
122 static gboolean
123 is_separator (GtkTreeModel *model,
124               GtkTreeIter  *iter,
125               gpointer      data)
126 {
127   GtkTreePath *path;
128   gboolean result;
129
130   path = gtk_tree_model_get_path (model, iter);
131   result = gtk_tree_path_get_indices (path)[0] == 4;
132   gtk_tree_path_free (path);
133
134   return result;
135 }
136
137 static GtkTreeModel *
138 create_capital_store (void)
139 {
140   struct {
141     gchar *group;
142     gchar *capital;
143   } capitals[] = {
144     { "A - B", NULL }, 
145     { NULL, "Albany" },
146     { NULL, "Annapolis" },
147     { NULL, "Atlanta" },
148     { NULL, "Augusta" }, 
149     { NULL, "Austin" },
150     { NULL, "Baton Rouge" },
151     { NULL, "Bismarck" },
152     { NULL, "Boise" },
153     { NULL, "Boston" },
154     { "C - D", NULL },
155     { NULL, "Carson City" },
156     { NULL, "Charleston" },
157     { NULL, "Cheyenne" },
158     { NULL, "Columbia" },
159     { NULL, "Columbus" },
160     { NULL, "Concord" },
161     { NULL, "Denver" },
162     { NULL, "Des Moines" },
163     { NULL, "Dover" },
164     { "E - J", NULL },
165     { NULL, "Frankfort" },
166     { NULL, "Harrisburg" },
167     { NULL, "Hartford" },
168     { NULL, "Helena" },
169     { NULL, "Honolulu" },
170     { NULL, "Indianapolis" },
171     { NULL, "Jackson" },
172     { NULL, "Jefferson City" },
173     { NULL, "Juneau" },
174     { "K - O" },
175     { NULL, "Lansing" },
176     { NULL, "Lincoln" },
177     { NULL, "Little Rock" },
178     { NULL, "Madison" },
179     { NULL, "Montgomery" },
180     { NULL, "Montpelier" },
181     { NULL, "Nashville" },
182     { NULL, "Oklahoma City" },
183     { NULL, "Olympia" },
184     { NULL, "P - S" },
185     { NULL, "Phoenix" },
186     { NULL, "Pierre" },
187     { NULL, "Providence" },
188     { NULL, "Raleigh" },
189     { NULL, "Richmond" },
190     { NULL, "Sacramento" },
191     { NULL, "Salem" },
192     { NULL, "Salt Lake City" },
193     { NULL, "Santa Fe" },
194     { NULL, "Springfield" },
195     { NULL, "St. Paul" },
196     { "T - Z", NULL },
197     { NULL, "Tallahassee" },
198     { NULL, "Topeka" },
199     { NULL, "Trenton" },
200     { NULL, NULL }
201   };
202   
203   GtkTreeIter iter, iter2;
204   GtkTreeStore *store;
205   gint i;
206
207   store = gtk_tree_store_new (1, G_TYPE_STRING);
208   
209   for (i = 0; capitals[i].group || capitals[i].capital; i++)
210     {
211       if (capitals[i].group)
212         {
213           gtk_tree_store_append (store, &iter, NULL);
214           gtk_tree_store_set (store, &iter, 0, capitals[i].group, -1);
215         }
216       else if (capitals[i].capital)
217         {
218           gtk_tree_store_append (store, &iter2, &iter);
219           gtk_tree_store_set (store, &iter2, 0, capitals[i].capital, -1);
220         }
221     }
222   
223   return GTK_TREE_MODEL (store);
224 }
225
226 static void
227 is_capital_sensitive (GtkCellLayout   *cell_layout,
228                       GtkCellRenderer *cell,
229                       GtkTreeModel    *tree_model,
230                       GtkTreeIter     *iter,
231                       gpointer         data)
232 {
233   gboolean sensitive;
234
235   sensitive = !gtk_tree_model_iter_has_child (tree_model, iter);
236
237   g_object_set (cell, "sensitive", sensitive, NULL);
238 }
239
240 static void
241 fill_combo_entry (GtkWidget *combo)
242 {
243   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "One");
244   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Two");
245   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "2\302\275");
246   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Three");
247 }
248
249
250 /* A simple validating entry */
251
252 #define TYPE_MASK_ENTRY             (mask_entry_get_type ())
253 #define MASK_ENTRY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MASK_ENTRY, MaskEntry))
254 #define MASK_ENTRY_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), TYPE_MASK_ENTRY, MaskEntryClass))
255 #define IS_MASK_ENTRY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MASK_ENTRY))
256 #define IS_MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), TYPE_MASK_ENTRY))
257 #define MASK_ENTRY_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), TYPE_MASK_ENTRY, MaskEntryClass))
258
259
260 typedef struct _MaskEntry MaskEntry;
261 struct _MaskEntry
262 {
263   GtkEntry entry;
264   gchar *mask;
265 };
266
267 typedef struct _MaskEntryClass MaskEntryClass;
268 struct _MaskEntryClass
269 {
270   GtkEntryClass parent_class;
271 };
272
273
274 static void mask_entry_editable_init (GtkEditableInterface *iface);
275
276 G_DEFINE_TYPE_WITH_CODE (MaskEntry, mask_entry, GTK_TYPE_ENTRY,
277                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
278                                                 mask_entry_editable_init));
279
280
281 static void
282 mask_entry_set_background (MaskEntry *entry)
283 {
284   static const GdkColor error_color = { 0, 65535, 60000, 60000 };
285
286   if (entry->mask)
287     {
288       if (!g_regex_match_simple (entry->mask, gtk_entry_get_text (GTK_ENTRY (entry)), 0, 0))
289         {
290           gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, &error_color);
291           return;
292         }
293     }
294
295   gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, NULL);
296 }
297
298
299 static void
300 mask_entry_changed (GtkEditable *editable)
301 {
302   mask_entry_set_background (MASK_ENTRY (editable));
303 }
304
305
306 static void
307 mask_entry_init (MaskEntry *entry)
308 {
309   entry->mask = NULL;
310 }
311
312
313 static void
314 mask_entry_class_init (MaskEntryClass *klass)
315 { }
316
317
318 static void
319 mask_entry_editable_init (GtkEditableInterface *iface)
320 {
321   iface->changed = mask_entry_changed;
322 }
323
324
325 GtkWidget *
326 do_combobox (GtkWidget *do_widget)
327 {
328   static GtkWidget *window = NULL;
329   GtkWidget *vbox, *frame, *box, *combo, *entry;
330   GtkTreeModel *model;
331   GtkCellRenderer *renderer;
332   GtkTreePath *path;
333   GtkTreeIter iter;
334
335   if (!window)
336   {
337     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
338     gtk_window_set_screen (GTK_WINDOW (window),
339                            gtk_widget_get_screen (do_widget));
340     gtk_window_set_title (GTK_WINDOW (window), "Combo boxes");
341    
342     g_signal_connect (window, "destroy",
343                       G_CALLBACK (gtk_widget_destroyed),
344                       &window);
345     
346     gtk_container_set_border_width (GTK_CONTAINER (window), 10);
347
348     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 2);
349     gtk_container_add (GTK_CONTAINER (window), vbox);
350
351     /* A combobox demonstrating cell renderers, separators and
352      *  insensitive rows 
353      */
354     frame = gtk_frame_new ("Some stock icons");
355     gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
356     
357     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
358     gtk_container_set_border_width (GTK_CONTAINER (box), 5);
359     gtk_container_add (GTK_CONTAINER (frame), box);
360     
361     model = create_stock_icon_store ();
362     combo = gtk_combo_box_new_with_model (model);
363     g_object_unref (model);
364     gtk_container_add (GTK_CONTAINER (box), combo);
365     
366     renderer = gtk_cell_renderer_pixbuf_new ();
367     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
368     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
369                                     "pixbuf", PIXBUF_COL, 
370                                     NULL);
371
372     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
373                                         renderer,
374                                         set_sensitive,
375                                         NULL, NULL);
376     
377     renderer = gtk_cell_renderer_text_new ();
378     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
379     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
380                                     "text", TEXT_COL,
381                                     NULL);
382
383     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
384                                         renderer,
385                                         set_sensitive,
386                                         NULL, NULL);
387
388     gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo), 
389                                           is_separator, NULL, NULL);
390     
391     gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
392     
393     /* A combobox demonstrating trees.
394      */
395     frame = gtk_frame_new ("Where are we ?");
396     gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
397
398     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
399     gtk_container_set_border_width (GTK_CONTAINER (box), 5);
400     gtk_container_add (GTK_CONTAINER (frame), box);
401     
402     model = create_capital_store ();
403     combo = gtk_combo_box_new_with_model (model);
404     g_object_unref (model);
405     gtk_container_add (GTK_CONTAINER (box), combo);
406
407     renderer = gtk_cell_renderer_text_new ();
408     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
409     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
410                                     "text", 0,
411                                     NULL);
412     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
413                                         renderer,
414                                         is_capital_sensitive,
415                                         NULL, NULL);
416
417     path = gtk_tree_path_new_from_indices (0, 8, -1);
418     gtk_tree_model_get_iter (model, &iter, path);
419     gtk_tree_path_free (path);
420     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
421
422     /* A GtkComboBoxEntry with validation.
423      */
424     frame = gtk_frame_new ("Editable");
425     gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
426     
427     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
428     gtk_container_set_border_width (GTK_CONTAINER (box), 5);
429     gtk_container_add (GTK_CONTAINER (frame), box);
430     
431     combo = gtk_combo_box_text_new_with_entry ();
432     fill_combo_entry (combo);
433     gtk_container_add (GTK_CONTAINER (box), combo);
434     
435     entry = g_object_new (TYPE_MASK_ENTRY, NULL);
436     MASK_ENTRY (entry)->mask = "^([0-9]*|One|Two|2\302\275|Three)$";
437      
438     gtk_container_remove (GTK_CONTAINER (combo), gtk_bin_get_child (GTK_BIN (combo)));
439     gtk_container_add (GTK_CONTAINER (combo), entry);
440   
441   }
442
443   if (!gtk_widget_get_visible (window))
444     {
445       gtk_widget_show_all (window);
446     }
447   else
448     {    
449       gtk_widget_destroy (window);
450       window = NULL;
451     }
452
453   return window;
454 }