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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <libgnome/gnome-i18n.h>
25 #include <libgnomevfs/gnome-vfs-utils.h>
28 #include "rb-query-creator-private.h"
29 #include "rb-rating.h"
31 const RBQueryCreatorPropertyType string_property_type
;
32 const RBQueryCreatorPropertyType escaped_string_property_type
;
33 const RBQueryCreatorPropertyType rating_property_type
;
34 const RBQueryCreatorPropertyType integer_property_type
;
35 const RBQueryCreatorPropertyType year_property_type
;
36 const RBQueryCreatorPropertyType duration_property_type
;
37 const RBQueryCreatorPropertyType relative_time_property_type
;
39 static GtkWidget
* stringCriteriaCreateWidget (gboolean
*constrain
);
40 static void stringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
41 static void stringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
42 static void escapedStringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
43 static void escapedStringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
44 static GtkWidget
* ratingCriteriaCreateWidget (gboolean
*constrain
);
45 static void ratingCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
46 static void ratingCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
47 static GtkWidget
* integerCriteriaCreateWidget (gboolean
*constrain
);
48 static void integerCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
49 static void integerCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
50 static GtkWidget
* yearCriteriaCreateWidget (gboolean
*constrain
);
51 static void yearCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
52 static void yearCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
53 static GtkWidget
* durationCriteriaCreateWidget (gboolean
*constrain
);
54 static void durationCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
55 static void durationCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
56 static GtkWidget
* relativeTimeCriteriaCreateWidget (gboolean
*constrain
);
57 static void relativeTimeCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
);
58 static void relativeTimeCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
);
62 * This table is the list of properties that are displayed in the query-creator
64 const RBQueryCreatorPropertyOption property_options
[] =
66 { N_("Title"), RHYTHMDB_PROP_TITLE
, RHYTHMDB_PROP_TITLE_FOLDED
, &string_property_type
},
67 { N_("Artist"), RHYTHMDB_PROP_ARTIST
, RHYTHMDB_PROP_ARTIST_FOLDED
, &string_property_type
},
68 { N_("Album"), RHYTHMDB_PROP_ALBUM
, RHYTHMDB_PROP_ALBUM_FOLDED
, &string_property_type
},
69 { N_("Genre"), RHYTHMDB_PROP_GENRE
, RHYTHMDB_PROP_GENRE_FOLDED
, &string_property_type
},
70 { N_("Year"), RHYTHMDB_PROP_DATE
, RHYTHMDB_PROP_DATE
, &year_property_type
},
71 { N_("Rating"), RHYTHMDB_PROP_RATING
, RHYTHMDB_PROP_RATING
, &rating_property_type
},
72 { N_("Path"), RHYTHMDB_PROP_LOCATION
, RHYTHMDB_PROP_LOCATION
, &escaped_string_property_type
},
74 { N_("Play Count"), RHYTHMDB_PROP_PLAY_COUNT
, RHYTHMDB_PROP_PLAY_COUNT
, &integer_property_type
},
75 { N_("Track Number"), RHYTHMDB_PROP_TRACK_NUMBER
, RHYTHMDB_PROP_TRACK_NUMBER
, &integer_property_type
},
76 { N_("Disc Number"), RHYTHMDB_PROP_DISC_NUMBER
, RHYTHMDB_PROP_DISC_NUMBER
, &integer_property_type
},
77 { N_("Bitrate"), RHYTHMDB_PROP_BITRATE
, RHYTHMDB_PROP_BITRATE
, &integer_property_type
},
79 { N_("Duration"), RHYTHMDB_PROP_DURATION
, RHYTHMDB_PROP_DURATION
, &duration_property_type
},
81 { N_("Time of Last Play"), RHYTHMDB_PROP_LAST_PLAYED
, RHYTHMDB_PROP_LAST_PLAYED
, &relative_time_property_type
},
82 { N_("Time Added to Library"), RHYTHMDB_PROP_FIRST_SEEN
, RHYTHMDB_PROP_FIRST_SEEN
, &relative_time_property_type
},
85 const int num_property_options
= G_N_ELEMENTS (property_options
);
89 * This table describes which properties can be used for sorting a playlist
90 * All entries MUST have column keys column keys listed in rb-entry-view.c
92 const RBQueryCreatorSortOption sort_options
[] =
94 { N_("Artist"), "Artist", N_("_In reverse alphabetical order") },
95 { N_("Album"), "Album", N_("_In reverse alphabetical order") },
96 { N_("Genre"), "Genre", N_("_In reverse alphabetical order") },
97 { N_("Title"), "Title", N_("_In reverse alphabetical order") },
98 { N_("Rating"), "Rating", N_("W_ith more highly rated tracks first") },
99 { N_("Play Count"), "PlayCount", N_("W_ith more often played songs first") },
100 { N_("Year"), "Year", N_("W_ith newer tracks first") },
101 { N_("Duration"), "Time", N_("W_ith longer tracks first") },
102 { N_("Track Number"), "Track", N_("_In decreasing order")},
103 { N_("Last Played"), "LastPlayed", N_("W_ith more recently played tracks first") },
104 { N_("Date Added"), "FirstSeen", N_("W_ith more recently added tracks first") },
107 const int num_sort_options
= G_N_ELEMENTS (sort_options
);
108 const int DEFAULT_SORTING_COLUMN
= 0;
109 const gint DEFAULT_SORTING_ORDER
= GTK_SORT_ASCENDING
;
113 * This is the property type for string properties
116 const RBQueryCreatorCriteriaOption string_criteria_options
[] =
118 { N_("contains"), 0, RHYTHMDB_QUERY_PROP_LIKE
},
119 { N_("does not contain"), 0, RHYTHMDB_QUERY_PROP_NOT_LIKE
},
120 { N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS
},
121 { N_("starts with"), 0, RHYTHMDB_QUERY_PROP_PREFIX
},
122 { N_("ends with"), 0, RHYTHMDB_QUERY_PROP_SUFFIX
},
125 const RBQueryCreatorPropertyType string_property_type
=
127 G_N_ELEMENTS (string_criteria_options
),
128 string_criteria_options
,
129 stringCriteriaCreateWidget
,
130 stringCriteriaSetWidgetData
,
131 stringCriteriaGetWidgetData
134 const RBQueryCreatorPropertyType escaped_string_property_type
=
136 G_N_ELEMENTS (string_criteria_options
),
137 string_criteria_options
,
138 stringCriteriaCreateWidget
,
139 escapedStringCriteriaSetWidgetData
,
140 escapedStringCriteriaGetWidgetData
145 * This are the property types for numeric quantities, such as rating and playcounts
148 const RBQueryCreatorCriteriaOption numeric_criteria_options
[] =
150 { N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS
},
151 { N_("at least"), 1, RHYTHMDB_QUERY_PROP_GREATER
}, /* matches if A >= B */
152 { N_("at most"), 1, RHYTHMDB_QUERY_PROP_LESS
} /* matches if A <= B */
156 * Property type for date quantities
159 const RBQueryCreatorCriteriaOption year_criteria_options
[] =
161 { N_("in"), 1, RHYTHMDB_QUERY_PROP_YEAR_EQUALS
},
162 /* matches if within 1-JAN-YEAR to 31-DEC-YEAR */
163 { N_("after"), 1, RHYTHMDB_QUERY_PROP_YEAR_GREATER
},
164 /* matches if >= 31-DEC-YEAR */
165 { N_("before"), 1, RHYTHMDB_QUERY_PROP_YEAR_LESS
}
166 /* matches if < 1-DEC-YEAR */
169 const RBQueryCreatorPropertyType rating_property_type
=
171 G_N_ELEMENTS (numeric_criteria_options
),
172 numeric_criteria_options
,
173 ratingCriteriaCreateWidget
,
174 ratingCriteriaSetWidgetData
,
175 ratingCriteriaGetWidgetData
178 const RBQueryCreatorPropertyType integer_property_type
=
180 G_N_ELEMENTS (numeric_criteria_options
),
181 numeric_criteria_options
,
182 integerCriteriaCreateWidget
,
183 integerCriteriaSetWidgetData
,
184 integerCriteriaGetWidgetData
187 const RBQueryCreatorPropertyType year_property_type
=
189 G_N_ELEMENTS (year_criteria_options
),
190 year_criteria_options
,
191 yearCriteriaCreateWidget
,
192 yearCriteriaSetWidgetData
,
193 yearCriteriaGetWidgetData
196 const RBQueryCreatorPropertyType duration_property_type
=
198 G_N_ELEMENTS (numeric_criteria_options
),
199 numeric_criteria_options
,
200 durationCriteriaCreateWidget
,
201 durationCriteriaSetWidgetData
,
202 durationCriteriaGetWidgetData
207 * This is the property type for relative time properties, such as last played and first seen
213 gulong timeMultiplier
;
214 } RBQueryCreatorTimeUnitOption
;
216 const RBQueryCreatorCriteriaOption relative_time_criteria_options
[] =
219 * Translators: this will match when within <value> of the current time
220 * e.g. "in the last" "7 days" will match if within 7 days of the current time
222 { N_("in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
},
225 * Translators: this is the opposite of the above, and will match if not
226 * within <value> of the current time
228 { N_("not in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
}
231 const RBQueryCreatorPropertyType relative_time_property_type
=
233 G_N_ELEMENTS (relative_time_criteria_options
),
234 relative_time_criteria_options
,
235 relativeTimeCriteriaCreateWidget
,
236 relativeTimeCriteriaSetWidgetData
,
237 relativeTimeCriteriaGetWidgetData
240 const RBQueryCreatorTimeUnitOption time_unit_options
[] =
242 { N_("seconds"), 1 },
243 { N_("minutes"), 60 },
244 { N_("hours"), 60 * 60 },
245 { N_("days"), 60 * 60 * 24 },
246 { N_("weeks"), 60 * 60 * 24 * 7 }
249 const int time_unit_options_default
= 4; /* days */
253 * Implementation for the string properties, using a single GtkEntry.
257 stringCriteriaCreateWidget (gboolean
*constrain
)
259 return gtk_entry_new ();
263 stringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
265 gtk_entry_set_text (GTK_ENTRY (widget
), g_value_get_string (val
));
269 stringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
271 const char* text
= gtk_entry_get_text (GTK_ENTRY (widget
));
273 g_value_init (val
, G_TYPE_STRING
);
274 g_value_set_string (val
, text
);
277 /* escaped string operations, for use with URIs, etc */
280 escapedStringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
282 char *text
= gnome_vfs_unescape_string (g_value_get_string (val
), NULL
);
283 gtk_entry_set_text (GTK_ENTRY (widget
), text
);
288 escapedStringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
290 char *text
= gnome_vfs_escape_host_and_path_string (gtk_entry_get_text (GTK_ENTRY (widget
)));
292 g_value_init (val
, G_TYPE_STRING
);
293 g_value_set_string (val
, text
);
298 * Implementation for the ratings property, using the RbRating widget
302 set_rating_score (RBRating
*rating
, gdouble score
)
304 g_object_set (G_OBJECT (rating
), "rating", score
, NULL
);
308 ratingCriteriaCreateWidget (gboolean
*constrain
)
310 RBRating
*rating
= rb_rating_new ();
311 g_signal_connect_object (G_OBJECT (rating
), "rated",
312 G_CALLBACK (set_rating_score
), NULL
, 0);
314 return GTK_WIDGET (rating
);
318 ratingCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
320 g_object_set (G_OBJECT (widget
), "rating", g_value_get_double (val
), NULL
);
324 ratingCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
327 g_object_get (G_OBJECT (widget
), "rating", &rating
, NULL
);
329 g_value_init (val
, G_TYPE_DOUBLE
);
330 g_value_set_double (val
, rating
);
335 * Implementation for the integer properties, using a single GtkSpinButton.
339 integerCriteriaCreateWidget (gboolean
*constrain
)
341 return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT
, 1.0);
345 integerCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
347 gulong num
= g_value_get_ulong (val
);
348 g_assert (num
<= G_MAXINT
);
350 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget
), (gint
)num
);
354 integerCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
356 gint num
= gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget
));
359 g_value_init (val
, G_TYPE_ULONG
);
360 g_value_set_ulong (val
, (gulong
)num
);
363 /* Implementation for Year properties, using a single GtkSpinButton. */
366 yearCriteriaCreateWidget (gboolean
*constrain
)
368 return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT
, 1.0);
372 yearCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
375 gulong num
= g_value_get_ulong (val
);
376 g_assert (num
<= G_MAXINT
);
378 /* Create a date structure to get year from */
380 g_date_set_julian (date
, num
);
382 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget
), (gint
)g_date_get_year(date
));
387 yearCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
390 gint num
= gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget
));
393 /* New date structure, use year set in widget */
394 date
= g_date_new_dmy (1, G_DATE_JANUARY
, num
);
396 g_value_init (val
, G_TYPE_ULONG
);
397 g_value_set_ulong (val
, (gulong
) g_date_get_julian (date
) );
404 * Implementation for the duration property, using two single GtkSpinButtons.
408 durationCriteriaCreateWidget (gboolean
*constrain
)
411 GtkWidget
*minutesSpin
;
412 GtkWidget
*minutesLabel
;
413 GtkWidget
*secondsSpin
;
415 /* the widget for Duration is set out like the following [ 2] : [30] */
416 box
= GTK_BOX (gtk_hbox_new (FALSE
, 3));
418 minutesSpin
= gtk_spin_button_new_with_range (0.0, G_MAXINT
, 1.0);
419 gtk_box_pack_start (box
, minutesSpin
, FALSE
, FALSE
, 0);
421 minutesLabel
= gtk_label_new (":");
422 gtk_box_pack_start (box
, minutesLabel
, FALSE
, FALSE
, 0);
424 secondsSpin
= gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
425 gtk_box_pack_start (box
, secondsSpin
, FALSE
, FALSE
, 0);
427 gtk_widget_show_all (GTK_WIDGET (box
));
428 return GTK_WIDGET (box
);
432 durationCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
434 GtkSpinButton
*minutesSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
435 GtkSpinButton
*secondsSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 2));
437 gtk_spin_button_set_value (minutesSpinner
, (gdouble
) (g_value_get_ulong (val
) / 60));
438 gtk_spin_button_set_value (secondsSpinner
, (gdouble
) (g_value_get_ulong (val
) % 60));
442 durationCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
445 GtkSpinButton
*minutesSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
446 GtkSpinButton
*secondsSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 2));
448 gint value
= gtk_spin_button_get_value_as_int (minutesSpinner
) * 60
449 + gtk_spin_button_get_value_as_int (secondsSpinner
);
450 g_assert (value
>= 0);
452 g_value_init (val
, G_TYPE_ULONG
);
453 g_value_set_ulong (val
, (gulong
) value
);
458 * Implementation for the relative time properties, using a spin button and a menu.
462 create_time_unit_option_menu (const RBQueryCreatorTimeUnitOption
*options
,
465 GtkWidget
*menu
= gtk_menu_new ();
466 GtkWidget
*option_menu
= gtk_option_menu_new ();
469 for (i
= 0; i
< length
; i
++) {
470 GtkWidget
*menu_item
= gtk_menu_item_new_with_label (_(options
[i
].name
));
471 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
474 gtk_widget_show_all (menu
);
475 gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu
), menu
);
481 relativeTimeCriteriaCreateWidget (gboolean
*constrain
)
486 GtkWidget
*timeOption
;
488 box
= GTK_BOX (gtk_hbox_new (FALSE
, 6));
490 timeSpin
= gtk_spin_button_new_with_range (1.0, G_MAXINT
, 1.0);
491 gtk_box_pack_start_defaults (box
, timeSpin
);
493 timeOption
= create_time_unit_option_menu (time_unit_options
, G_N_ELEMENTS (time_unit_options
));
494 gtk_option_menu_set_history(GTK_OPTION_MENU (timeOption
), time_unit_options_default
);
495 gtk_box_pack_start_defaults (box
, timeOption
);
497 gtk_widget_show_all (GTK_WIDGET (box
));
498 return GTK_WIDGET (box
);
502 relativeTimeCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
504 GtkBox
*box
= GTK_BOX (widget
);
506 GtkSpinButton
*timeSpin
= GTK_SPIN_BUTTON (get_box_widget_at_pos (box
, 0));
507 GtkOptionMenu
*unitMenu
= GTK_OPTION_MENU (get_box_widget_at_pos (box
, 1));
509 gulong time
= g_value_get_ulong (val
);
513 /* determine the best units to use for the given value */
514 for (i
= 0; i
< G_N_ELEMENTS(time_unit_options
); i
++) {
515 /* find out if the time is an even multiple of the unit */
516 if (time
% time_unit_options
[i
].timeMultiplier
== 0)
520 time
= time
/ time_unit_options
[unit
].timeMultiplier
;
521 g_assert (time
< G_MAXINT
);
522 /* set the time value and unit*/
523 gtk_option_menu_set_history(unitMenu
, unit
);
524 gtk_spin_button_set_value(timeSpin
, time
);
528 relativeTimeCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
530 GtkSpinButton
*timeSpin
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
531 GtkOptionMenu
*unitMenu
= GTK_OPTION_MENU (get_box_widget_at_pos (GTK_BOX (widget
), 1));
533 gulong timeMultiplier
= time_unit_options
[gtk_option_menu_get_history (unitMenu
)].timeMultiplier
;
534 gint value
= gtk_spin_button_get_value_as_int (timeSpin
) * timeMultiplier
;
535 g_assert (value
>= 0);
537 g_value_init (val
, G_TYPE_ULONG
);
538 g_value_set_ulong (val
, (gulong
) value
);