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
);
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
);
87 * This table describes which properties can be used for sorting a playlist
88 * All entries MUST have column keys column keys listed in rb-entry-view.c
90 const RBQueryCreatorSortOption sort_options
[] =
92 { N_("Artist"), "Artist", N_("_In reverse alphabetical order") },
93 { N_("Album"), "Album", N_("_In reverse alphabetical order") },
94 { N_("Genre"), "Genre", N_("_In reverse alphabetical order") },
95 { N_("Title"), "Title", N_("_In reverse alphabetical order") },
96 { N_("Rating"), "Rating", N_("W_ith more highly rated tracks first") },
97 { N_("Play Count"), "PlayCount", N_("W_ith more often played songs first") },
98 { N_("Year"), "Year", N_("W_ith newer tracks first") },
99 { N_("Duration"), "Time", N_("W_ith longer tracks first") },
100 { N_("Track Number"), "Track", N_("_In decreasing order")},
101 { N_("Last Played"), "LastPlayed", N_("W_ith more recently played tracks first") },
102 { N_("Date Added"), "FirstSeen", N_("W_ith more recently added tracks first") },
105 const int num_sort_options
= G_N_ELEMENTS (sort_options
);
106 const int DEFAULT_SORTING_COLUMN
= 0;
107 const gint DEFAULT_SORTING_ORDER
= GTK_SORT_ASCENDING
;
110 * This is the property type for string properties
113 const RBQueryCreatorCriteriaOption string_criteria_options
[] =
115 { N_("contains"), 0, RHYTHMDB_QUERY_PROP_LIKE
},
116 { N_("does not contain"), 0, RHYTHMDB_QUERY_PROP_NOT_LIKE
},
117 { N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS
},
118 { N_("starts with"), 0, RHYTHMDB_QUERY_PROP_PREFIX
},
119 { N_("ends with"), 0, RHYTHMDB_QUERY_PROP_SUFFIX
},
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
141 * This are the property types for numeric quantities, such as rating and playcounts
144 const RBQueryCreatorCriteriaOption numeric_criteria_options
[] =
146 { N_("equals"), 1, RHYTHMDB_QUERY_PROP_EQUALS
},
147 { N_("at least"), 1, RHYTHMDB_QUERY_PROP_GREATER
}, /* matches if A >= B */
148 { N_("at most"), 1, RHYTHMDB_QUERY_PROP_LESS
} /* matches if A <= B */
152 * Property type for date quantities
155 const RBQueryCreatorCriteriaOption year_criteria_options
[] =
157 { N_("in"), 1, RHYTHMDB_QUERY_PROP_YEAR_EQUALS
},
158 /* matches if within 1-JAN-YEAR to 31-DEC-YEAR */
159 { N_("after"), 1, RHYTHMDB_QUERY_PROP_YEAR_GREATER
},
160 /* matches if >= 31-DEC-YEAR */
161 { N_("before"), 1, RHYTHMDB_QUERY_PROP_YEAR_LESS
}
162 /* matches if < 1-DEC-YEAR */
165 const RBQueryCreatorPropertyType rating_property_type
=
167 G_N_ELEMENTS (numeric_criteria_options
),
168 numeric_criteria_options
,
169 ratingCriteriaCreateWidget
,
170 ratingCriteriaSetWidgetData
,
171 ratingCriteriaGetWidgetData
174 const RBQueryCreatorPropertyType integer_property_type
=
176 G_N_ELEMENTS (numeric_criteria_options
),
177 numeric_criteria_options
,
178 integerCriteriaCreateWidget
,
179 integerCriteriaSetWidgetData
,
180 integerCriteriaGetWidgetData
183 const RBQueryCreatorPropertyType year_property_type
=
185 G_N_ELEMENTS (year_criteria_options
),
186 year_criteria_options
,
187 yearCriteriaCreateWidget
,
188 yearCriteriaSetWidgetData
,
189 yearCriteriaGetWidgetData
192 const RBQueryCreatorPropertyType duration_property_type
=
194 G_N_ELEMENTS (numeric_criteria_options
),
195 numeric_criteria_options
,
196 durationCriteriaCreateWidget
,
197 durationCriteriaSetWidgetData
,
198 durationCriteriaGetWidgetData
202 * This is the property type for relative time properties, such as last played and first seen
208 gulong timeMultiplier
;
209 } RBQueryCreatorTimeUnitOption
;
211 const RBQueryCreatorCriteriaOption relative_time_criteria_options
[] =
214 * Translators: this will match when within <value> of the current time
215 * e.g. "in the last" "7 days" will match if within 7 days of the current time
217 { N_("in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
},
220 * Translators: this is the opposite of the above, and will match if not
221 * within <value> of the current time
223 { N_("not in the last"), 1, RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
}
226 const RBQueryCreatorPropertyType relative_time_property_type
=
228 G_N_ELEMENTS (relative_time_criteria_options
),
229 relative_time_criteria_options
,
230 relativeTimeCriteriaCreateWidget
,
231 relativeTimeCriteriaSetWidgetData
,
232 relativeTimeCriteriaGetWidgetData
235 const RBQueryCreatorTimeUnitOption time_unit_options
[] =
237 { N_("seconds"), 1 },
238 { N_("minutes"), 60 },
239 { N_("hours"), 60 * 60 },
240 { N_("days"), 60 * 60 * 24 },
241 { N_("weeks"), 60 * 60 * 24 * 7 }
244 const int time_unit_options_default
= 4; /* days */
247 * Implementation for the string properties, using a single GtkEntry.
251 stringCriteriaCreateWidget (gboolean
*constrain
)
253 return gtk_entry_new ();
257 stringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
259 gtk_entry_set_text (GTK_ENTRY (widget
), g_value_get_string (val
));
263 stringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
265 const char* text
= gtk_entry_get_text (GTK_ENTRY (widget
));
267 g_value_init (val
, G_TYPE_STRING
);
268 g_value_set_string (val
, text
);
271 /* escaped string operations, for use with URIs, etc */
274 escapedStringCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
276 char *text
= gnome_vfs_unescape_string (g_value_get_string (val
), NULL
);
277 gtk_entry_set_text (GTK_ENTRY (widget
), text
);
282 escapedStringCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
284 char *text
= gnome_vfs_escape_host_and_path_string (gtk_entry_get_text (GTK_ENTRY (widget
)));
286 g_value_init (val
, G_TYPE_STRING
);
287 g_value_set_string (val
, text
);
291 * Implementation for the ratings property, using the RbRating widget
295 set_rating_score (RBRating
*rating
, gdouble score
)
297 g_object_set (G_OBJECT (rating
), "rating", score
, NULL
);
301 ratingCriteriaCreateWidget (gboolean
*constrain
)
303 RBRating
*rating
= rb_rating_new ();
304 g_signal_connect_object (G_OBJECT (rating
), "rated",
305 G_CALLBACK (set_rating_score
), NULL
, 0);
307 return GTK_WIDGET (rating
);
311 ratingCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
313 g_object_set (G_OBJECT (widget
), "rating", g_value_get_double (val
), NULL
);
317 ratingCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
320 g_object_get (G_OBJECT (widget
), "rating", &rating
, NULL
);
322 g_value_init (val
, G_TYPE_DOUBLE
);
323 g_value_set_double (val
, rating
);
327 * Implementation for the integer properties, using a single GtkSpinButton.
331 integerCriteriaCreateWidget (gboolean
*constrain
)
333 return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT
, 1.0);
337 integerCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
339 gulong num
= g_value_get_ulong (val
);
340 g_assert (num
<= G_MAXINT
);
342 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget
), (gint
)num
);
346 integerCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
348 gint num
= gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget
));
351 g_value_init (val
, G_TYPE_ULONG
);
352 g_value_set_ulong (val
, (gulong
)num
);
355 /* Implementation for Year properties, using a single GtkSpinButton. */
358 yearCriteriaCreateWidget (gboolean
*constrain
)
360 return gtk_spin_button_new_with_range (0.0, (double)G_MAXINT
, 1.0);
364 yearCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
367 gulong num
= g_value_get_ulong (val
);
369 g_assert (num
<= G_MAXINT
);
372 /* Create a date structure to get year from */
374 g_date_set_julian (date
, num
);
375 display_year
= (gint
)g_date_get_year(date
);
380 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget
), display_year
);
384 yearCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
387 gint num
= gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget
));
388 guint32 display_date
;
391 g_value_init (val
, G_TYPE_ULONG
);
394 /* New date structure, use year set in widget */
395 date
= g_date_new_dmy (1, G_DATE_JANUARY
, num
);
396 display_date
= g_date_get_julian (date
);
401 g_value_set_ulong (val
, (gulong
)display_date
);
405 * Implementation for the duration property, using two single GtkSpinButtons.
409 durationCriteriaCreateWidget (gboolean
*constrain
)
412 GtkWidget
*minutesSpin
;
413 GtkWidget
*minutesLabel
;
414 GtkWidget
*secondsSpin
;
416 /* the widget for Duration is set out like the following [ 2] : [30] */
417 box
= GTK_BOX (gtk_hbox_new (FALSE
, 3));
419 minutesSpin
= gtk_spin_button_new_with_range (0.0, G_MAXINT
, 1.0);
420 gtk_box_pack_start (box
, minutesSpin
, FALSE
, FALSE
, 0);
422 minutesLabel
= gtk_label_new (":");
423 gtk_box_pack_start (box
, minutesLabel
, FALSE
, FALSE
, 0);
425 secondsSpin
= gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
426 gtk_box_pack_start (box
, secondsSpin
, FALSE
, FALSE
, 0);
428 gtk_widget_show_all (GTK_WIDGET (box
));
429 return GTK_WIDGET (box
);
433 durationCriteriaSetWidgetData (GtkWidget
*widget
, GValue
*val
)
435 GtkSpinButton
*minutesSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
436 GtkSpinButton
*secondsSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 2));
438 gtk_spin_button_set_value (minutesSpinner
, (gdouble
) (g_value_get_ulong (val
) / 60));
439 gtk_spin_button_set_value (secondsSpinner
, (gdouble
) (g_value_get_ulong (val
) % 60));
443 durationCriteriaGetWidgetData (GtkWidget
*widget
, GValue
*val
)
446 GtkSpinButton
*minutesSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 0));
447 GtkSpinButton
*secondsSpinner
= GTK_SPIN_BUTTON (get_box_widget_at_pos (GTK_BOX (widget
), 2));
449 gint value
= gtk_spin_button_get_value_as_int (minutesSpinner
) * 60
450 + gtk_spin_button_get_value_as_int (secondsSpinner
);
451 g_assert (value
>= 0);
453 g_value_init (val
, G_TYPE_ULONG
);
454 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
);