Updated Macedonian Translation <arangela@cvs.gnome.org>
[rhythmbox.git] / widgets / rb-query-creator.c
blob4ac44bc0afef666ef175cb9dc68c585d2af798a2
1 /*
2 * arch-tag: Implementation of RhythmDB query creation dialog
4 * Copyright (C) 2003, 2004 Colin Walters <walters@gnome.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <config.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <libgnome/gnome-i18n.h>
26 #include <gtk/gtk.h>
28 #include "rhythmdb.h"
29 #include "rb-query-creator.h"
30 #include "rb-query-creator-private.h"
31 #include "rb-dialog.h"
32 #include "rb-debug.h"
33 #include "rb-preferences.h"
34 #include "rb-glade-helpers.h"
37 static void rb_query_creator_class_init (RBQueryCreatorClass *klass);
38 static GObject *rb_query_creator_constructor (GType type, guint n_construct_properties,
39 GObjectConstructParam *construct_properties);
40 static void rb_query_creator_dispose (GObject *object);
41 static void rb_query_creator_set_property (GObject *object,
42 guint prop_id,
43 const GValue *value,
44 GParamSpec *pspec);
45 static void rb_query_creator_get_property (GObject *object,
46 guint prop_id,
47 GValue *value,
48 GParamSpec *pspec);
49 static void select_criteria_from_value (RBQueryCreator *creator,
50 GtkWidget *option_menu,
51 RhythmDBPropType prop,
52 RhythmDBQueryType qtype);
53 static GtkWidget * create_property_option_menu (RBQueryCreator *creator,
54 const RBQueryCreatorPropertyOption *options,
55 int length);
56 static GtkWidget * create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
57 int length);
58 static void setup_sort_option_menu (RBQueryCreator *creator,
59 GtkWidget *option_menu,
60 const RBQueryCreatorSortOption *options,
61 int length);
63 static GtkWidget * append_row (RBQueryCreator *creator);
64 static void add_button_click_cb (GtkWidget *button, RBQueryCreator *creator);
65 static void remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator);
66 static void limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator);
68 static int get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
69 int length, RhythmDBPropType prop);
70 static void sort_option_menu_changed (GtkOptionMenu *propmenu, RBQueryCreator *creator);
72 typedef struct
74 RhythmDB *db;
76 gboolean creating;
78 GtkSizeGroup *property_size_group;
79 GtkSizeGroup *criteria_size_group;
80 GtkSizeGroup *entry_size_group;
81 GtkSizeGroup *button_size_group;
83 GtkBox *vbox;
84 GList *rows;
86 GtkWidget *addbutton;
87 GtkWidget *disjunction_check;
88 GtkWidget *limit_check;
89 GtkWidget *limit_entry;
90 GtkWidget *limit_option;
91 GtkWidget *sort_label;
92 GtkWidget *sort_menu;
93 GtkWidget *sort_desc;
94 } RBQueryCreatorPrivate;
97 G_DEFINE_TYPE (RBQueryCreator, rb_query_creator, GTK_TYPE_DIALOG)
98 #define QUERY_CREATOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), rb_query_creator_get_type(), RBQueryCreatorPrivate))
100 enum
102 PROP_0,
103 PROP_DB,
104 PROP_CREATING,
108 static void
109 rb_query_creator_class_init (RBQueryCreatorClass *klass)
111 GObjectClass *object_class = G_OBJECT_CLASS (klass);
113 object_class->dispose = rb_query_creator_dispose;
114 object_class->constructor = rb_query_creator_constructor;
115 object_class->set_property = rb_query_creator_set_property;
116 object_class->get_property = rb_query_creator_get_property;
118 g_object_class_install_property (object_class,
119 PROP_DB,
120 g_param_spec_object ("db",
121 "RhythmDB",
122 "RhythmDB database",
123 RHYTHMDB_TYPE,
124 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
126 g_object_class_install_property (object_class,
127 PROP_CREATING,
128 g_param_spec_boolean ("creating",
129 "creating",
130 "Whether or not we're creating a new playlist",
131 TRUE,
132 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
134 g_type_class_add_private (klass, sizeof (RBQueryCreatorPrivate));
137 static void
138 rb_query_creator_init (RBQueryCreator *creator)
143 static GObject *
144 rb_query_creator_constructor (GType type, guint n_construct_properties,
145 GObjectConstructParam *construct_properties)
147 RBQueryCreatorPrivate *priv;
148 RBQueryCreator *creator;
149 RBQueryCreatorClass *klass;
150 GladeXML *xml;
151 GtkWidget *mainbox;
153 klass = RB_QUERY_CREATOR_CLASS (g_type_class_peek (type));
154 creator = RB_QUERY_CREATOR (G_OBJECT_CLASS (rb_query_creator_parent_class)
155 ->constructor (type, n_construct_properties, construct_properties));
156 priv = QUERY_CREATOR_GET_PRIVATE (creator);
158 if (priv->creating) {
159 gtk_dialog_add_button (GTK_DIALOG (creator),
160 GTK_STOCK_CANCEL,
161 GTK_RESPONSE_CLOSE);
162 gtk_dialog_add_button (GTK_DIALOG (creator),
163 GTK_STOCK_NEW,
164 GTK_RESPONSE_OK);
165 } else {
166 gtk_dialog_add_button (GTK_DIALOG (creator),
167 GTK_STOCK_CLOSE,
168 GTK_RESPONSE_CLOSE);
170 gtk_dialog_set_default_response (GTK_DIALOG (creator),
171 GTK_RESPONSE_CLOSE);
173 priv->property_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
174 priv->criteria_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
175 priv->entry_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
176 priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
178 if (priv->creating)
179 gtk_window_set_title (GTK_WINDOW (creator), _("Create Automatic Playlist"));
180 else
181 gtk_window_set_title (GTK_WINDOW (creator), _("Edit Automatic Playlist"));
183 gtk_container_set_border_width (GTK_CONTAINER (creator), 5);
184 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (creator)->vbox), 2);
185 gtk_dialog_set_has_separator (GTK_DIALOG (creator), FALSE);
187 xml = rb_glade_xml_new ("create-playlist.glade",
188 "main_vbox",
189 creator);
191 priv->disjunction_check = GTK_WIDGET (glade_xml_get_widget (xml, "disjunctionCheck"));
192 priv->limit_check = GTK_WIDGET (glade_xml_get_widget (xml, "limitCheck"));
193 priv->limit_entry = GTK_WIDGET (glade_xml_get_widget (xml, "limitEntry"));
194 priv->limit_option = GTK_WIDGET (glade_xml_get_widget (xml, "limitOption"));
195 priv->addbutton = GTK_WIDGET (glade_xml_get_widget (xml, "addButton"));
196 priv->sort_label = GTK_WIDGET (glade_xml_get_widget (xml, "sortLabel"));
197 priv->sort_menu = GTK_WIDGET (glade_xml_get_widget (xml, "sortMenu"));
198 priv->sort_desc = GTK_WIDGET (glade_xml_get_widget (xml, "sortDesc"));
200 g_signal_connect_object (G_OBJECT (priv->limit_check), "toggled", G_CALLBACK (limit_toggled_cb),
201 creator, 0);
202 limit_toggled_cb (priv->limit_check, creator);
204 gtk_size_group_add_widget (priv->button_size_group, priv->addbutton);
205 g_signal_connect_object (G_OBJECT (priv->addbutton), "clicked", G_CALLBACK (add_button_click_cb),
206 creator, 0);
208 setup_sort_option_menu (creator, priv->sort_menu, sort_options, num_sort_options);
210 priv->vbox = GTK_BOX (glade_xml_get_widget (xml, "sub_vbox"));
211 if (priv->creating)
212 append_row (creator);
214 mainbox = glade_xml_get_widget (xml, "main_vbox");
215 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (creator)->vbox), mainbox, FALSE, FALSE, 0);
216 gtk_widget_show_all (GTK_WIDGET (creator));
218 return G_OBJECT (creator);
221 static void
222 rb_query_creator_dispose (GObject *object)
224 RBQueryCreatorPrivate *priv;
226 g_return_if_fail (RB_IS_QUERY_CREATOR (object));
228 priv = QUERY_CREATOR_GET_PRIVATE (object);
229 g_return_if_fail (priv != NULL);
231 if (priv->property_size_group != NULL)
232 g_object_unref (G_OBJECT (priv->property_size_group));
233 priv->property_size_group = NULL;
234 if (priv->criteria_size_group != NULL)
235 g_object_unref (G_OBJECT (priv->criteria_size_group));
236 priv->criteria_size_group = NULL;
237 if (priv->entry_size_group != NULL)
238 g_object_unref (G_OBJECT (priv->entry_size_group));
239 priv->entry_size_group = NULL;
240 if (priv->button_size_group != NULL)
241 g_object_unref (G_OBJECT (priv->button_size_group));
242 priv->button_size_group = NULL;
244 if (priv->rows)
245 g_list_free (priv->rows);
246 priv->rows = NULL;
248 G_OBJECT_CLASS (rb_query_creator_parent_class)->dispose (object);
251 static void
252 rb_query_creator_set_property (GObject *object,
253 guint prop_id,
254 const GValue *value,
255 GParamSpec *pspec)
257 RBQueryCreator *creator = RB_QUERY_CREATOR (object);
258 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
260 switch (prop_id)
262 case PROP_DB:
263 priv->db = g_value_get_object (value);
264 break;
265 case PROP_CREATING:
266 priv->creating = g_value_get_boolean (value);
267 break;
268 default:
269 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
270 break;
274 static void
275 rb_query_creator_get_property (GObject *object,
276 guint prop_id,
277 GValue *value,
278 GParamSpec *pspec)
280 RBQueryCreator *creator = RB_QUERY_CREATOR (object);
281 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
283 switch (prop_id)
285 case PROP_DB:
286 g_value_set_object (value, priv->db);
287 break;
288 case PROP_CREATING:
289 g_value_set_boolean (value, priv->creating);
290 break;
291 default:
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
293 break;
298 GtkWidget *
299 rb_query_creator_new (RhythmDB *db)
301 return g_object_new (RB_TYPE_QUERY_CREATOR, "db", db, NULL);
304 static gboolean
305 rb_query_creator_load_query (RBQueryCreator *creator, GPtrArray *query,
306 int limit_count, int limit_size, int limit_time)
308 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
309 int i;
310 GList *rows;
311 gboolean disjunction = FALSE;
312 RhythmDBQueryData *qdata;
313 GPtrArray *subquery;
315 g_return_val_if_fail (query->len == 2, FALSE);
317 qdata = g_ptr_array_index (query, 1);
318 g_return_val_if_fail (qdata->type == RHYTHMDB_QUERY_SUBQUERY, FALSE);
320 subquery = qdata->subquery;
322 if (subquery->len > 0)
323 for (i = 0; i < subquery->len; i++) {
324 RhythmDBQueryData *data = g_ptr_array_index (subquery, i);
325 if (data->type != RHYTHMDB_QUERY_DISJUNCTION)
326 append_row (creator);
329 rows = priv->rows;
331 for (i = 0; i < subquery->len; i++) {
332 RhythmDBQueryData *data = g_ptr_array_index (subquery, i);
333 GtkOptionMenu *propmenu;
334 GtkWidget *criteria_menu;
335 int index;
336 const RBQueryCreatorPropertyType *property_type;
338 if (data->type == RHYTHMDB_QUERY_DISJUNCTION) {
339 disjunction = TRUE;
340 continue;
343 propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data), 0));
344 index = get_property_index_from_proptype (property_options, num_property_options, data->propid);
345 gtk_option_menu_set_history (propmenu, index);
347 criteria_menu = get_box_widget_at_pos (GTK_BOX (rows->data), 1);
348 select_criteria_from_value (creator, criteria_menu, data->propid, data->type);
350 property_type = property_options[index].property_type;
351 g_assert (property_type->criteria_set_widget_data != NULL);
352 property_type->criteria_set_widget_data (get_box_widget_at_pos (GTK_BOX (rows->data), 2),
353 data->val);
355 rows = rows->next;
358 /* setup the limits */
360 int limit;
362 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->disjunction_check),
363 disjunction);
364 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->limit_check),
365 limit_count || limit_size || limit_time);
367 if (limit_count > 0) {
368 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 0);
369 limit = limit_count;
370 } else if (limit_time > 0) {
371 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 3);
372 limit = limit_time / 60;
373 } else if (limit_size % 1000 == 0) {
374 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 2);
375 limit = limit_size / 1000;
376 } else {
377 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 1);
378 limit = limit_size;
381 gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->limit_entry), limit);
384 return TRUE;
387 static gboolean
388 rb_query_creator_set_sorting (RBQueryCreator *creator, const char *sort_column, gint sort_direction)
390 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
391 int i;
393 if (!sort_column || ! *sort_column) {
394 g_warning("No playlist sorting order");
396 sort_column = sort_options[DEFAULT_SORTING_COLUMN].sort_key;
397 sort_direction = DEFAULT_SORTING_ORDER;
400 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sort_desc), (sort_direction == GTK_SORT_DESCENDING));
402 for (i = 0; i < num_property_options; i++)
403 if (strcmp (sort_options[i].sort_key, sort_column) == 0)
404 break;
406 /* check that it is a valid sort option */
407 g_return_val_if_fail (i < num_property_options, FALSE);
409 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->sort_menu), i);
410 sort_option_menu_changed (GTK_OPTION_MENU (priv->sort_menu), creator); /* force the checkbox to change label */
411 return TRUE;
414 GtkWidget *
415 rb_query_creator_new_from_query (RhythmDB *db, GPtrArray *query,
416 int limit_count, int limit_size, int limit_time,
417 const char *sort_column, gint sort_direction)
419 RBQueryCreator *creator = g_object_new (RB_TYPE_QUERY_CREATOR, "db", db,
420 "creating", FALSE, NULL);
421 if (!creator)
422 return NULL;
424 if ( !rb_query_creator_load_query (creator, query, limit_count, limit_size, limit_time)
425 | !rb_query_creator_set_sorting (creator, sort_column, sort_direction)) {
426 gtk_widget_destroy (GTK_WIDGET (creator));
427 return NULL;
431 return GTK_WIDGET (creator);
434 GtkWidget *
435 get_box_widget_at_pos (GtkBox *box, guint pos)
437 GtkWidget *ret = NULL;
438 GList *children = gtk_container_get_children (GTK_CONTAINER (box));
439 GList *tem;
440 for (tem = children; tem; tem = tem->next) {
441 GValue thispos = { 0, };
442 g_value_init (&thispos, G_TYPE_INT);
443 gtk_container_child_get_property (GTK_CONTAINER (box),
444 GTK_WIDGET (tem->data),
445 "position", &thispos);
446 if (g_value_get_int (&thispos) == pos) {
447 ret = tem->data;
448 break;
451 g_list_free (children);
452 return GTK_WIDGET (ret);
455 static GtkWidget *
456 get_entry_for_property (RBQueryCreator *creator, RhythmDBPropType prop,
457 gboolean *constrain)
459 const RBQueryCreatorPropertyType *property_type;
460 int index = get_property_index_from_proptype (property_options, num_property_options, prop);
462 property_type = property_options[index].property_type;
463 g_assert (property_type->criteria_create_widget != NULL);
465 *constrain = TRUE;
466 return property_type->criteria_create_widget (constrain);
469 GPtrArray *
470 rb_query_creator_get_query (RBQueryCreator *creator)
472 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
473 GPtrArray *query;
474 GPtrArray *sub_query;
475 GList *rows, *row;
476 gboolean disjunction;
478 disjunction = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->disjunction_check));
479 sub_query = g_ptr_array_new ();
480 rows = priv->rows;
482 for (row = rows; row; row = row->next) {
483 GtkOptionMenu *propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data),
484 0));
485 GtkOptionMenu *criteria_menu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data),
486 1));
487 guint prop_position = gtk_option_menu_get_history (propmenu);
488 const RBQueryCreatorPropertyOption *prop_option = &property_options[prop_position];
489 const RBQueryCreatorCriteriaOption *criteria_options = prop_option->property_type->criteria_options;
490 const RBQueryCreatorCriteriaOption *criteria_option = &criteria_options[gtk_option_menu_get_history (criteria_menu)];
492 g_assert (prop_option->property_type->criteria_get_widget_data != NULL);
494 RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1);
495 GValue *val = g_new0 (GValue, 1);
497 data->type = criteria_option->val;
498 data->propid = criteria_option->strict ? prop_option->strict_val : prop_option->fuzzy_val;
500 prop_option->property_type->criteria_get_widget_data (get_box_widget_at_pos (GTK_BOX (row->data), 2), val);
501 data->val = val;
503 g_ptr_array_add (sub_query, data);
506 if (disjunction && row->next)
507 rhythmdb_query_append (priv->db,
508 sub_query,
509 RHYTHMDB_QUERY_DISJUNCTION,
510 RHYTHMDB_QUERY_END);
512 query = rhythmdb_query_parse (priv->db,
513 /* type=songs */
514 RHYTHMDB_QUERY_PROP_EQUALS,
515 RHYTHMDB_PROP_TYPE,
516 RHYTHMDB_ENTRY_TYPE_SONG,
517 /* the constructed query */
518 RHYTHMDB_QUERY_SUBQUERY,
519 sub_query,
520 RHYTHMDB_QUERY_END);
521 return query;
524 void
525 rb_query_creator_get_limit (RBQueryCreator *creator, RBQueryCreatorLimitType *type,
526 guint *limit)
528 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
529 guint limitpos;
531 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->limit_check)))
532 *limit = (guint) gtk_spin_button_get_value(GTK_SPIN_BUTTON (priv->limit_entry));
533 else
534 *limit = 0;
535 limitpos = gtk_option_menu_get_history (GTK_OPTION_MENU (priv->limit_option));
536 switch (limitpos)
538 case 0:
539 *type = RB_QUERY_CREATOR_LIMIT_COUNT;
540 break;
541 case 1:
542 *type = RB_QUERY_CREATOR_LIMIT_MB;
543 break;
544 case 2:
545 *type = RB_QUERY_CREATOR_LIMIT_MB;
546 *limit *= 1000;
547 break;
548 case 3:
549 *type = RB_QUERY_CREATOR_LIMIT_SECONDS;
550 *limit *= 60;
551 break;
552 default:
553 g_assert_not_reached ();
557 void
558 rb_query_creator_get_sort_order (RBQueryCreator *creator, const char **sort_column, gint *sort_direction)
560 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
561 int i;
563 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->sort_desc)))
564 *sort_direction = GTK_SORT_DESCENDING;
565 else
566 *sort_direction = GTK_SORT_ASCENDING;
568 i = gtk_option_menu_get_history (GTK_OPTION_MENU (priv->sort_menu));
569 *sort_column = sort_options[i].sort_key;
572 static void
573 limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator)
575 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
576 gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (limit));
578 gtk_widget_set_sensitive (priv->limit_entry, active);
579 gtk_widget_set_sensitive (priv->limit_option, active);
580 gtk_widget_set_sensitive (priv->sort_menu, active);
581 gtk_widget_set_sensitive (priv->sort_label, active);
582 gtk_widget_set_sensitive (priv->sort_desc, active);
585 static GtkWidget *
586 lookup_row_by_widget (RBQueryCreator *creator, GtkWidget *widget)
588 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
589 GList *rows = priv->rows;
590 GList *row;
591 GtkWidget *ret = NULL;
592 guint i;
594 for (row = rows, i = 0; row; row = row->next, i++) {
595 GList *columns = gtk_container_get_children (GTK_CONTAINER (row->data));
596 gboolean found = g_list_find (columns, widget) != NULL;
597 g_list_free (columns);
598 if (found) {
599 ret = row->data;
600 break;
603 return ret;
606 static void
607 remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator)
609 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
610 GtkWidget *row;
612 row = lookup_row_by_widget (creator, button);
613 g_assert (row);
614 gtk_container_remove (GTK_CONTAINER (priv->vbox),
615 GTK_WIDGET (row));
616 priv->rows = g_list_remove (priv->rows, row);
619 static void
620 add_button_click_cb (GtkWidget *button, RBQueryCreator *creator)
622 append_row (creator);
625 static GtkWidget *
626 append_row (RBQueryCreator *creator)
628 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
629 GtkWidget *option;
630 GtkWidget *criteria;
631 GtkWidget *entry;
632 GtkWidget *remove_button;
633 GtkBox *hbox;
634 GList *rows;
635 guint len;
636 gboolean constrain;
638 rows = priv->rows;
639 len = g_list_length (rows);
641 hbox = GTK_BOX (gtk_hbox_new (FALSE, 5));
642 gtk_box_pack_start_defaults (GTK_BOX (priv->vbox), GTK_WIDGET (hbox));
643 priv->rows = g_list_prepend (priv->rows, hbox);
644 gtk_box_reorder_child (priv->vbox, GTK_WIDGET (hbox), -1);
646 /* This is the main (leftmost) GtkOptionMenu, for types. */
647 option = create_property_option_menu (creator, property_options, num_property_options);
648 gtk_size_group_add_widget (priv->property_size_group, option);
649 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (option));
650 gtk_option_menu_set_history (GTK_OPTION_MENU (option), 0);
651 criteria = create_criteria_option_menu (property_options[0].property_type->criteria_options,
652 property_options[0].property_type->num_criteria_options);
653 gtk_size_group_add_widget (priv->criteria_size_group, criteria);
654 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (criteria));
656 entry = get_entry_for_property (creator, property_options[0].strict_val, &constrain);
657 if (constrain)
658 gtk_size_group_add_widget (priv->entry_size_group, entry);
659 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (entry));
661 remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
662 g_signal_connect_object (G_OBJECT (remove_button), "clicked", G_CALLBACK (remove_button_click_cb),
663 creator, 0);
664 gtk_size_group_add_widget (priv->button_size_group, remove_button);
665 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (remove_button));
667 gtk_widget_show_all (GTK_WIDGET (priv->vbox));
668 return GTK_WIDGET (hbox);
671 static int
672 get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
673 int length, RhythmDBPropType prop)
675 int i;
677 for (i = 0; i < length; i++)
678 if (prop == options[i].strict_val || prop == options[i].fuzzy_val)
679 return i;
681 g_assert_not_reached ();
685 static void
686 select_criteria_from_value (RBQueryCreator *creator,
687 GtkWidget *option_menu,
688 RhythmDBPropType prop,
689 RhythmDBQueryType qtype)
691 int i;
692 const RBQueryCreatorCriteriaOption *options;
693 guint length;
695 i = get_property_index_from_proptype (property_options, num_property_options, prop);
696 length = property_options[i].property_type->num_criteria_options;
697 options = property_options[i].property_type->criteria_options;
699 for (i = 0; i < length; i++) {
700 if (qtype == options[i].val) {
701 gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), i);
702 return;
705 g_assert_not_reached ();
708 static void
709 property_option_menu_changed (GtkOptionMenu *propmenu,
710 RBQueryCreator *creator)
712 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
713 GtkWidget *row;
714 GtkWidget *criteria;
715 GtkWidget *entry;
716 const RBQueryCreatorPropertyOption *prop_option;
717 const RBQueryCreatorCriteriaOption *criteria_options;
718 guint length;
719 guint old_value;
720 gboolean constrain;
722 prop_option = &property_options[gtk_option_menu_get_history (propmenu)];
723 old_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (propmenu), "prop-menu old-value"));
725 /* don't recreate the criteria menu and entry if they will be the same*/
726 if (prop_option->property_type == property_options[old_value].property_type)
727 return;
729 g_object_set_data (G_OBJECT (propmenu), "prop-menu old-value",
730 GINT_TO_POINTER (gtk_option_menu_get_history (propmenu)));
732 row = lookup_row_by_widget (creator, GTK_WIDGET (propmenu));
734 criteria = get_box_widget_at_pos (GTK_BOX (row), 1);
735 gtk_container_remove (GTK_CONTAINER (row), criteria);
737 criteria_options = prop_option->property_type->criteria_options;
738 length = prop_option->property_type->num_criteria_options;
740 criteria = create_criteria_option_menu (criteria_options, length);
741 gtk_widget_show (criteria);
742 gtk_size_group_add_widget (priv->criteria_size_group, criteria);
743 gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (criteria));
744 gtk_box_reorder_child (GTK_BOX (row), criteria, 1);
746 entry = get_box_widget_at_pos (GTK_BOX (row), 2);
747 gtk_container_remove (GTK_CONTAINER (row), entry);
748 entry = get_entry_for_property (creator, prop_option->strict_val,
749 &constrain);
750 gtk_widget_show (entry);
752 if (constrain)
753 gtk_size_group_add_widget (priv->entry_size_group, entry);
754 gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (entry));
755 gtk_box_reorder_child (GTK_BOX (row), entry, 2);
758 static GtkWidget*
759 create_property_option_menu (RBQueryCreator *creator,
760 const RBQueryCreatorPropertyOption *options,
761 int length)
763 GtkWidget *option_menu;
764 GtkWidget *menu;
765 GtkWidget *menu_item;
766 int i;
768 option_menu = gtk_option_menu_new ();
769 menu = gtk_menu_new ();
771 /* add the property options */
772 for (i = 0; i < length; i++) {
773 menu_item = gtk_menu_item_new_with_label (_(options[i].name));
774 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
775 gtk_widget_show (menu_item);
778 gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0);
779 g_object_set_data (G_OBJECT (option_menu), "prop-menu old-value", GINT_TO_POINTER (0));
781 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
782 gtk_widget_show (menu);
784 g_signal_connect_object (G_OBJECT (option_menu), "changed",
785 G_CALLBACK (property_option_menu_changed), creator, 0);
787 return option_menu;
790 static GtkWidget*
791 create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
792 int length)
794 GtkWidget *option_menu;
795 GtkWidget *menu;
796 GtkWidget *menu_item;
797 int i;
799 option_menu = gtk_option_menu_new ();
800 menu = gtk_menu_new ();
802 for (i = 0; i < length; i++) {
803 menu_item = gtk_menu_item_new_with_label (_(options[i].name));
804 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
805 gtk_widget_show (menu_item);
808 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
809 gtk_widget_show (menu);
811 return option_menu;
814 static void
815 sort_option_menu_changed (GtkOptionMenu *propmenu,
816 RBQueryCreator *creator)
818 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
819 int index = gtk_option_menu_get_history (propmenu);
821 gtk_button_set_label (GTK_BUTTON (priv->sort_desc), _(sort_options[index].sort_descending_name));
822 rb_debug("changing descending label to %s[%d]", sort_options[index].sort_descending_name, index);
825 static void
826 setup_sort_option_menu (RBQueryCreator *creator,
827 GtkWidget *option_menu,
828 const RBQueryCreatorSortOption *options,
829 int length)
831 GtkWidget *menu;
832 GtkWidget *menu_item;
833 int i;
835 menu = gtk_menu_new ();
836 gtk_widget_show (menu);
838 for (i = 0; i < length; i++) {
839 menu_item = gtk_menu_item_new_with_label (_(options[i].name));
840 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
841 gtk_widget_show (menu_item);
844 g_signal_connect_object (G_OBJECT (option_menu), "changed",
845 G_CALLBACK (sort_option_menu_changed), creator, 0);
847 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);