* podcast/rb-podcast-manager.c: (rb_podcast_manager_add_post): add
[rhythmbox.git] / widgets / rb-query-creator.c
blob06a654d5e84e8316ca627fd9e7547c0a2dc7ab2c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of RhythmDB query creation dialog
5 * Copyright (C) 2003, 2004 Colin Walters <walters@gnome.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
24 #include <string.h>
25 #include <stdlib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
30 #include "rhythmdb.h"
31 #include "rb-query-creator.h"
32 #include "rb-query-creator-private.h"
33 #include "rb-dialog.h"
34 #include "rb-debug.h"
35 #include "rb-preferences.h"
36 #include "rb-glade-helpers.h"
37 #include "rb-util.h"
39 static void rb_query_creator_class_init (RBQueryCreatorClass *klass);
40 static GObject *rb_query_creator_constructor (GType type, guint n_construct_properties,
41 GObjectConstructParam *construct_properties);
42 static void rb_query_creator_dispose (GObject *object);
43 static void rb_query_creator_set_property (GObject *object,
44 guint prop_id,
45 const GValue *value,
46 GParamSpec *pspec);
47 static void rb_query_creator_get_property (GObject *object,
48 guint prop_id,
49 GValue *value,
50 GParamSpec *pspec);
51 static void select_criteria_from_value (RBQueryCreator *creator,
52 GtkWidget *option_menu,
53 RhythmDBPropType prop,
54 RhythmDBQueryType qtype);
55 static GtkWidget * create_property_option_menu (RBQueryCreator *creator,
56 const RBQueryCreatorPropertyOption *options,
57 int length);
58 static GtkWidget * create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
59 int length);
60 static void setup_sort_option_menu (RBQueryCreator *creator,
61 GtkWidget *option_menu,
62 const RBQueryCreatorSortOption *options,
63 int length);
65 static GtkWidget * append_row (RBQueryCreator *creator);
66 static void add_button_click_cb (GtkWidget *button, RBQueryCreator *creator);
67 static void remove_button_click_cb (GtkWidget *button, RBQueryCreator *creator);
68 static void limit_toggled_cb (GtkWidget *limit, RBQueryCreator *creator);
70 static int get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
71 int length, RhythmDBPropType prop);
72 static void sort_option_menu_changed (GtkOptionMenu *propmenu, RBQueryCreator *creator);
74 typedef struct
76 RhythmDB *db;
78 gboolean creating;
80 GtkSizeGroup *property_size_group;
81 GtkSizeGroup *criteria_size_group;
82 GtkSizeGroup *entry_size_group;
83 GtkSizeGroup *button_size_group;
85 GtkBox *vbox;
86 GList *rows;
88 GtkWidget *addbutton;
89 GtkWidget *disjunction_check;
90 GtkWidget *limit_check;
91 GtkWidget *limit_entry;
92 GtkWidget *limit_option;
93 GtkWidget *sort_label;
94 GtkWidget *sort_menu;
95 GtkWidget *sort_desc;
96 } RBQueryCreatorPrivate;
98 G_DEFINE_TYPE (RBQueryCreator, rb_query_creator, GTK_TYPE_DIALOG)
99 #define QUERY_CREATOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), rb_query_creator_get_type(), RBQueryCreatorPrivate))
101 enum
103 PROP_0,
104 PROP_DB,
105 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,
145 guint n_construct_properties,
146 GObjectConstructParam *construct_properties)
148 RBQueryCreatorPrivate *priv;
149 RBQueryCreator *creator;
150 GladeXML *xml;
151 GtkWidget *mainbox;
153 creator = RB_QUERY_CREATOR (G_OBJECT_CLASS (rb_query_creator_parent_class)
154 ->constructor (type, n_construct_properties, construct_properties));
155 priv = QUERY_CREATOR_GET_PRIVATE (creator);
157 if (priv->creating) {
158 gtk_dialog_add_button (GTK_DIALOG (creator),
159 GTK_STOCK_CANCEL,
160 GTK_RESPONSE_CLOSE);
161 gtk_dialog_add_button (GTK_DIALOG (creator),
162 GTK_STOCK_NEW,
163 GTK_RESPONSE_OK);
164 } else {
165 gtk_dialog_add_button (GTK_DIALOG (creator),
166 GTK_STOCK_CLOSE,
167 GTK_RESPONSE_CLOSE);
169 gtk_dialog_set_default_response (GTK_DIALOG (creator),
170 GTK_RESPONSE_CLOSE);
172 priv->property_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
173 priv->criteria_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
174 priv->entry_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
175 priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
177 if (priv->creating)
178 gtk_window_set_title (GTK_WINDOW (creator), _("Create Automatic Playlist"));
179 else
180 gtk_window_set_title (GTK_WINDOW (creator), _("Edit Automatic Playlist"));
182 gtk_container_set_border_width (GTK_CONTAINER (creator), 5);
183 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (creator)->vbox), 2);
184 gtk_dialog_set_has_separator (GTK_DIALOG (creator), FALSE);
186 xml = rb_glade_xml_new ("create-playlist.glade",
187 "main_vbox",
188 creator);
190 priv->disjunction_check = GTK_WIDGET (glade_xml_get_widget (xml, "disjunctionCheck"));
191 priv->limit_check = GTK_WIDGET (glade_xml_get_widget (xml, "limitCheck"));
192 priv->limit_entry = GTK_WIDGET (glade_xml_get_widget (xml, "limitEntry"));
193 priv->limit_option = GTK_WIDGET (glade_xml_get_widget (xml, "limitOption"));
194 priv->addbutton = GTK_WIDGET (glade_xml_get_widget (xml, "addButton"));
195 priv->sort_label = GTK_WIDGET (glade_xml_get_widget (xml, "sortLabel"));
196 priv->sort_menu = GTK_WIDGET (glade_xml_get_widget (xml, "sortMenu"));
197 priv->sort_desc = GTK_WIDGET (glade_xml_get_widget (xml, "sortDesc"));
199 g_signal_connect_object (G_OBJECT (priv->limit_check), "toggled", G_CALLBACK (limit_toggled_cb),
200 creator, 0);
201 limit_toggled_cb (priv->limit_check, creator);
203 gtk_size_group_add_widget (priv->button_size_group, priv->addbutton);
204 g_signal_connect_object (G_OBJECT (priv->addbutton), "clicked", G_CALLBACK (add_button_click_cb),
205 creator, 0);
207 setup_sort_option_menu (creator, priv->sort_menu, sort_options, num_sort_options);
209 priv->vbox = GTK_BOX (glade_xml_get_widget (xml, "sub_vbox"));
210 if (priv->creating)
211 append_row (creator);
213 mainbox = glade_xml_get_widget (xml, "main_vbox");
214 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (creator)->vbox), mainbox, FALSE, FALSE, 0);
215 gtk_widget_show_all (GTK_WIDGET (creator));
217 g_object_unref (xml);
219 return G_OBJECT (creator);
222 static void
223 rb_query_creator_dispose (GObject *object)
225 RBQueryCreatorPrivate *priv;
227 g_return_if_fail (RB_IS_QUERY_CREATOR (object));
229 priv = QUERY_CREATOR_GET_PRIVATE (object);
230 g_return_if_fail (priv != NULL);
232 if (priv->property_size_group != NULL)
233 g_object_unref (G_OBJECT (priv->property_size_group));
234 priv->property_size_group = NULL;
235 if (priv->criteria_size_group != NULL)
236 g_object_unref (G_OBJECT (priv->criteria_size_group));
237 priv->criteria_size_group = NULL;
238 if (priv->entry_size_group != NULL)
239 g_object_unref (G_OBJECT (priv->entry_size_group));
240 priv->entry_size_group = NULL;
241 if (priv->button_size_group != NULL)
242 g_object_unref (G_OBJECT (priv->button_size_group));
243 priv->button_size_group = NULL;
245 if (priv->rows)
246 g_list_free (priv->rows);
247 priv->rows = NULL;
249 G_OBJECT_CLASS (rb_query_creator_parent_class)->dispose (object);
252 static void
253 rb_query_creator_set_property (GObject *object,
254 guint prop_id,
255 const GValue *value,
256 GParamSpec *pspec)
258 RBQueryCreator *creator = RB_QUERY_CREATOR (object);
259 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
261 switch (prop_id)
263 case PROP_DB:
264 priv->db = g_value_get_object (value);
265 break;
266 case PROP_CREATING:
267 priv->creating = g_value_get_boolean (value);
268 break;
269 default:
270 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
271 break;
275 static void
276 rb_query_creator_get_property (GObject *object,
277 guint prop_id,
278 GValue *value,
279 GParamSpec *pspec)
281 RBQueryCreator *creator = RB_QUERY_CREATOR (object);
282 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
284 switch (prop_id)
286 case PROP_DB:
287 g_value_set_object (value, priv->db);
288 break;
289 case PROP_CREATING:
290 g_value_set_boolean (value, priv->creating);
291 break;
292 default:
293 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
294 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,
306 GPtrArray *query,
307 RhythmDBQueryModelLimitType limit_type,
308 GValueArray *limit_value)
310 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
311 int i;
312 GList *rows;
313 gboolean disjunction = FALSE;
314 RhythmDBQueryData *qdata;
315 GPtrArray *subquery;
317 g_return_val_if_fail (query->len == 2, FALSE);
319 qdata = g_ptr_array_index (query, 1);
320 g_return_val_if_fail (qdata->type == RHYTHMDB_QUERY_SUBQUERY, FALSE);
322 subquery = qdata->subquery;
324 if (subquery->len > 0)
325 for (i = 0; i < subquery->len; i++) {
326 RhythmDBQueryData *data = g_ptr_array_index (subquery, i);
327 if (data->type != RHYTHMDB_QUERY_DISJUNCTION)
328 append_row (creator);
331 rows = priv->rows;
333 for (i = 0; i < subquery->len; i++) {
334 RhythmDBQueryData *data = g_ptr_array_index (subquery, i);
335 GtkOptionMenu *propmenu;
336 GtkWidget *criteria_menu;
337 int index;
338 const RBQueryCreatorPropertyType *property_type;
340 if (data->type == RHYTHMDB_QUERY_DISJUNCTION) {
341 disjunction = TRUE;
342 continue;
345 propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (rows->data), 0));
346 index = get_property_index_from_proptype (property_options, num_property_options, data->propid);
347 gtk_option_menu_set_history (propmenu, index);
349 criteria_menu = get_box_widget_at_pos (GTK_BOX (rows->data), 1);
350 select_criteria_from_value (creator, criteria_menu, data->propid, data->type);
352 property_type = property_options[index].property_type;
353 g_assert (property_type->criteria_set_widget_data != NULL);
354 property_type->criteria_set_widget_data (get_box_widget_at_pos (GTK_BOX (rows->data), 2),
355 data->val);
357 rows = rows->next;
360 /* setup the limits */
362 guint64 limit;
364 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->disjunction_check),
365 disjunction);
366 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->limit_check),
367 limit_type != RHYTHMDB_QUERY_MODEL_LIMIT_NONE);
369 switch (limit_type) {
370 case RHYTHMDB_QUERY_MODEL_LIMIT_NONE:
371 limit = 0;
372 break;
374 case RHYTHMDB_QUERY_MODEL_LIMIT_COUNT:
375 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 0);
376 limit = g_value_get_ulong (g_value_array_get_nth (limit_value, 0));
377 break;
379 case RHYTHMDB_QUERY_MODEL_LIMIT_TIME:
380 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 3);
381 /* convert to minutes */
382 limit = g_value_get_ulong (g_value_array_get_nth (limit_value, 0)) / 60;
383 break;
385 case RHYTHMDB_QUERY_MODEL_LIMIT_SIZE:
386 limit = g_value_get_uint64 (g_value_array_get_nth (limit_value, 0));
388 if (limit % 1000 == 0) {
389 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 2);
390 limit /= 1000;
391 } else {
392 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->limit_option), 1);
395 break;
396 default:
397 g_assert_not_reached ();
400 gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->limit_entry), limit);
403 return TRUE;
406 static gboolean
407 rb_query_creator_set_sorting (RBQueryCreator *creator,
408 const char *sort_column,
409 gint sort_direction)
411 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
412 int i;
414 if (!sort_column || ! *sort_column) {
415 g_warning("No playlist sorting order");
417 sort_column = sort_options[DEFAULT_SORTING_COLUMN].sort_key;
418 sort_direction = DEFAULT_SORTING_ORDER;
421 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sort_desc), (sort_direction == GTK_SORT_DESCENDING));
423 for (i = 0; i < num_sort_options; i++)
424 if (strcmp (sort_options[i].sort_key, sort_column) == 0)
425 break;
427 /* check that it is a valid sort option */
428 g_return_val_if_fail (i < num_property_options, FALSE);
430 gtk_option_menu_set_history (GTK_OPTION_MENU (priv->sort_menu), i);
431 sort_option_menu_changed (GTK_OPTION_MENU (priv->sort_menu), creator); /* force the checkbox to change label */
433 return TRUE;
436 GtkWidget *
437 rb_query_creator_new_from_query (RhythmDB *db,
438 GPtrArray *query,
439 RhythmDBQueryModelLimitType limit_type,
440 GValueArray *limit_value,
441 const char *sort_column,
442 gint sort_direction)
444 RBQueryCreator *creator = g_object_new (RB_TYPE_QUERY_CREATOR, "db", db,
445 "creating", FALSE, NULL);
446 if (!creator)
447 return NULL;
449 if ( !rb_query_creator_load_query (creator, query, limit_type, limit_value)
450 | !rb_query_creator_set_sorting (creator, sort_column, sort_direction)) {
451 gtk_widget_destroy (GTK_WIDGET (creator));
452 return NULL;
455 return GTK_WIDGET (creator);
458 GtkWidget *
459 get_box_widget_at_pos (GtkBox *box, guint pos)
461 GtkWidget *ret = NULL;
462 GList *children = gtk_container_get_children (GTK_CONTAINER (box));
463 GList *tem;
464 for (tem = children; tem; tem = tem->next) {
465 GValue thispos = { 0, };
466 g_value_init (&thispos, G_TYPE_INT);
467 gtk_container_child_get_property (GTK_CONTAINER (box),
468 GTK_WIDGET (tem->data),
469 "position", &thispos);
470 if (g_value_get_int (&thispos) == pos) {
471 ret = tem->data;
472 break;
475 g_list_free (children);
476 return GTK_WIDGET (ret);
479 static GtkWidget *
480 get_entry_for_property (RBQueryCreator *creator,
481 RhythmDBPropType prop,
482 gboolean *constrain)
484 const RBQueryCreatorPropertyType *property_type;
485 int index = get_property_index_from_proptype (property_options, num_property_options, prop);
487 property_type = property_options[index].property_type;
488 g_assert (property_type->criteria_create_widget != NULL);
490 *constrain = TRUE;
491 return property_type->criteria_create_widget (constrain);
494 GPtrArray *
495 rb_query_creator_get_query (RBQueryCreator *creator)
497 RBQueryCreatorPrivate *priv;
498 GPtrArray *query;
499 GPtrArray *sub_query;
500 GList *rows, *row;
501 gboolean disjunction;
503 g_return_val_if_fail (RB_IS_QUERY_CREATOR (creator), NULL);
505 priv = QUERY_CREATOR_GET_PRIVATE (creator);
507 disjunction = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->disjunction_check));
508 sub_query = g_ptr_array_new ();
509 rows = priv->rows;
511 for (row = rows; row; row = row->next) {
512 GtkOptionMenu *propmenu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data),
513 0));
514 GtkOptionMenu *criteria_menu = GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (row->data),
515 1));
516 guint prop_position = gtk_option_menu_get_history (propmenu);
517 const RBQueryCreatorPropertyOption *prop_option = &property_options[prop_position];
518 const RBQueryCreatorCriteriaOption *criteria_options = prop_option->property_type->criteria_options;
519 const RBQueryCreatorCriteriaOption *criteria_option = &criteria_options[gtk_option_menu_get_history (criteria_menu)];
521 g_assert (prop_option->property_type->criteria_get_widget_data != NULL);
523 RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1);
524 GValue *val = g_new0 (GValue, 1);
526 data->type = criteria_option->val;
527 data->propid = criteria_option->strict ? prop_option->strict_val : prop_option->fuzzy_val;
529 prop_option->property_type->criteria_get_widget_data (get_box_widget_at_pos (GTK_BOX (row->data), 2), val);
530 data->val = val;
532 g_ptr_array_add (sub_query, data);
535 if (disjunction && row->next)
536 rhythmdb_query_append (priv->db,
537 sub_query,
538 RHYTHMDB_QUERY_DISJUNCTION,
539 RHYTHMDB_QUERY_END);
541 query = rhythmdb_query_parse (priv->db,
542 /* type=songs */
543 RHYTHMDB_QUERY_PROP_EQUALS,
544 RHYTHMDB_PROP_TYPE,
545 RHYTHMDB_ENTRY_TYPE_SONG,
546 /* the constructed query */
547 RHYTHMDB_QUERY_SUBQUERY,
548 sub_query,
549 RHYTHMDB_QUERY_END);
550 return query;
553 void
554 rb_query_creator_get_limit (RBQueryCreator *creator,
555 RhythmDBQueryModelLimitType *type,
556 GValueArray **limit)
558 RBQueryCreatorPrivate *priv;
560 g_return_if_fail (RB_IS_QUERY_CREATOR (creator));
562 priv = QUERY_CREATOR_GET_PRIVATE (creator);
564 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->limit_check))) {
565 guint64 l;
567 l = gtk_spin_button_get_value(GTK_SPIN_BUTTON (priv->limit_entry));
568 *limit = g_value_array_new (0);
570 switch (gtk_option_menu_get_history (GTK_OPTION_MENU (priv->limit_option))) {
571 case 0:
572 *type = RHYTHMDB_QUERY_MODEL_LIMIT_COUNT;
573 rb_value_array_append_data (*limit, G_TYPE_ULONG, (gulong)l);
574 break;
575 case 1:
576 *type = RHYTHMDB_QUERY_MODEL_LIMIT_SIZE;
577 rb_value_array_append_data (*limit, G_TYPE_UINT64, l);
578 break;
580 case 2:
581 *type = RHYTHMDB_QUERY_MODEL_LIMIT_SIZE;
582 rb_value_array_append_data (*limit, G_TYPE_ULONG, (gulong)l);
583 break;
585 case 3:
586 *type = RHYTHMDB_QUERY_MODEL_LIMIT_TIME;
587 rb_value_array_append_data (*limit, G_TYPE_ULONG, (gulong)l * 60);
588 break;
590 default:
591 g_assert_not_reached ();
593 } else {
594 *type = RHYTHMDB_QUERY_MODEL_LIMIT_NONE;
595 *limit = NULL;
599 void
600 rb_query_creator_get_sort_order (RBQueryCreator *creator,
601 const char **sort_column,
602 gint *sort_direction)
604 RBQueryCreatorPrivate *priv;
606 g_return_if_fail (RB_IS_QUERY_CREATOR (creator));
608 priv = QUERY_CREATOR_GET_PRIVATE (creator);
610 if (sort_direction != NULL) {
611 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->sort_desc)))
612 *sort_direction = GTK_SORT_DESCENDING;
613 else
614 *sort_direction = GTK_SORT_ASCENDING;
617 if (sort_column != NULL) {
618 int i;
619 i = gtk_option_menu_get_history (GTK_OPTION_MENU (priv->sort_menu));
620 *sort_column = sort_options[i].sort_key;
624 static void
625 limit_toggled_cb (GtkWidget *limit,
626 RBQueryCreator *creator)
628 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
629 gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (limit));
631 gtk_widget_set_sensitive (priv->limit_entry, active);
632 gtk_widget_set_sensitive (priv->limit_option, active);
633 gtk_widget_set_sensitive (priv->sort_menu, active);
634 gtk_widget_set_sensitive (priv->sort_label, active);
635 gtk_widget_set_sensitive (priv->sort_desc, active);
638 static GtkWidget *
639 lookup_row_by_widget (RBQueryCreator *creator,
640 GtkWidget *widget)
642 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
643 GList *rows = priv->rows;
644 GList *row;
645 GtkWidget *ret = NULL;
646 guint i;
648 for (row = rows, i = 0; row; row = row->next, i++) {
649 GList *columns = gtk_container_get_children (GTK_CONTAINER (row->data));
650 gboolean found = g_list_find (columns, widget) != NULL;
651 g_list_free (columns);
652 if (found) {
653 ret = row->data;
654 break;
657 return ret;
660 static void
661 remove_button_click_cb (GtkWidget *button,
662 RBQueryCreator *creator)
664 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
665 GtkWidget *row;
667 row = lookup_row_by_widget (creator, button);
668 g_assert (row);
669 gtk_container_remove (GTK_CONTAINER (priv->vbox),
670 GTK_WIDGET (row));
671 priv->rows = g_list_remove (priv->rows, row);
674 static void
675 add_button_click_cb (GtkWidget *button,
676 RBQueryCreator *creator)
678 append_row (creator);
681 static GtkWidget *
682 append_row (RBQueryCreator *creator)
684 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
685 GtkWidget *option;
686 GtkWidget *criteria;
687 GtkWidget *entry;
688 GtkWidget *remove_button;
689 GtkBox *hbox;
690 GList *rows;
691 guint len;
692 gboolean constrain;
694 rows = priv->rows;
695 len = g_list_length (rows);
697 hbox = GTK_BOX (gtk_hbox_new (FALSE, 5));
698 gtk_box_pack_start_defaults (GTK_BOX (priv->vbox), GTK_WIDGET (hbox));
699 priv->rows = g_list_prepend (priv->rows, hbox);
700 gtk_box_reorder_child (priv->vbox, GTK_WIDGET (hbox), -1);
702 /* This is the main (leftmost) GtkOptionMenu, for types. */
703 option = create_property_option_menu (creator, property_options, num_property_options);
704 gtk_size_group_add_widget (priv->property_size_group, option);
705 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (option));
706 gtk_option_menu_set_history (GTK_OPTION_MENU (option), 0);
707 criteria = create_criteria_option_menu (property_options[0].property_type->criteria_options,
708 property_options[0].property_type->num_criteria_options);
709 gtk_size_group_add_widget (priv->criteria_size_group, criteria);
710 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (criteria));
712 entry = get_entry_for_property (creator, property_options[0].strict_val, &constrain);
713 if (constrain)
714 gtk_size_group_add_widget (priv->entry_size_group, entry);
715 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (entry));
717 remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
718 g_signal_connect_object (G_OBJECT (remove_button), "clicked", G_CALLBACK (remove_button_click_cb),
719 creator, 0);
720 gtk_size_group_add_widget (priv->button_size_group, remove_button);
721 gtk_box_pack_start_defaults (hbox, GTK_WIDGET (remove_button));
723 gtk_widget_show_all (GTK_WIDGET (priv->vbox));
724 return GTK_WIDGET (hbox);
727 static int
728 get_property_index_from_proptype (const RBQueryCreatorPropertyOption *options,
729 int length,
730 RhythmDBPropType prop)
732 int i;
734 for (i = 0; i < length; i++)
735 if (prop == options[i].strict_val || prop == options[i].fuzzy_val)
736 return i;
738 g_assert_not_reached ();
741 static void
742 select_criteria_from_value (RBQueryCreator *creator,
743 GtkWidget *option_menu,
744 RhythmDBPropType prop,
745 RhythmDBQueryType qtype)
747 int i;
748 const RBQueryCreatorCriteriaOption *options;
749 guint length;
751 i = get_property_index_from_proptype (property_options, num_property_options, prop);
752 length = property_options[i].property_type->num_criteria_options;
753 options = property_options[i].property_type->criteria_options;
755 for (i = 0; i < length; i++) {
756 if (qtype == options[i].val) {
757 gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), i);
758 return;
761 g_assert_not_reached ();
764 static void
765 property_option_menu_changed (GtkOptionMenu *propmenu,
766 RBQueryCreator *creator)
768 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
769 GtkWidget *row;
770 GtkWidget *criteria;
771 GtkWidget *entry;
772 const RBQueryCreatorPropertyOption *prop_option;
773 const RBQueryCreatorCriteriaOption *criteria_options;
774 guint length;
775 guint old_value;
776 gboolean constrain;
778 prop_option = &property_options[gtk_option_menu_get_history (propmenu)];
779 old_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (propmenu), "prop-menu old-value"));
781 /* don't recreate the criteria menu and entry if they will be the same*/
782 if (prop_option->property_type == property_options[old_value].property_type)
783 return;
785 g_object_set_data (G_OBJECT (propmenu), "prop-menu old-value",
786 GINT_TO_POINTER (gtk_option_menu_get_history (propmenu)));
788 row = lookup_row_by_widget (creator, GTK_WIDGET (propmenu));
790 criteria = get_box_widget_at_pos (GTK_BOX (row), 1);
791 gtk_container_remove (GTK_CONTAINER (row), criteria);
793 criteria_options = prop_option->property_type->criteria_options;
794 length = prop_option->property_type->num_criteria_options;
796 criteria = create_criteria_option_menu (criteria_options, length);
797 gtk_widget_show (criteria);
798 gtk_size_group_add_widget (priv->criteria_size_group, criteria);
799 gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (criteria));
800 gtk_box_reorder_child (GTK_BOX (row), criteria, 1);
802 entry = get_box_widget_at_pos (GTK_BOX (row), 2);
803 gtk_container_remove (GTK_CONTAINER (row), entry);
804 entry = get_entry_for_property (creator, prop_option->strict_val,
805 &constrain);
806 gtk_widget_show (entry);
808 if (constrain)
809 gtk_size_group_add_widget (priv->entry_size_group, entry);
810 gtk_box_pack_start_defaults (GTK_BOX (row), GTK_WIDGET (entry));
811 gtk_box_reorder_child (GTK_BOX (row), entry, 2);
814 static GtkWidget*
815 create_property_option_menu (RBQueryCreator *creator,
816 const RBQueryCreatorPropertyOption *options,
817 int length)
819 GtkWidget *option_menu;
820 GtkWidget *menu;
821 GtkWidget *menu_item;
822 int i;
824 option_menu = gtk_option_menu_new ();
825 menu = gtk_menu_new ();
827 /* add the property options */
828 for (i = 0; i < length; i++) {
829 menu_item = gtk_menu_item_new_with_label (_(options[i].name));
830 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
831 gtk_widget_show (menu_item);
834 gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0);
835 g_object_set_data (G_OBJECT (option_menu), "prop-menu old-value", GINT_TO_POINTER (0));
837 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
838 gtk_widget_show (menu);
840 g_signal_connect_object (G_OBJECT (option_menu), "changed",
841 G_CALLBACK (property_option_menu_changed), creator, 0);
843 return option_menu;
846 static GtkWidget*
847 create_criteria_option_menu (const RBQueryCreatorCriteriaOption *options,
848 int length)
850 GtkWidget *option_menu;
851 GtkWidget *menu;
852 GtkWidget *menu_item;
853 int i;
855 option_menu = gtk_option_menu_new ();
856 menu = gtk_menu_new ();
858 for (i = 0; i < length; i++) {
859 menu_item = gtk_menu_item_new_with_label (_(options[i].name));
860 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
861 gtk_widget_show (menu_item);
864 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
865 gtk_widget_show (menu);
867 return option_menu;
870 static void
871 sort_option_menu_changed (GtkOptionMenu *propmenu,
872 RBQueryCreator *creator)
874 RBQueryCreatorPrivate *priv = QUERY_CREATOR_GET_PRIVATE (creator);
875 int index = gtk_option_menu_get_history (propmenu);
877 gtk_button_set_label (GTK_BUTTON (priv->sort_desc), _(sort_options[index].sort_descending_name));
878 rb_debug("changing descending label to %s[%d]", sort_options[index].sort_descending_name, index);
881 static void
882 setup_sort_option_menu (RBQueryCreator *creator,
883 GtkWidget *option_menu,
884 const RBQueryCreatorSortOption *options,
885 int length)
887 GtkWidget *menu;
888 GtkWidget *menu_item;
889 int i;
891 menu = gtk_menu_new ();
892 gtk_widget_show (menu);
894 for (i = 0; i < length; i++) {
895 menu_item = gtk_menu_item_new_with_label (_(options[i].name));
896 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
897 gtk_widget_show (menu_item);
900 g_signal_connect_object (G_OBJECT (option_menu), "changed",
901 G_CALLBACK (sort_option_menu_changed), creator, 0);
903 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);