2 * arch-tag: Implementation of RhythmDB query creation properties
4 * Copyright (C) 2003, 2004 Colin Walters <walters@gnome.org>
5 * Copyright (C) 2005 James Livingston <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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include <libgnome/gnome-i18n.h>
27 #include "rb-query-creator-private.h"
28 #include "rb-rating.h"
30 const RBQueryCreatorPropertyType string_property_type
;
31 const RBQueryCreatorPropertyType escaped_string_property_type
;
32 const RBQueryCreatorPropertyType rating_property_type
;
33 const RBQueryCreatorPropertyType integer_property_type
;
34 const RBQueryCreatorPropertyType year_property_type
;
35 const RBQueryCreatorPropertyType duration_property_type
;
36 const RBQueryCreatorPropertyType relative_time_property_type
;
38 static GtkWidget
* stringCriteriaCreateWidget (gboolean
*constrain
);
39 static void stringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
40 static void stringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
41 static void escapedStringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
42 static void escapedStringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
43 static GtkWidget
* ratingCriteriaCreateWidget (gboolean
*constrain
);
44 static void ratingCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
45 static void ratingCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
46 static GtkWidget
* integerCriteriaCreateWidget (gboolean
*constrain
);
47 static void integerCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
48 static void integerCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
49 static GtkWidget
* yearCriteriaCreateWidget (gboolean
*constrain
);
50 static void yearCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
51 static void yearCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
52 static GtkWidget
* durationCriteriaCreateWidget (gboolean
*constrain
);
53 static void durationCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
54 static void durationCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
55 static GtkWidget
* relativeTimeCriteriaCreateWidget (gboolean
*constrain
);
56 static void relativeTimeCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
57 static void relativeTimeCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
61 * This table is the list of properties that are displayed in the query-creator
63 const RBQueryCreatorPropertyOption property_options
[] =
65 { N_("Title"), RHYTHMDB_PROP_TITLE
, RHYTHMDB_PROP_TITLE_FOLDED
, &string_property_type
},
66 { N_("Artist"), RHYTHMDB_PROP_ARTIST
, RHYTHMDB_PROP_ARTIST_FOLDED
, &string_property_type
},
67 { N_("Album"), RHYTHMDB_PROP_ALBUM
, RHYTHMDB_PROP_ALBUM_FOLDED
, &string_property_type
},
68 { N_("Genre"), RHYTHMDB_PROP_GENRE
, RHYTHMDB_PROP_GENRE_FOLDED
, &string_property_type
},
69 { N_("Year"), RHYTHMDB_PROP_DATE
, RHYTHMDB_PROP_DATE
, &year_property_type
},
70 { N_("Rating"), RHYTHMDB_PROP_RATING
, RHYTHMDB_PROP_RATING
, &rating_property_type
},
71 { N_("Path"), RHYTHMDB_PROP_LOCATION
, RHYTHMDB_PROP_LOCATION
, &escaped_string_property_type
},
73 { N_("Play Count"), RHYTHMDB_PROP_PLAY_COUNT
, RHYTHMDB_PROP_PLAY_COUNT
, &integer_property_type
},
74 { N_("Track Number"), RHYTHMDB_PROP_TRACK_NUMBER
, RHYTHMDB_PROP_TRACK_NUMBER
, &integer_property_type
},
75 { N_("Disc Number"), RHYTHMDB_PROP_DISC_NUMBER
, RHYTHMDB_PROP_DISC_NUMBER
, &integer_property_type
},
76 { N_("Bitrate"), RHYTHMDB_PROP_BITRATE
, RHYTHMDB_PROP_BITRATE
, &integer_property_type
},
78 { N_("Duration"), RHYTHMDB_PROP_DURATION
, RHYTHMDB_PROP_DURATION
, &duration_property_type
},
80 { N_("Time of Last Play"), RHYTHMDB_PROP_LAST_PLAYED
, RHYTHMDB_PROP_LAST_PLAYED
, &relative_time_property_type
},
81 { N_("Time Added to Library"), RHYTHMDB_PROP_FIRST_SEEN
, RHYTHMDB_PROP_FIRST_SEEN
, &relative_time_property_type
},
84 const int num_property_options
= G_N_ELEMENTS (property_options
);
88 * This table describes which properties can be used for sorting a playlist
89 * All entries MUST have column keys column keys listed in rb-entry-view.c
91 const RBQueryCreatorSortOption sort_options
[] =
93 { N_("Artist"), "Artist", N_("_In reverse alphabetical order") },
94 { N_("Album"), "Album", N_("_In reverse alphabetical order") },
95 { N_("Genre"), "Genre", N_("_In reverse alphabetical order") },
96 { N_("Title"), "Title", N_("_In reverse alphabetical order") },
97 { N_("Rating"), "Rating", N_("W_ith more highly rated tracks first") },
98 { N_("Play Count"), "PlayCount", N_("W_ith more often played songs first") },
99 { N_("Year"), "Year", N_("W_ith newer tracks first") },
100 { N_("Duration"), "Time", N_("W_ith longer tracks first") },
101 { N_("Track Number"), "Track", N_("_In decreasing order")},
102 { N_("Last Played"), "LastPlayed", N_("W_ith more recently played tracks first") },
103 { N_("Date Added"), "FirstSeen", N_("W_ith more recently added tracks first") },
106 const int num_sort_options
= G_N_ELEMENTS (sort_options
);
107 const int DEFAULT_SORTING_COLUMN
= 0;
108 const gint DEFAULT_SORTING_ORDER
= GTK_SORT_ASCENDING
;
112 * This is the property type for string properties
115 const RBQueryCreatorCriteriaOption string_criteria_options
[] =
117 { N_("contains"), 0, RHYTHMDB_QUERY_PROP_LIKE
},
118 { N_("does not contain"), 0, RHYTHMDB_QUERY_PROP_NOT_LIKE
},
119 { N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS
}
122 const RBQueryCreatorPropertyType string_property_type
=
124 G_N_ELEMENTS (string_criteria_options
),
125 string_criteria_options
,
126 stringCriteriaCreateWidget
,
127 stringCriteriaSetWidgetData
,
128 stringCriteriaGetWidgetData
131 const RBQueryCreatorPropertyType escaped_string_property_type
=
133 G_N_ELEMENTS (string_criteria_options
),
134 string_criteria_options
,
135 stringCriteriaCreateWidget
,
136 escapedStringCriteriaSetWidgetData
,
137 escapedStringCriteriaGetWidgetData
142 * This are the property types for numeric quantities, such as rating and playcounts
145 const RBQueryCreatorCriteriaOption numeric_criteria_options
[] =
147 { N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS
},
148 { N_("at least"), 1, RHYTHMDB_QUERY_PROP_GREATER
}, /* matches if A >= B */
149 { N_("at most"), 1, RHYTHMDB_QUERY_PROP_LESS
} /* matches if A <= B */
153 * Property type for date quantities
156 const RBQueryCreatorCriteriaOption year_criteria_options
[] =
158 { N_("in"), 1, RHYTHMDB_QUERY_PROP_YEAR_EQUALS
},
159 /* matches if within 1-JAN-YEAR to 31-DEC-YEAR */
160 { N_("after"), 1, RHYTHMDB_QUERY_PROP_YEAR_GREATER
},
161 /* matches if >= 31-DEC-YEAR */
162 { N_("before"), 1, RHYTHMDB_QUERY_PROP_YEAR_LESS
}
163 /* matches if < 1-DEC-YEAR */
166 const RBQueryCreatorPropertyType rating_property_type
=
168 G_N_ELEMENTS (numeric_criteria_options
),
169 numeric_criteria_options
,
170 ratingCriteriaCreateWidget
,
171 ratingCriteriaSetWidgetData
,
172 ratingCriteriaGetWidgetData
175 const RBQueryCreatorPropertyType integer_property_type
=
177 G_N_ELEMENTS (numeric_criteria_options
),
178 numeric_criteria_options
,
179 integerCriteriaCreateWidget
,
180 integerCriteriaSetWidgetData
,
181 integerCriteriaGetWidgetData
184 const RBQueryCreatorPropertyType year_property_type
=
186 G_N_ELEMENTS (year_criteria_options
),
187 year_criteria_options
,
188 yearCriteriaCreateWidget
,
189 yearCriteriaSetWidgetData
,
190 yearCriteriaGetWidgetData
193 const RBQueryCreatorPropertyType duration_property_type
=
195 G_N_ELEMENTS (numeric_criteria_options
),
196 numeric_criteria_options
,
197 durationCriteriaCreateWidget
,
198 durationCriteriaSetWidgetData
,
199 durationCriteriaGetWidgetData
204 * This is the property type for relative time properties, such as last played and first seen
210 gulong timeMultiplier
;
211 } RBQueryCreatorTimeUnitOption
;
213 const RBQueryCreatorCriteriaOption relative_time_criteria_options
[] =
216 * Translators: this will match when within <value> of the current time
217 * e.g. "in the last" "7 days" will match if within 7 days of the current time
219 { N_("in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
},
222 * Translators: this is the opposite of the above, and will match if not
223 * within <value> of the current time
225 { N_("not in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
}
228 const RBQueryCreatorPropertyType relative_time_property_type
=
230 G_N_ELEMENTS (relative_time_criteria_options
),
231 relative_time_criteria_options
,
232 relativeTimeCriteriaCreateWidget
,
233 relativeTimeCriteriaSetWidgetData
,
234 relativeTimeCriteriaGetWidgetData
237 const RBQueryCreatorTimeUnitOption time_unit_options
[] =
239 { N_("seconds"), 1 },
240 { N_("minutes"), 60 },
241 { N_("hours"), 60 * 60 },
242 { N_("days"), 60 * 60 * 24 },
243 { N_("weeks"), 60 * 60 * 24 * 7 }
246 const int time_unit_options_default
= 4; /* days */
250 * Implementation for the string properties, using a single GtkEntry.
254 stringCriteriaCreateWidget (gboolean
*constrain
)
256 return gtk_entry_new ();
260 stringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
262 gtk_entry_set_text (GTK_ENTRY (widget
), g_value_get_string (val
));
266 stringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
268 const char* text
= gtk_entry_get_text (GTK_ENTRY (widget
));
270 g_value_init (val
, G_TYPE_STRING
);
271 g_value_set_string (val
, text
);
274 /* escaped string operations, for use with URIs, etc */
277 escapedStringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
279 char *text
= gnome_vfs_unescape_string (g_value_get_string (val
), NULL
);
280 gtk_entry_set_text (GTK_ENTRY (widget
), text
);
285 escapedStringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
287 char *text
= gnome_vfs_escape_path_string (gtk_entry_get_text (GTK_ENTRY (widget
)));
289 g_value_init (val
, G_TYPE_STRING
);
290 g_value_set_string (val
, text
);
295 * Implementation for the ratings property, using the RbRating widget
299 set_rating_score (RBRating
*rating
, gdouble score
)
301 g_object_set (G_OBJECT (rating
), "rating", score
, NULL
);
305 ratingCriteriaCreateWidget (gboolean
*constrain
)
307 RBRating
*rating
= rb_rating_new ();
308 g_signal_connect_object (G_OBJECT (rating
), "rated",
309 G_CALLBACK (set_rating_score
), NULL
, 0);
311 return GTK_WIDGET (rating
);
315 ratingCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
317 g_object_set (G_OBJECT (widget
), "rating", g_value_get_double (val
), NULL
);
321 ratingCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
324 g_object_get (G_OBJECT (widget
), "rating", &rating
, NULL
);
326 g_value_init (val
, G_TYPE_DOUBLE
);
327 g_value_set_double (val
, rating
);
332 * Implementation for the integer properties, using a single GtkSpinButton.
336 integerCriteriaCreateWidget (gboolean
*constrain
)
338 return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT
, 1.0);
342 integerCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
344 gulong num
= g_value_get_ulong (val
);
345 g_assert (num
<= G_MAXINT
);
347 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget
), (gint
)num
);
351 integerCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
353 gint num
= gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget
));
356 g_value_init (val
, G_TYPE_ULONG
);
357 g_value_set_ulong (val
, (gulong
)num
);
360 /* Implementation for Year properties, using a single GtkSpinButton. */
363 yearCriteriaCreateWidget (gboolean
*constrain
)
365 return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT
, 1.0);
369 yearCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
372 gulong num
= g_value_get_ulong (val
);
373 g_assert (num
<= G_MAXINT
);
375 /* Create a date structure to get year from */
377 g_date_set_julian (date
, num
);
379 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget
), (gint
)g_date_get_year(date
));
384 yearCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
387 gint num
= gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget
));
390 /* New date structure, use year set in widget */
391 date
= g_date_new_dmy (1, G_DATE_JANUARY
, num
);
393 g_value_init (val
, G_TYPE_ULONG
);
394 g_value_set_ulong (val
, (gulong
) g_date_get_julian (date
) );
401 * Implementation for the duration property, using two single GtkSpinButtons.
405 durationCriteriaCreateWidget (gboolean
*constrain
)
408 GtkWidget
*minutesSpin
;
409 GtkWidget
*minutesLabel
;
410 GtkWidget
*secondsSpin
;
412 /* the widget for Duration is set out like the following [ 2] : [30] */
413 box
= GTK_BOX (gtk_hbox_new (FALSE
, 3));
415 minutesSpin
= gtk_spin_button_new_with_range (0.0, G_MAXINT
, 1.0);
416 gtk_box_pack_start (box
, minutesSpin
, FALSE
, FALSE
, 0);
418 minutesLabel
= gtk_label_new (":");
419 gtk_box_pack_start (box
, minutesLabel
, FALSE
, FALSE
, 0);
421 secondsSpin
= gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
422 gtk_box_pack_start (box
, secondsSpin
, FALSE
, FALSE
, 0);
424 gtk_widget_show_all (GTK_WIDGET (box
));
425 return GTK_WIDGET (box
);
429 durationCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
431 GtkSpinButton
*minutesSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
432 GtkSpinButton
*secondsSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 2));
434 gtk_spin_button_set_value (minutesSpinner
, (gdouble
) (g_value_get_ulong (val
) / 60));
435 gtk_spin_button_set_value (secondsSpinner
, (gdouble
) (g_value_get_ulong (val
) % 60));
439 durationCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
442 GtkSpinButton
*minutesSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
443 GtkSpinButton
*secondsSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 2));
445 gint value
= gtk_spin_button_get_value_as_int (minutesSpinner
) * 60
446 + gtk_spin_button_get_value_as_int (secondsSpinner
);
447 g_assert (value
>= 0);
449 g_value_init (val
, G_TYPE_ULONG
);
450 g_value_set_ulong (val
, (gulong
) value
);
455 * Implementation for the relative time properties, using a spin button and a menu.
459 create_time_unit_option_menu (const RBQueryCreatorTimeUnitOption
*options
,
462 GtkWidget
*menu
= gtk_menu_new ();
463 GtkWidget
*option_menu
= gtk_option_menu_new ();
466 for (i
= 0; i
< length
; i
++) {
467 GtkWidget
*menu_item
= gtk_menu_item_new_with_label (_(options
[i
].name
));
468 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
471 gtk_widget_show_all (menu
);
472 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu
), menu
);
478 relativeTimeCriteriaCreateWidget (gboolean
*constrain
)
483 GtkWidget
*timeOption
;
485 box
= GTK_BOX (gtk_hbox_new (FALSE
, 6));
487 timeSpin
= gtk_spin_button_new_with_range (1.0, G_MAXINT
, 1.0);
488 gtk_box_pack_start_defaults (box
, timeSpin
);
490 timeOption
= create_time_unit_option_menu (time_unit_options
, G_N_ELEMENTS (time_unit_options
));
491 gtk_option_menu_set_history(GTK_OPTION_MENU (timeOption
), time_unit_options_default
);
492 gtk_box_pack_start_defaults (box
, timeOption
);
494 gtk_widget_show_all (GTK_WIDGET (box
));
495 return GTK_WIDGET (box
);
499 relativeTimeCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
501 GtkBox
*box
= GTK_BOX (widget
);
503 GtkSpinButton
*timeSpin
= GTK_SPIN_BUTTON (get_box_widget_at_pos (box
, 0));
504 GtkOptionMenu
*unitMenu
= GTK_OPTION_MENU (get_box_widget_at_pos (box
, 1));
506 gulong time
= g_value_get_ulong (val
);
510 /* determine the best units to use for the given value */
511 for (i
= 0; i
< G_N_ELEMENTS(time_unit_options
); i
++) {
512 /* find out if the time is an even multiple of the unit */
513 if (time
% time_unit_options
[i
].timeMultiplier
== 0)
517 time
= time
/ time_unit_options
[unit
].timeMultiplier
;
518 g_assert (time
< G_MAXINT
);
519 /* set the time value and unit*/
520 gtk_option_menu_set_history(unitMenu
, unit
);
521 gtk_spin_button_set_value(timeSpin
, time
);
525 relativeTimeCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
527 GtkSpinButton
*timeSpin
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
528 GtkOptionMenu
*unitMenu
= GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (widget
), 1));
530 gulong timeMultiplier
= time_unit_options
[gtk_option_menu_get_history (unitMenu
)].timeMultiplier
;
531 gint value
= gtk_spin_button_get_value_as_int (timeSpin
) * timeMultiplier
;
532 g_assert (value
>= 0);
534 g_value_init (val
, G_TYPE_ULONG
);
535 g_value_set_ulong (val
, (gulong
) value
);