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

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