mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-06 08:40:08 +00:00
474 lines
12 KiB
C
474 lines
12 KiB
C
/* Combo boxes
|
|
*
|
|
* The ComboBox widget allows to select one option out of a list.
|
|
* The ComboBoxEntry 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 <gtk/gtk.h>
|
|
|
|
enum
|
|
{
|
|
PIXBUF_COL,
|
|
TEXT_COL
|
|
};
|
|
|
|
static gchar *
|
|
strip_underscore (const gchar *text)
|
|
{
|
|
gchar *p, *q;
|
|
gchar *result;
|
|
|
|
result = g_strdup (text);
|
|
p = q = result;
|
|
while (*p)
|
|
{
|
|
if (*p != '_')
|
|
{
|
|
*q = *p;
|
|
q++;
|
|
}
|
|
p++;
|
|
}
|
|
*q = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
static GtkTreeModel *
|
|
create_stock_icon_store (void)
|
|
{
|
|
gchar *stock_id[6] = {
|
|
GTK_STOCK_DIALOG_WARNING,
|
|
GTK_STOCK_STOP,
|
|
GTK_STOCK_NEW,
|
|
GTK_STOCK_CLEAR,
|
|
NULL,
|
|
GTK_STOCK_OPEN
|
|
};
|
|
|
|
GtkStockItem item;
|
|
GdkPixbuf *pixbuf;
|
|
GtkWidget *cellview;
|
|
GtkTreeIter iter;
|
|
GtkListStore *store;
|
|
gchar *label;
|
|
gint i;
|
|
|
|
cellview = gtk_cell_view_new ();
|
|
|
|
store = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (stock_id); i++)
|
|
{
|
|
if (stock_id[i])
|
|
{
|
|
pixbuf = gtk_widget_render_icon_pixbuf (cellview, stock_id[i],
|
|
GTK_ICON_SIZE_BUTTON);
|
|
gtk_stock_lookup (stock_id[i], &item);
|
|
label = strip_underscore (item.label);
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (store, &iter,
|
|
PIXBUF_COL, pixbuf,
|
|
TEXT_COL, label,
|
|
-1);
|
|
g_object_unref (pixbuf);
|
|
g_free (label);
|
|
}
|
|
else
|
|
{
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (store, &iter,
|
|
PIXBUF_COL, NULL,
|
|
TEXT_COL, "separator",
|
|
-1);
|
|
}
|
|
}
|
|
|
|
gtk_widget_destroy (cellview);
|
|
|
|
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, "Lansing" },
|
|
{ NULL, "Lincoln" },
|
|
{ NULL, "Little Rock" },
|
|
{ NULL, "Madison" },
|
|
{ NULL, "Montgomery" },
|
|
{ NULL, "Montpelier" },
|
|
{ NULL, "Nashville" },
|
|
{ NULL, "Oklahoma City" },
|
|
{ NULL, "Olympia" },
|
|
{ NULL, "P - S" },
|
|
{ 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)
|
|
{
|
|
static const GdkRGBA error_color = { 1.0, 0.9, 0.9, 1.0 };
|
|
|
|
if (entry->mask)
|
|
{
|
|
if (!g_regex_match_simple (entry->mask, gtk_entry_get_text (GTK_ENTRY (entry)), 0, 0))
|
|
{
|
|
gtk_widget_override_color (GTK_WIDGET (entry), 0, &error_color);
|
|
return;
|
|
}
|
|
}
|
|
|
|
gtk_widget_override_color (GTK_WIDGET (entry), 0, 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_screen (GTK_WINDOW (window),
|
|
gtk_widget_get_screen (do_widget));
|
|
gtk_window_set_title (GTK_WINDOW (window), "Combo boxes");
|
|
|
|
g_signal_connect (window, "destroy",
|
|
G_CALLBACK (gtk_widget_destroyed),
|
|
&window);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
|
gtk_container_add (GTK_CONTAINER (window), vbox);
|
|
|
|
/* A combobox demonstrating cell renderers, separators and
|
|
* insensitive rows
|
|
*/
|
|
frame = gtk_frame_new ("Some stock icons");
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
|
|
gtk_container_add (GTK_CONTAINER (frame), box);
|
|
|
|
model = create_stock_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,
|
|
"pixbuf", PIXBUF_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_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
|
|
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_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
|
|
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_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
|
|
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_all (window);
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_destroy (window);
|
|
window = NULL;
|
|
}
|
|
|
|
return window;
|
|
}
|