/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * arch-tag: Implementation of RhythmDB query creation dialog * * Copyright (C) 2003, 2004 Colin Walters * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "config.h" #include #include #include #include #include "rhythmdb.h" #include "rb-query-creator.h" #include "rb-query-creator-private.h" #include "rb-dialog.h" #include "rb-debug.h" #include "rb-preferences.h" #include "rb-glade-helpers.h" #include "rb-util.h" static void rb_query_creator_class_init (RBQueryCreatorClass *klass); static GObject *rb_query_creator_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void rb_query_creator_dispose (GObject *object); static void rb_query_creator_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void rb_query_creator_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void select_criteria_from_value (RBQueryCreator *creator, GtkWidget *option_menu, RhythmDBPropType prop, RhythmDBQueryType qtype); static GtkWidget * create_property_option_menu (RBQueryCreator *creator, const RBQueryCreatorPropertyOption *options, int length); static GtkWidget * create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options, int length); static void setup_sort_option_menu (RBQueryCreator *creator, GtkWidget *option_menu, const RBQueryCreatorSortOption *options, int length); static GtkWidget * append_row (RBQueryCreator *creator); static void add_button_click_cb (GtkWidget *button, RBQueryCreator *creator); static void remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator); static void limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator); static int get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options, int length, RhythmDBPropType prop); static void sort_option_menu_changed (GtkOptionMenu *propmenu, RBQueryCreator *creator); typedef struct { RhythmDB *db; gboolean creating; GtkSizeGroup *property_size_group; GtkSizeGroup *criteria_size_group; GtkSizeGroup *entry_size_group; GtkSizeGroup *button_size_group; GtkBox *vbox; GList *rows; GtkWidget *addbutton; GtkWidget *disjunction_check; GtkWidget *limit_check; GtkWidget *limit_entry; GtkWidget *limit_option; GtkWidget *sort_label; GtkWidget *sort_menu; GtkWidget *sort_desc; } RBQueryCreatorPrivate; G_DEFINE_TYPE (RBQueryCreator, rb_query_creator, GTK_TYPE_DIALOG) #define QUERY_CREATOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), rb_query_creator_get_type(), RBQueryCreatorPrivate)) enum { PROP_0, PROP_DB, PROP_CREATING, }; static void rb_query_creator_class_init (RBQueryCreatorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = rb_query_creator_dispose; object_class->constructor = rb_query_creator_constructor; object_class->set_property = rb_query_creator_set_property; object_class->get_property = rb_query_creator_get_property; g_object_class_install_property (object_class, PROP_DB, g_param_spec_object ("db", "RhythmDB", "RhythmDB database", RHYTHMDB_TYPE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CREATING, g_param_spec_boolean ("creating", "creating", "Whether or not we're creating a new playlist", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (RBQueryCreatorPrivate)); } static void rb_query_creator_init (RBQueryCreator *creator) { } static GObject * rb_query_creator_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { RBQueryCreatorPrivate *priv; RBQueryCreator *creator; GladeXML *xml; GtkWidget *mainbox; creator = RB_QUERY_CREATOR (G_OBJECT_CLASS (rb_query_creator_parent_class) ->constructor (type, n_construct_properties, construct_properties)); priv = QUERY_CREATOR_GET_PRIVATE (creator); if (priv->creating) { gtk_dialog_add_button (GTK_DIALOG (creator), GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE); gtk_dialog_add_button (GTK_DIALOG (creator), GTK_STOCK_NEW, GTK_RESPONSE_OK); } else { gtk_dialog_add_button (GTK_DIALOG (creator), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); } gtk_dialog_set_default_response (GTK_DIALOG (creator), GTK_RESPONSE_CLOSE); priv->property_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); priv->criteria_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); priv->entry_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); if (priv->creating) gtk_window_set_title (GTK_WINDOW (creator), _("Create Automatic Playlist")); else gtk_window_set_title (GTK_WINDOW (creator), _("Edit Automatic Playlist")); gtk_container_set_border_width (GTK_CONTAINER (creator), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (creator)->vbox), 2); gtk_dialog_set_has_separator (GTK_DIALOG (creator), FALSE); xml = rb_glade_xml_new ("create-playlist.glade", "main_vbox", creator); priv->disjunction_check = GTK_WIDGET (glade_xml_get_widget (xml, "disjunctionCheck")); priv->limit_check = GTK_WIDGET (glade_xml_get_widget (xml, "limitCheck")); priv->limit_entry = GTK_WIDGET (glade_xml_get_widget (xml, "limitEntry")); priv->limit_option = GTK_WIDGET (glade_xml_get_widget (xml, "limitOption")); priv->addbutton = GTK_WIDGET (glade_xml_get_widget (xml, "addButton")); priv->sort_label = GTK_WIDGET (glade_xml_get_widget (xml, "sortLabel")); priv->sort_menu = GTK_WIDGET (glade_xml_get_widget (xml, "sortMenu")); priv->sort_desc = GTK_WIDGET (glade_xml_get_widget (xml, "sortDesc")); g_signal_connect_object (G_OBJECT (priv->limit_check), "toggled", G_CALLBACK (limit_toggled_cb), creator, 0); limit_toggled_cb (priv->limit_check, creator); gtk_size_group_add_widget (priv->button_size_group, priv->addbutton); g_signal_connect_object (G_OBJECT (priv->addbutton), "clicked", G_CALLBACK (add_button_click_cb), creator, 0); setup_sort_option_menu (creator, priv->sort_menu, sort_options, num_sort_options); priv->vbox = GTK_BOX (glade_xml_get_widget (xml, "sub_vbox")); if (priv->creating) append_row (creator); mainbox = glade_xml_get_widget (xml, "main_vbox"); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (creator)->vbox), mainbox, FALSE, FALSE, 0); gtk_widget_show_all (GTK_WIDGET (creator)); g_object_unref (xml); return G_OBJECT (creator); } static void rb_query_creator_dispose (GObject *object) { RBQueryCreatorPrivate *priv; g_return_if_fail (RB_IS_QUERY_CREATOR (object)); priv = QUERY_CREATOR_GET_PRIVATE (object); g_return_if_fail (priv != NULL); if (priv->property_size_group != NULL) g_object_unref (G_OBJECT (priv->property_size_group)); priv->property_size_group = NULL; if (priv->criteria_size_group != NULL) g_object_unref (G_OBJECT (priv->criteria_size_group)); priv->criteria_size_group = NULL; if (priv->entry_size_group != NULL) g_object_unref (G_OBJECT (priv->entry_size_group)); priv->entry_size_group = NULL; if (priv->button_size_group != NULL) g_object_unref (G_OBJECT (priv->button_size_group)); priv->button_size_group = NULL; if (priv->rows) g_list_free (priv->rows); priv->rows = NULL; G_OBJECT_CLASS (rb_query_creator_parent_class)->dispose (object); } static void rb_query_creator_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { RBQueryCreator *creator = RB_QUERY_CREATOR (object); RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); switch (prop_id) { case PROP_DB: priv->db = g_value_get_object (value); break; case PROP_CREATING: priv->creating = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void rb_query_creator_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { RBQueryCreator *creator = RB_QUERY_CREATOR (object); RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); switch (prop_id) { case PROP_DB: g_value_set_object (value, priv->db); break; case PROP_CREATING: g_value_set_boolean (value, priv->creating); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } GtkWidget * rb_query_creator_new (RhythmDB *db) { return g_object_new (RB_TYPE_QUERY_CREATOR, "db", db, NULL); } static gboolean rb_query_creator_load_query (RBQueryCreator *creator, GPtrArray *query, RhythmDBQueryModelLimitType limit_type, GValueArray *limit_value) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); int i; GList *rows; gboolean disjunction = FALSE; RhythmDBQueryData *qdata; GPtrArray *subquery; g_return_val_if_fail (query->len == 2, FALSE); qdata = g_ptr_array_index (query, 1); g_return_val_if_fail (qdata->type == RHYTHMDB_QUERY_SUBQUERY, FALSE); subquery = qdata->subquery; if (subquery->len > 0) for (i = 0; i < subquery->len; i++) { RhythmDBQueryData *data = g_ptr_array_index (subquery, i); if (data->type != RHYTHMDB_QUERY_DISJUNCTION) append_row (creator); } rows = priv->rows; for (i = 0; i < subquery->len; i++) { RhythmDBQueryData *data = g_ptr_array_index (subquery, i); GtkOptionMenu *propmenu; GtkWidget *criteria_menu; int index; const RBQueryCreatorPropertyType *property_type; if (data->type == RHYTHMDB_QUERY_DISJUNCTION) { disjunction = TRUE; continue; } propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data), 0)); index = get_property_index_from_proptype (property_options, num_property_options, data->propid); gtk_option_menu_set_history (propmenu, index); criteria_menu = get_box_widget_at_pos (GTK_BOX (rows->data), 1); select_criteria_from_value (creator, criteria_menu, data->propid, data->type); property_type = property_options[index].property_type; g_assert (property_type->criteria_set_widget_data != NULL); property_type->criteria_set_widget_data (get_box_widget_at_pos (GTK_BOX (rows->data), 2), data->val); rows = rows->next; } /* setup the limits */ { guint64 limit; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->disjunction_check), disjunction); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->limit_check), limit_type != RHYTHMDB_QUERY_MODEL_LIMIT_NONE); switch (limit_type) { case RHYTHMDB_QUERY_MODEL_LIMIT_NONE: limit = 0; break; case RHYTHMDB_QUERY_MODEL_LIMIT_COUNT: gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 0); limit = g_value_get_ulong (g_value_array_get_nth (limit_value, 0)); break; case RHYTHMDB_QUERY_MODEL_LIMIT_TIME: gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 3); /* convert to minutes */ limit = g_value_get_ulong (g_value_array_get_nth (limit_value, 0)) / 60; break; case RHYTHMDB_QUERY_MODEL_LIMIT_SIZE: limit = g_value_get_uint64 (g_value_array_get_nth (limit_value, 0)); if (limit % 1000 == 0) { gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 2); limit /= 1000; } else { gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 1); } break; default: g_assert_not_reached (); } gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->limit_entry), limit); } return TRUE; } static gboolean rb_query_creator_set_sorting (RBQueryCreator *creator, const char *sort_column, gint sort_direction) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); int i; if (!sort_column || ! *sort_column) { g_warning("No playlist sorting order"); sort_column = sort_options[DEFAULT_SORTING_COLUMN].sort_key; sort_direction = DEFAULT_SORTING_ORDER; } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sort_desc), (sort_direction == GTK_SORT_DESCENDING)); for (i = 0; i < num_sort_options; i++) if (strcmp (sort_options[i].sort_key, sort_column) == 0) break; /* check that it is a valid sort option */ g_return_val_if_fail (i < num_property_options, FALSE); gtk_option_menu_set_history (GTK_OPTION_MENU (priv->sort_menu), i); sort_option_menu_changed (GTK_OPTION_MENU (priv->sort_menu), creator); /* force the checkbox to change label */ return TRUE; } GtkWidget * rb_query_creator_new_from_query (RhythmDB *db, GPtrArray *query, RhythmDBQueryModelLimitType limit_type, GValueArray *limit_value, const char *sort_column, gint sort_direction) { RBQueryCreator *creator = g_object_new (RB_TYPE_QUERY_CREATOR, "db", db, "creating", FALSE, NULL); if (!creator) return NULL; if ( !rb_query_creator_load_query (creator, query, limit_type, limit_value) | !rb_query_creator_set_sorting (creator, sort_column, sort_direction)) { gtk_widget_destroy (GTK_WIDGET (creator)); return NULL; } return GTK_WIDGET (creator); } GtkWidget * get_box_widget_at_pos (GtkBox *box, guint pos) { GtkWidget *ret = NULL; GList *children = gtk_container_get_children (GTK_CONTAINER (box)); GList *tem; for (tem = children; tem; tem = tem->next) { GValue thispos = { 0, }; g_value_init (&thispos, G_TYPE_INT); gtk_container_child_get_property (GTK_CONTAINER (box), GTK_WIDGET (tem->data), "position", &thispos); if (g_value_get_int (&thispos) == pos) { ret = tem->data; break; } } g_list_free (children); return GTK_WIDGET (ret); } static GtkWidget * get_entry_for_property (RBQueryCreator *creator, RhythmDBPropType prop, gboolean *constrain) { const RBQueryCreatorPropertyType *property_type; int index = get_property_index_from_proptype (property_options, num_property_options, prop); property_type = property_options[index].property_type; g_assert (property_type->criteria_create_widget != NULL); *constrain = TRUE; return property_type->criteria_create_widget (constrain); } GPtrArray * rb_query_creator_get_query (RBQueryCreator *creator) { RBQueryCreatorPrivate *priv; GPtrArray *query; GPtrArray *sub_query; GList *rows, *row; gboolean disjunction; g_return_val_if_fail (RB_IS_QUERY_CREATOR (creator), NULL); priv = QUERY_CREATOR_GET_PRIVATE (creator); disjunction = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->disjunction_check)); sub_query = g_ptr_array_new (); rows = priv->rows; for (row = rows; row; row = row->next) { GtkOptionMenu *propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data), 0)); GtkOptionMenu *criteria_menu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data), 1)); guint prop_position = gtk_option_menu_get_history (propmenu); const RBQueryCreatorPropertyOption *prop_option = &property_options[prop_position]; const RBQueryCreatorCriteriaOption *criteria_options = prop_option->property_type->criteria_options; const RBQueryCreatorCriteriaOption *criteria_option = &criteria_options[gtk_option_menu_get_history (criteria_menu)]; g_assert (prop_option->property_type->criteria_get_widget_data != NULL); { RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1); GValue *val = g_new0 (GValue, 1); data->type = criteria_option->val; data->propid = criteria_option->strict ? prop_option->strict_val : prop_option->fuzzy_val; prop_option->property_type->criteria_get_widget_data (get_box_widget_at_pos (GTK_BOX (row->data), 2), val); data->val = val; g_ptr_array_add (sub_query, data); } if (disjunction && row->next) rhythmdb_query_append (priv->db, sub_query, RHYTHMDB_QUERY_DISJUNCTION, RHYTHMDB_QUERY_END); } query = rhythmdb_query_parse (priv->db, /* type=songs */ RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_SONG, /* the constructed query */ RHYTHMDB_QUERY_SUBQUERY, sub_query, RHYTHMDB_QUERY_END); return query; } void rb_query_creator_get_limit (RBQueryCreator *creator, RhythmDBQueryModelLimitType *type, GValueArray **limit) { RBQueryCreatorPrivate *priv; g_return_if_fail (RB_IS_QUERY_CREATOR (creator)); priv = QUERY_CREATOR_GET_PRIVATE (creator); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->limit_check))) { guint64 l; l = gtk_spin_button_get_value(GTK_SPIN_BUTTON (priv->limit_entry)); *limit = g_value_array_new (0); switch (gtk_option_menu_get_history (GTK_OPTION_MENU (priv->limit_option))) { case 0: *type = RHYTHMDB_QUERY_MODEL_LIMIT_COUNT; rb_value_array_append_data (*limit, G_TYPE_ULONG, (gulong)l); break; case 1: *type = RHYTHMDB_QUERY_MODEL_LIMIT_SIZE; rb_value_array_append_data (*limit, G_TYPE_UINT64, l); break; case 2: *type = RHYTHMDB_QUERY_MODEL_LIMIT_SIZE; rb_value_array_append_data (*limit, G_TYPE_ULONG, (gulong)l); break; case 3: *type = RHYTHMDB_QUERY_MODEL_LIMIT_TIME; rb_value_array_append_data (*limit, G_TYPE_ULONG, (gulong)l * 60); break; default: g_assert_not_reached (); } } else { *type = RHYTHMDB_QUERY_MODEL_LIMIT_NONE; *limit = NULL; } } void rb_query_creator_get_sort_order (RBQueryCreator *creator, const char **sort_column, gint *sort_direction) { RBQueryCreatorPrivate *priv; g_return_if_fail (RB_IS_QUERY_CREATOR (creator)); priv = QUERY_CREATOR_GET_PRIVATE (creator); if (sort_direction != NULL) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->sort_desc))) *sort_direction = GTK_SORT_DESCENDING; else *sort_direction = GTK_SORT_ASCENDING; } if (sort_column != NULL) { int i; i = gtk_option_menu_get_history (GTK_OPTION_MENU (priv->sort_menu)); *sort_column = sort_options[i].sort_key; } } static void limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (limit)); gtk_widget_set_sensitive (priv->limit_entry, active); gtk_widget_set_sensitive (priv->limit_option, active); gtk_widget_set_sensitive (priv->sort_menu, active); gtk_widget_set_sensitive (priv->sort_label, active); gtk_widget_set_sensitive (priv->sort_desc, active); } static GtkWidget * lookup_row_by_widget (RBQueryCreator *creator, GtkWidget *widget) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); GList *rows = priv->rows; GList *row; GtkWidget *ret = NULL; guint i; for (row = rows, i = 0; row; row = row->next, i++) { GList *columns = gtk_container_get_children (GTK_CONTAINER (row->data)); gboolean found = g_list_find (columns, widget) != NULL; g_list_free (columns); if (found) { ret = row->data; break; } } return ret; } static void remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); GtkWidget *row; row = lookup_row_by_widget (creator, button); g_assert (row); gtk_container_remove (GTK_CONTAINER (priv->vbox), GTK_WIDGET (row)); priv->rows = g_list_remove (priv->rows, row); } static void add_button_click_cb (GtkWidget *button, RBQueryCreator *creator) { append_row (creator); } static GtkWidget * append_row (RBQueryCreator *creator) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); GtkWidget *option; GtkWidget *criteria; GtkWidget *entry; GtkWidget *remove_button; GtkBox *hbox; GList *rows; guint len; gboolean constrain; rows = priv->rows; len = g_list_length (rows); hbox = GTK_BOX (gtk_hbox_new (FALSE, 5)); gtk_box_pack_start_defaults (GTK_BOX (priv->vbox), GTK_WIDGET (hbox)); priv->rows = g_list_prepend (priv->rows, hbox); gtk_box_reorder_child (priv->vbox, GTK_WIDGET (hbox), -1); /* This is the main (leftmost) GtkOptionMenu, for types. */ option = create_property_option_menu (creator, property_options, num_property_options); gtk_size_group_add_widget (priv->property_size_group, option); gtk_box_pack_start_defaults (hbox, GTK_WIDGET (option)); gtk_option_menu_set_history (GTK_OPTION_MENU (option), 0); criteria = create_criteria_option_menu (property_options[0].property_type->criteria_options, property_options[0].property_type->num_criteria_options); gtk_size_group_add_widget (priv->criteria_size_group, criteria); gtk_box_pack_start_defaults (hbox, GTK_WIDGET (criteria)); entry = get_entry_for_property (creator, property_options[0].strict_val, &constrain); if (constrain) gtk_size_group_add_widget (priv->entry_size_group, entry); gtk_box_pack_start_defaults (hbox, GTK_WIDGET (entry)); remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); g_signal_connect_object (G_OBJECT (remove_button), "clicked", G_CALLBACK (remove_button_click_cb), creator, 0); gtk_size_group_add_widget (priv->button_size_group, remove_button); gtk_box_pack_start_defaults (hbox, GTK_WIDGET (remove_button)); gtk_widget_show_all (GTK_WIDGET (priv->vbox)); return GTK_WIDGET (hbox); } static int get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options, int length, RhythmDBPropType prop) { int i; for (i = 0; i < length; i++) if (prop == options[i].strict_val || prop == options[i].fuzzy_val) return i; g_assert_not_reached (); } static void select_criteria_from_value (RBQueryCreator *creator, GtkWidget *option_menu, RhythmDBPropType prop, RhythmDBQueryType qtype) { int i; const RBQueryCreatorCriteriaOption *options; guint length; i = get_property_index_from_proptype (property_options, num_property_options, prop); length = property_options[i].property_type->num_criteria_options; options = property_options[i].property_type->criteria_options; for (i = 0; i < length; i++) { if (qtype == options[i].val) { gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), i); return; } } g_assert_not_reached (); } static void property_option_menu_changed (GtkOptionMenu *propmenu, RBQueryCreator *creator) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); GtkWidget *row; GtkWidget *criteria; GtkWidget *entry; const RBQueryCreatorPropertyOption *prop_option; const RBQueryCreatorCriteriaOption *criteria_options; guint length; guint old_value; gboolean constrain; prop_option = &property_options[gtk_option_menu_get_history (propmenu)]; old_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (propmenu), "prop-menu old-value")); /* don't recreate the criteria menu and entry if they will be the same*/ if (prop_option->property_type == property_options[old_value].property_type) return; g_object_set_data (G_OBJECT (propmenu), "prop-menu old-value", GINT_TO_POINTER (gtk_option_menu_get_history (propmenu))); row = lookup_row_by_widget (creator, GTK_WIDGET (propmenu)); criteria = get_box_widget_at_pos (GTK_BOX (row), 1); gtk_container_remove (GTK_CONTAINER (row), criteria); criteria_options = prop_option->property_type->criteria_options; length = prop_option->property_type->num_criteria_options; criteria = create_criteria_option_menu (criteria_options, length); gtk_widget_show (criteria); gtk_size_group_add_widget (priv->criteria_size_group, criteria); gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (criteria)); gtk_box_reorder_child (GTK_BOX (row), criteria, 1); entry = get_box_widget_at_pos (GTK_BOX (row), 2); gtk_container_remove (GTK_CONTAINER (row), entry); entry = get_entry_for_property (creator, prop_option->strict_val, &constrain); gtk_widget_show (entry); if (constrain) gtk_size_group_add_widget (priv->entry_size_group, entry); gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (entry)); gtk_box_reorder_child (GTK_BOX (row), entry, 2); } static GtkWidget* create_property_option_menu (RBQueryCreator *creator, const RBQueryCreatorPropertyOption *options, int length) { GtkWidget *option_menu; GtkWidget *menu; GtkWidget *menu_item; int i; option_menu = gtk_option_menu_new (); menu = gtk_menu_new (); /* add the property options */ for (i = 0; i < length; i++) { menu_item = gtk_menu_item_new_with_label (_(options[i].name)); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); gtk_widget_show (menu_item); } gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0); g_object_set_data (G_OBJECT (option_menu), "prop-menu old-value", GINT_TO_POINTER (0)); gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu); gtk_widget_show (menu); g_signal_connect_object (G_OBJECT (option_menu), "changed", G_CALLBACK (property_option_menu_changed), creator, 0); return option_menu; } static GtkWidget* create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options, int length) { GtkWidget *option_menu; GtkWidget *menu; GtkWidget *menu_item; int i; option_menu = gtk_option_menu_new (); menu = gtk_menu_new (); for (i = 0; i < length; i++) { menu_item = gtk_menu_item_new_with_label (_(options[i].name)); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); gtk_widget_show (menu_item); } gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu); gtk_widget_show (menu); return option_menu; } static void sort_option_menu_changed (GtkOptionMenu *propmenu, RBQueryCreator *creator) { RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator); int index = gtk_option_menu_get_history (propmenu); gtk_button_set_label (GTK_BUTTON (priv->sort_desc), _(sort_options[index].sort_descending_name)); rb_debug("changing descending label to %s[%d]", sort_options[index].sort_descending_name, index); } static void setup_sort_option_menu (RBQueryCreator *creator, GtkWidget *option_menu, const RBQueryCreatorSortOption *options, int length) { GtkWidget *menu; GtkWidget *menu_item; int i; menu = gtk_menu_new (); gtk_widget_show (menu); for (i = 0; i < length; i++) { menu_item = gtk_menu_item_new_with_label (_(options[i].name)); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); gtk_widget_show (menu_item); } g_signal_connect_object (G_OBJECT (option_menu), "changed", G_CALLBACK (sort_option_menu_changed), creator, 0); gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu); }