| /* Combo Boxes | 
 |  * | 
 |  * The GtkComboBox widget allows to select one option out of a list. | 
 |  * The GtkComboBoxEntry additionally allows the user to enter a value | 
 |  * that is not in the list of options. | 
 |  * | 
 |  * How the options are displayed is controlled by cell renderers. | 
 |  */ | 
 |  | 
 | #include <glib/gi18n.h> | 
 | #include <gtk/gtk.h> | 
 |  | 
 | enum | 
 | { | 
 |   ICON_NAME_COL, | 
 |   TEXT_COL | 
 | }; | 
 |  | 
 | static GtkTreeModel * | 
 | create_icon_store (void) | 
 | { | 
 |   const gchar *icon_names[6] = { | 
 |     "dialog-warning", | 
 |     "process-stop", | 
 |     "document-new", | 
 |     "edit-clear", | 
 |     NULL, | 
 |     "document-open" | 
 |   }; | 
 |   const gchar *labels[6] = { | 
 |     N_("Warning"), | 
 |     N_("Stop"), | 
 |     N_("New"), | 
 |     N_("Clear"), | 
 |     NULL, | 
 |     N_("Open") | 
 |   }; | 
 |  | 
 |   GtkTreeIter iter; | 
 |   GtkListStore *store; | 
 |   gint i; | 
 |  | 
 |   store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); | 
 |  | 
 |   for (i = 0; i < G_N_ELEMENTS (icon_names); i++) | 
 |     { | 
 |       if (icon_names[i]) | 
 |         { | 
 |           gtk_list_store_append (store, &iter); | 
 |           gtk_list_store_set (store, &iter, | 
 |                               ICON_NAME_COL, icon_names[i], | 
 |                               TEXT_COL, _(labels[i]), | 
 |                               -1); | 
 |         } | 
 |       else | 
 |         { | 
 |           gtk_list_store_append (store, &iter); | 
 |           gtk_list_store_set (store, &iter, | 
 |                               ICON_NAME_COL, NULL, | 
 |                               TEXT_COL, "separator", | 
 |                               -1); | 
 |         } | 
 |     } | 
 |  | 
 |   return GTK_TREE_MODEL (store); | 
 | } | 
 |  | 
 | /* A GtkCellLayoutDataFunc that demonstrates how one can control | 
 |  * sensitivity of rows. This particular function does nothing | 
 |  * useful and just makes the second row insensitive. | 
 |  */ | 
 | static void | 
 | set_sensitive (GtkCellLayout   *cell_layout, | 
 |                GtkCellRenderer *cell, | 
 |                GtkTreeModel    *tree_model, | 
 |                GtkTreeIter     *iter, | 
 |                gpointer         data) | 
 | { | 
 |   GtkTreePath *path; | 
 |   gint *indices; | 
 |   gboolean sensitive; | 
 |  | 
 |   path = gtk_tree_model_get_path (tree_model, iter); | 
 |   indices = gtk_tree_path_get_indices (path); | 
 |   sensitive = indices[0] != 1; | 
 |   gtk_tree_path_free (path); | 
 |  | 
 |   g_object_set (cell, "sensitive", sensitive, NULL); | 
 | } | 
 |  | 
 | /* A GtkTreeViewRowSeparatorFunc that demonstrates how rows can be | 
 |  * rendered as separators. This particular function does nothing | 
 |  * useful and just turns the fourth row into a separator. | 
 |  */ | 
 | static gboolean | 
 | is_separator (GtkTreeModel *model, | 
 |               GtkTreeIter  *iter, | 
 |               gpointer      data) | 
 | { | 
 |   GtkTreePath *path; | 
 |   gboolean result; | 
 |  | 
 |   path = gtk_tree_model_get_path (model, iter); | 
 |   result = gtk_tree_path_get_indices (path)[0] == 4; | 
 |   gtk_tree_path_free (path); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | static GtkTreeModel * | 
 | create_capital_store (void) | 
 | { | 
 |   struct { | 
 |     gchar *group; | 
 |     gchar *capital; | 
 |   } capitals[] = { | 
 |     { "A - B", NULL }, | 
 |     { NULL, "Albany" }, | 
 |     { NULL, "Annapolis" }, | 
 |     { NULL, "Atlanta" }, | 
 |     { NULL, "Augusta" }, | 
 |     { NULL, "Austin" }, | 
 |     { NULL, "Baton Rouge" }, | 
 |     { NULL, "Bismarck" }, | 
 |     { NULL, "Boise" }, | 
 |     { NULL, "Boston" }, | 
 |     { "C - D", NULL }, | 
 |     { NULL, "Carson City" }, | 
 |     { NULL, "Charleston" }, | 
 |     { NULL, "Cheyenne" }, | 
 |     { NULL, "Columbia" }, | 
 |     { NULL, "Columbus" }, | 
 |     { NULL, "Concord" }, | 
 |     { NULL, "Denver" }, | 
 |     { NULL, "Des Moines" }, | 
 |     { NULL, "Dover" }, | 
 |     { "E - J", NULL }, | 
 |     { NULL, "Frankfort" }, | 
 |     { NULL, "Harrisburg" }, | 
 |     { NULL, "Hartford" }, | 
 |     { NULL, "Helena" }, | 
 |     { NULL, "Honolulu" }, | 
 |     { NULL, "Indianapolis" }, | 
 |     { NULL, "Jackson" }, | 
 |     { NULL, "Jefferson City" }, | 
 |     { NULL, "Juneau" }, | 
 |     { "K - O", NULL }, | 
 |     { NULL, "Lansing" }, | 
 |     { NULL, "Lincoln" }, | 
 |     { NULL, "Little Rock" }, | 
 |     { NULL, "Madison" }, | 
 |     { NULL, "Montgomery" }, | 
 |     { NULL, "Montpelier" }, | 
 |     { NULL, "Nashville" }, | 
 |     { NULL, "Oklahoma City" }, | 
 |     { NULL, "Olympia" }, | 
 |     { "P - S", NULL }, | 
 |     { NULL, "Phoenix" }, | 
 |     { NULL, "Pierre" }, | 
 |     { NULL, "Providence" }, | 
 |     { NULL, "Raleigh" }, | 
 |     { NULL, "Richmond" }, | 
 |     { NULL, "Sacramento" }, | 
 |     { NULL, "Salem" }, | 
 |     { NULL, "Salt Lake City" }, | 
 |     { NULL, "Santa Fe" }, | 
 |     { NULL, "Springfield" }, | 
 |     { NULL, "St. Paul" }, | 
 |     { "T - Z", NULL }, | 
 |     { NULL, "Tallahassee" }, | 
 |     { NULL, "Topeka" }, | 
 |     { NULL, "Trenton" }, | 
 |     { NULL, NULL } | 
 |   }; | 
 |  | 
 |   GtkTreeIter iter, iter2; | 
 |   GtkTreeStore *store; | 
 |   gint i; | 
 |  | 
 |   store = gtk_tree_store_new (1, G_TYPE_STRING); | 
 |  | 
 |   for (i = 0; capitals[i].group || capitals[i].capital; i++) | 
 |     { | 
 |       if (capitals[i].group) | 
 |         { | 
 |           gtk_tree_store_append (store, &iter, NULL); | 
 |           gtk_tree_store_set (store, &iter, 0, capitals[i].group, -1); | 
 |         } | 
 |       else if (capitals[i].capital) | 
 |         { | 
 |           gtk_tree_store_append (store, &iter2, &iter); | 
 |           gtk_tree_store_set (store, &iter2, 0, capitals[i].capital, -1); | 
 |         } | 
 |     } | 
 |  | 
 |   return GTK_TREE_MODEL (store); | 
 | } | 
 |  | 
 | static void | 
 | is_capital_sensitive (GtkCellLayout   *cell_layout, | 
 |                       GtkCellRenderer *cell, | 
 |                       GtkTreeModel    *tree_model, | 
 |                       GtkTreeIter     *iter, | 
 |                       gpointer         data) | 
 | { | 
 |   gboolean sensitive; | 
 |  | 
 |   sensitive = !gtk_tree_model_iter_has_child (tree_model, iter); | 
 |  | 
 |   g_object_set (cell, "sensitive", sensitive, NULL); | 
 | } | 
 |  | 
 | static void | 
 | fill_combo_entry (GtkWidget *combo) | 
 | { | 
 |   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "One"); | 
 |   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Two"); | 
 |   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "2\302\275"); | 
 |   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Three"); | 
 | } | 
 |  | 
 |  | 
 | /* A simple validating entry */ | 
 |  | 
 | #define TYPE_MASK_ENTRY             (mask_entry_get_type ()) | 
 | #define MASK_ENTRY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MASK_ENTRY, MaskEntry)) | 
 | #define MASK_ENTRY_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), TYPE_MASK_ENTRY, MaskEntryClass)) | 
 | #define IS_MASK_ENTRY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MASK_ENTRY)) | 
 | #define IS_MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), TYPE_MASK_ENTRY)) | 
 | #define MASK_ENTRY_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), TYPE_MASK_ENTRY, MaskEntryClass)) | 
 |  | 
 |  | 
 | typedef struct _MaskEntry MaskEntry; | 
 | struct _MaskEntry | 
 | { | 
 |   GtkEntry entry; | 
 |   gchar *mask; | 
 | }; | 
 |  | 
 | typedef struct _MaskEntryClass MaskEntryClass; | 
 | struct _MaskEntryClass | 
 | { | 
 |   GtkEntryClass parent_class; | 
 | }; | 
 |  | 
 |  | 
 | static void mask_entry_editable_init (GtkEditableInterface *iface); | 
 |  | 
 | G_DEFINE_TYPE_WITH_CODE (MaskEntry, mask_entry, GTK_TYPE_ENTRY, | 
 |                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, | 
 |                                                 mask_entry_editable_init)); | 
 |  | 
 |  | 
 | static void | 
 | mask_entry_set_background (MaskEntry *entry) | 
 | { | 
 |   if (entry->mask) | 
 |     { | 
 |       if (!g_regex_match_simple (entry->mask, gtk_editable_get_text (GTK_EDITABLE (entry)), 0, 0)) | 
 |         { | 
 |           PangoAttrList *attrs; | 
 |  | 
 |           attrs = pango_attr_list_new (); | 
 |           pango_attr_list_insert (attrs, pango_attr_foreground_new (65535, 32767, 32767)); | 
 |           gtk_entry_set_attributes (GTK_ENTRY (entry), attrs); | 
 |           pango_attr_list_unref (attrs); | 
 |           return; | 
 |         } | 
 |     } | 
 |  | 
 |   gtk_entry_set_attributes (GTK_ENTRY (entry), NULL); | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | mask_entry_changed (GtkEditable *editable) | 
 | { | 
 |   mask_entry_set_background (MASK_ENTRY (editable)); | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | mask_entry_init (MaskEntry *entry) | 
 | { | 
 |   entry->mask = NULL; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | mask_entry_class_init (MaskEntryClass *klass) | 
 | { } | 
 |  | 
 |  | 
 | static void | 
 | mask_entry_editable_init (GtkEditableInterface *iface) | 
 | { | 
 |   iface->changed = mask_entry_changed; | 
 | } | 
 |  | 
 |  | 
 | GtkWidget * | 
 | do_combobox (GtkWidget *do_widget) | 
 | { | 
 |   static GtkWidget *window = NULL; | 
 |   GtkWidget *vbox, *frame, *box, *combo, *entry; | 
 |   GtkTreeModel *model; | 
 |   GtkCellRenderer *renderer; | 
 |   GtkTreePath *path; | 
 |   GtkTreeIter iter; | 
 |  | 
 |   if (!window) | 
 |   { | 
 |     window = gtk_window_new (GTK_WINDOW_TOPLEVEL); | 
 |     gtk_window_set_display (GTK_WINDOW (window), | 
 |                             gtk_widget_get_display (do_widget)); | 
 |     gtk_window_set_title (GTK_WINDOW (window), "Combo Boxes"); | 
 |  | 
 |     g_signal_connect (window, "destroy", | 
 |                       G_CALLBACK (gtk_widget_destroyed), &window); | 
 |  | 
 |     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); | 
 |     g_object_set (vbox, "margin", 10, NULL); | 
 |     gtk_container_add (GTK_CONTAINER (window), vbox); | 
 |  | 
 |     /* A combobox demonstrating cell renderers, separators and | 
 |      *  insensitive rows | 
 |      */ | 
 |     frame = gtk_frame_new ("Items with icons"); | 
 |     gtk_container_add (GTK_CONTAINER (vbox), frame); | 
 |  | 
 |     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | 
 |     g_object_set (box, "margin", 5, NULL); | 
 |     gtk_container_add (GTK_CONTAINER (frame), box); | 
 |  | 
 |     model = create_icon_store (); | 
 |     combo = gtk_combo_box_new_with_model (model); | 
 |     g_object_unref (model); | 
 |     gtk_container_add (GTK_CONTAINER (box), combo); | 
 |  | 
 |     renderer = gtk_cell_renderer_pixbuf_new (); | 
 |     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); | 
 |     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, | 
 |                                     "icon-name", ICON_NAME_COL, | 
 |                                     NULL); | 
 |  | 
 |     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), | 
 |                                         renderer, | 
 |                                         set_sensitive, | 
 |                                         NULL, NULL); | 
 |  | 
 |     renderer = gtk_cell_renderer_text_new (); | 
 |     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); | 
 |     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, | 
 |                                     "text", TEXT_COL, | 
 |                                     NULL); | 
 |  | 
 |     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), | 
 |                                         renderer, | 
 |                                         set_sensitive, | 
 |                                         NULL, NULL); | 
 |  | 
 |     gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo), | 
 |                                           is_separator, NULL, NULL); | 
 |  | 
 |     gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); | 
 |  | 
 |     /* A combobox demonstrating trees. | 
 |      */ | 
 |     frame = gtk_frame_new ("Where are we ?"); | 
 |     gtk_container_add (GTK_CONTAINER (vbox), frame); | 
 |  | 
 |     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | 
 |     g_object_set (box, "margin", 5, NULL); | 
 |     gtk_container_add (GTK_CONTAINER (frame), box); | 
 |  | 
 |     model = create_capital_store (); | 
 |     combo = gtk_combo_box_new_with_model (model); | 
 |     g_object_unref (model); | 
 |     gtk_container_add (GTK_CONTAINER (box), combo); | 
 |  | 
 |     renderer = gtk_cell_renderer_text_new (); | 
 |     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); | 
 |     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, | 
 |                                     "text", 0, | 
 |                                     NULL); | 
 |     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), | 
 |                                         renderer, | 
 |                                         is_capital_sensitive, | 
 |                                         NULL, NULL); | 
 |  | 
 |     path = gtk_tree_path_new_from_indices (0, 8, -1); | 
 |     gtk_tree_model_get_iter (model, &iter, path); | 
 |     gtk_tree_path_free (path); | 
 |     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter); | 
 |  | 
 |     /* A GtkComboBoxEntry with validation */ | 
 |     frame = gtk_frame_new ("Editable"); | 
 |     gtk_container_add (GTK_CONTAINER (vbox), frame); | 
 |  | 
 |     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | 
 |     g_object_set (box, "margin", 5, NULL); | 
 |     gtk_container_add (GTK_CONTAINER (frame), box); | 
 |  | 
 |     combo = gtk_combo_box_text_new_with_entry (); | 
 |     fill_combo_entry (combo); | 
 |     gtk_container_add (GTK_CONTAINER (box), combo); | 
 |  | 
 |     entry = g_object_new (TYPE_MASK_ENTRY, NULL); | 
 |     MASK_ENTRY (entry)->mask = "^([0-9]*|One|Two|2\302\275|Three)$"; | 
 |  | 
 |     gtk_container_remove (GTK_CONTAINER (combo), gtk_bin_get_child (GTK_BIN (combo))); | 
 |     gtk_container_add (GTK_CONTAINER (combo), entry); | 
 |  | 
 |     /* A combobox with string IDs */ | 
 |     frame = gtk_frame_new ("String IDs"); | 
 |     gtk_container_add (GTK_CONTAINER (vbox), frame); | 
 |  | 
 |     box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | 
 |     g_object_set (box, "margin", 5, NULL); | 
 |     gtk_container_add (GTK_CONTAINER (frame), box); | 
 |  | 
 |     combo = gtk_combo_box_text_new (); | 
 |     gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "never", "Not visible"); | 
 |     gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "when-active", "Visible when active"); | 
 |     gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "always", "Always visible"); | 
 |     gtk_container_add (GTK_CONTAINER (box), combo); | 
 |  | 
 |     entry = gtk_entry_new (); | 
 |     g_object_bind_property (combo, "active-id", | 
 |                             entry, "text", | 
 |                             G_BINDING_BIDIRECTIONAL); | 
 |     gtk_container_add (GTK_CONTAINER (box), entry); | 
 |   } | 
 |  | 
 |   if (!gtk_widget_get_visible (window)) | 
 |     gtk_widget_show (window); | 
 |   else | 
 |     gtk_widget_destroy (window); | 
 |  | 
 |   return window; | 
 | } |