udapted vi.po
[rhythmbox.git] / widgets / rb-song-info.c
blobeab133ae3c323b44c06be201c58180c26605ffb9
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of local song properties dialog
5 * Copyright (C) 2002 Olivier Martin <omartin@ifrance.com>
6 * Copyright (C) 2003 Colin Walters <walters@verbum.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
25 * Yes, this code is ugly.
28 #include "config.h"
30 #include <string.h>
31 #include <time.h>
32 #include <math.h>
34 #define EPSILON 0.0001
36 #include <glib/gi18n.h>
37 #include <gtk/gtk.h>
38 #include <glade/glade.h>
39 #include <libgnomevfs/gnome-vfs.h>
40 #include <libgnomevfs/gnome-vfs-utils.h>
42 #include "rhythmdb.h"
43 #include "rhythmdb-property-model.h"
44 #include "rb-song-info.h"
45 #include "rb-glade-helpers.h"
46 #include "rb-dialog.h"
47 #include "rb-rating.h"
48 #include "rb-preferences.h"
49 #include "eel-gconf-extensions.h"
50 #include "rb-source.h"
51 #include "rb-shell.h"
53 static void rb_song_info_class_init (RBSongInfoClass *klass);
54 static void rb_song_info_init (RBSongInfo *song_info);
55 static GObject *rb_song_info_constructor (GType type, guint n_construct_properties,
56 GObjectConstructParam *construct_properties);
58 static void rb_song_info_show (GtkWidget *widget);
59 static void rb_song_info_finalize (GObject *object);
60 static void rb_song_info_set_property (GObject *object,
61 guint prop_id,
62 const GValue *value,
63 GParamSpec *pspec);
64 static void rb_song_info_get_property (GObject *object,
65 guint prop_id,
66 GValue *value,
67 GParamSpec *pspec);
68 static void rb_song_info_response_cb (GtkDialog *dialog,
69 int response_id,
70 RBSongInfo *song_info);
71 static void rb_song_info_populate_dialog (RBSongInfo *song_info);
72 static void rb_song_info_populate_dialog_multiple (RBSongInfo *song_info);
73 static void rb_song_info_update_duration (RBSongInfo *song_info);
74 static void rb_song_info_update_location (RBSongInfo *song_info);
75 static void rb_song_info_update_filesize (RBSongInfo *song_info);
76 static void rb_song_info_update_play_count (RBSongInfo *song_info);
77 static void rb_song_info_update_last_played (RBSongInfo *song_info);
78 static void rb_song_info_update_bitrate (RBSongInfo *song_info);
79 static void rb_song_info_update_buttons (RBSongInfo *song_info);
80 static void rb_song_info_update_rating (RBSongInfo *song_info);
81 static void rb_song_info_update_year (RBSongInfo *song_info);
82 static void rb_song_info_update_playback_error (RBSongInfo *song_info);
84 static void rb_song_info_backward_clicked_cb (GtkWidget *button,
85 RBSongInfo *song_info);
86 static void rb_song_info_forward_clicked_cb (GtkWidget *button,
87 RBSongInfo *song_info);
88 static void rb_song_info_query_model_changed_cb (GObject *source,
89 GParamSpec *pspec,
90 RBSongInfo *song_info);
91 static void rb_song_info_rated_cb (RBRating *rating,
92 double score,
93 RBSongInfo *song_info);
94 static void rb_song_info_mnemonic_cb (GtkWidget *target);
95 static void rb_song_info_sync_entries (RBSongInfo *dialog);
97 struct RBSongInfoPrivate
99 RhythmDB *db;
100 RBSource *source;
101 RBEntryView *entry_view;
102 RhythmDBQueryModel *query_model;
104 /* information on the displayed song */
105 RhythmDBEntry *current_entry;
106 GList *selected_entries;
108 gboolean editable;
110 /* the dialog widgets */
111 GtkWidget *backward;
112 GtkWidget *forward;
113 GtkWidget *notebook;
115 GtkWidget *title;
116 GtkWidget *artist;
117 GtkWidget *album;
118 GtkWidget *genre;
119 GtkWidget *track_cur;
120 GtkWidget *disc_cur;
121 GtkWidget *year;
122 GtkWidget *playback_error_box;
123 GtkWidget *playback_error_label;
125 GtkWidget *bitrate;
126 GtkWidget *duration;
127 GtkWidget *name;
128 GtkWidget *location;
129 GtkWidget *filesize;
130 GtkWidget *play_count;
131 GtkWidget *last_played;
132 GtkWidget *rating;
134 RhythmDBPropertyModel* albums;
135 RhythmDBPropertyModel* artists;
136 RhythmDBPropertyModel* genres;
139 #define RB_SONG_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SONG_INFO, RBSongInfoPrivate))
141 enum
143 PRE_METADATA_CHANGE,
144 POST_METADATA_CHANGE,
145 LAST_SIGNAL
148 enum
150 PROP_0,
151 PROP_SOURCE,
152 PROP_ENTRY_VIEW,
153 PROP_CURRENT_ENTRY
156 static guint rb_song_info_signals[LAST_SIGNAL] = { 0 };
158 G_DEFINE_TYPE (RBSongInfo, rb_song_info, GTK_TYPE_DIALOG)
160 static void
161 rb_song_info_class_init (RBSongInfoClass *klass)
163 GObjectClass *object_class = G_OBJECT_CLASS (klass);
164 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
166 object_class->set_property = rb_song_info_set_property;
167 object_class->get_property = rb_song_info_get_property;
168 object_class->constructor = rb_song_info_constructor;
170 widget_class->show = rb_song_info_show;
172 g_object_class_install_property (object_class,
173 PROP_SOURCE,
174 g_param_spec_object ("source",
175 "RBSource",
176 "RBSource object",
177 RB_TYPE_SOURCE,
178 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
179 g_object_class_install_property (object_class,
180 PROP_ENTRY_VIEW,
181 g_param_spec_object ("entry-view",
182 "RBEntryView",
183 "RBEntryView object",
184 RB_TYPE_ENTRY_VIEW,
185 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
186 g_object_class_install_property (object_class,
187 PROP_CURRENT_ENTRY,
188 g_param_spec_boxed ("current-entry",
189 "RhythmDBEntry",
190 "RhythmDBEntry object",
191 RHYTHMDB_TYPE_ENTRY,
192 G_PARAM_READABLE));
194 object_class->finalize = rb_song_info_finalize;
196 rb_song_info_signals[PRE_METADATA_CHANGE] =
197 g_signal_new ("pre-metadata-change",
198 G_OBJECT_CLASS_TYPE (object_class),
199 G_SIGNAL_RUN_LAST,
200 G_STRUCT_OFFSET (RBSongInfoClass, pre_metadata_change),
201 NULL, NULL,
202 g_cclosure_marshal_VOID__BOXED,
203 G_TYPE_NONE,
205 RHYTHMDB_TYPE_ENTRY);
207 rb_song_info_signals[POST_METADATA_CHANGE] =
208 g_signal_new ("post-metadata-change",
209 G_OBJECT_CLASS_TYPE (object_class),
210 G_SIGNAL_RUN_LAST,
211 G_STRUCT_OFFSET (RBSongInfoClass, post_metadata_change),
212 NULL, NULL,
213 g_cclosure_marshal_VOID__BOXED,
214 G_TYPE_NONE,
216 RHYTHMDB_TYPE_ENTRY);
218 g_type_class_add_private (klass, sizeof (RBSongInfoPrivate));
221 static void
222 rb_song_info_init (RBSongInfo *song_info)
224 /* create the dialog and some buttons backward - forward - close */
225 song_info->priv = RB_SONG_INFO_GET_PRIVATE (song_info);
227 g_signal_connect_object (G_OBJECT (song_info),
228 "response",
229 G_CALLBACK (rb_song_info_response_cb),
230 song_info, 0);
232 gtk_dialog_set_has_separator (GTK_DIALOG (song_info), FALSE);
234 gtk_container_set_border_width (GTK_CONTAINER (song_info), 5);
235 gtk_window_set_resizable (GTK_WINDOW (song_info), TRUE);
236 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (song_info)->vbox), 2);
239 static void
240 rb_song_info_show (GtkWidget *widget)
242 if (GTK_WIDGET_CLASS (rb_song_info_parent_class)->show)
243 GTK_WIDGET_CLASS (rb_song_info_parent_class)->show (widget);
245 rb_song_info_update_playback_error (RB_SONG_INFO (widget));
248 static void
249 rb_song_info_construct_single (RBSongInfo *song_info, GladeXML *xml,
250 gboolean editable)
252 song_info->priv->backward = gtk_dialog_add_button (GTK_DIALOG (song_info),
253 GTK_STOCK_GO_BACK,
254 GTK_RESPONSE_NONE);
256 g_signal_connect_object (G_OBJECT (song_info->priv->backward),
257 "clicked",
258 G_CALLBACK (rb_song_info_backward_clicked_cb),
259 song_info, 0);
261 song_info->priv->forward = gtk_dialog_add_button (GTK_DIALOG (song_info),
262 GTK_STOCK_GO_FORWARD,
263 GTK_RESPONSE_NONE);
265 g_signal_connect_object (G_OBJECT (song_info->priv->forward),
266 "clicked",
267 G_CALLBACK (rb_song_info_forward_clicked_cb),
268 song_info, 0);
270 gtk_window_set_title (GTK_WINDOW (song_info), _("Song Properties"));
272 /* get the widgets from the XML */
273 song_info->priv->notebook = glade_xml_get_widget (xml, "song_info_vbox");
274 song_info->priv->title = glade_xml_get_widget (xml, "song_info_title");
275 song_info->priv->track_cur = glade_xml_get_widget (xml, "song_info_track_cur");
276 song_info->priv->bitrate = glade_xml_get_widget (xml, "song_info_bitrate");
277 song_info->priv->duration = glade_xml_get_widget (xml, "song_info_duration");
278 song_info->priv->location = glade_xml_get_widget (xml, "song_info_location");
279 song_info->priv->filesize = glade_xml_get_widget (xml, "song_info_filesize");
280 song_info->priv->play_count = glade_xml_get_widget (xml, "song_info_playcount");
281 song_info->priv->last_played = glade_xml_get_widget (xml, "song_info_lastplayed");
282 song_info->priv->name = glade_xml_get_widget (xml, "song_info_name");
284 rb_glade_boldify_label (xml, "title_label");
285 rb_glade_boldify_label (xml, "trackn_label");
286 rb_glade_boldify_label (xml, "name_label");
287 rb_glade_boldify_label (xml, "location_label");
288 rb_glade_boldify_label (xml, "filesize_label");
289 rb_glade_boldify_label (xml, "last_played_label");
290 rb_glade_boldify_label (xml, "play_count_label");
291 rb_glade_boldify_label (xml, "duration_label");
292 rb_glade_boldify_label (xml, "bitrate_label");
294 /* whenever you press a mnemonic, the associated GtkEntry's text gets highlighted */
295 g_signal_connect_object (G_OBJECT (song_info->priv->title),
296 "mnemonic-activate",
297 G_CALLBACK (rb_song_info_mnemonic_cb),
298 NULL, 0);
299 g_signal_connect_object (G_OBJECT (song_info->priv->track_cur),
300 "mnemonic-activate",
301 G_CALLBACK (rb_song_info_mnemonic_cb),
302 NULL, 0);
304 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->title), editable);
305 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->track_cur), editable);
307 /* default focus */
308 gtk_widget_grab_focus (song_info->priv->title);
312 static void
313 rb_song_info_construct_multiple (RBSongInfo *song_info, GladeXML *xml,
314 gboolean editable)
316 gtk_window_set_title (GTK_WINDOW (song_info),
317 _("Multiple Song Properties"));
318 gtk_widget_grab_focus (song_info->priv->artist);
320 song_info->priv->notebook = glade_xml_get_widget (xml, "song_info_notebook");
323 static void
324 rb_song_info_add_completion (GtkEntry *entry, RhythmDBPropertyModel *propmodel)
326 GtkEntryCompletion* completion;
328 completion = gtk_entry_completion_new();
329 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (propmodel));
330 gtk_entry_completion_set_text_column (completion, RHYTHMDB_PROPERTY_MODEL_COLUMN_TITLE);
331 gtk_entry_set_completion (entry, completion);
332 g_object_unref (completion);
335 static GObject *
336 rb_song_info_constructor (GType type, guint n_construct_properties,
337 GObjectConstructParam *construct_properties)
339 RBSongInfo *song_info;
340 RBSongInfoClass *klass;
341 GladeXML *xml;
342 GList *selected_entries;
343 GList *tem;
344 gboolean editable = TRUE;
345 RBShell *shell;
347 klass = RB_SONG_INFO_CLASS (g_type_class_peek (RB_TYPE_SONG_INFO));
349 song_info = RB_SONG_INFO (G_OBJECT_CLASS (rb_song_info_parent_class)
350 ->constructor (type, n_construct_properties, construct_properties));
352 selected_entries = rb_entry_view_get_selected_entries (song_info->priv->entry_view);
354 g_return_val_if_fail (selected_entries != NULL, NULL);
356 for (tem = selected_entries; tem; tem = tem->next) {
357 if (!rhythmdb_entry_is_editable (song_info->priv->db,
358 selected_entries->data)) {
359 editable = FALSE;
360 break;
364 song_info->priv->editable = editable;
366 if (selected_entries->next == NULL) {
367 song_info->priv->current_entry = selected_entries->data;
368 song_info->priv->selected_entries = NULL;
370 g_list_foreach (selected_entries, (GFunc)rhythmdb_entry_unref, NULL);
371 g_list_free (selected_entries);
372 } else {
373 song_info->priv->current_entry = NULL;
374 song_info->priv->selected_entries = selected_entries;
377 if (song_info->priv->current_entry) {
378 xml = rb_glade_xml_new ("song-info.glade",
379 "song_info_vbox",
380 song_info);
381 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (song_info)->vbox),
382 glade_xml_get_widget (xml, "song_info_vbox"));
383 } else {
384 xml = rb_glade_xml_new ("song-info-multiple.glade",
385 "song_info_notebook",
386 song_info);
387 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (song_info)->vbox),
388 glade_xml_get_widget (xml, "song_info_notebook"));
391 glade_xml_signal_autoconnect (xml);
393 song_info->priv->artist = glade_xml_get_widget (xml, "song_info_artist");
394 song_info->priv->album = glade_xml_get_widget (xml, "song_info_album");
395 song_info->priv->genre = glade_xml_get_widget (xml, "song_info_genre");
396 song_info->priv->year = glade_xml_get_widget (xml, "song_info_year");
397 song_info->priv->playback_error_box = glade_xml_get_widget (xml, "song_info_error_box");
398 song_info->priv->playback_error_label = glade_xml_get_widget (xml, "song_info_error_label");
399 song_info->priv->disc_cur = glade_xml_get_widget (xml, "song_info_disc_cur");
401 rb_song_info_add_completion (GTK_ENTRY (song_info->priv->genre), song_info->priv->genres);
402 rb_song_info_add_completion (GTK_ENTRY (song_info->priv->artist), song_info->priv->artists);
403 rb_song_info_add_completion (GTK_ENTRY (song_info->priv->album), song_info->priv->albums);
405 rb_glade_boldify_label (xml, "album_label");
406 rb_glade_boldify_label (xml, "artist_label");
407 rb_glade_boldify_label (xml, "genre_label");
408 rb_glade_boldify_label (xml, "year_label");
409 rb_glade_boldify_label (xml, "rating_label");
410 rb_glade_boldify_label (xml, "discn_label");
412 g_signal_connect_object (G_OBJECT (song_info->priv->artist),
413 "mnemonic-activate",
414 G_CALLBACK (rb_song_info_mnemonic_cb),
415 NULL, 0);
416 g_signal_connect_object (G_OBJECT (song_info->priv->album),
417 "mnemonic-activate",
418 G_CALLBACK (rb_song_info_mnemonic_cb),
419 NULL, 0);
420 g_signal_connect_object (G_OBJECT (song_info->priv->genre),
421 "mnemonic-activate",
422 G_CALLBACK (rb_song_info_mnemonic_cb),
423 NULL, 0);
424 g_signal_connect_object (G_OBJECT (song_info->priv->year),
425 "mnemonic-activate",
426 G_CALLBACK (rb_song_info_mnemonic_cb),
427 NULL, 0);
428 g_signal_connect_object (G_OBJECT (song_info->priv->disc_cur),
429 "mnemonic-activate",
430 G_CALLBACK (rb_song_info_mnemonic_cb),
431 NULL, 0);
433 /* this widget has to be customly created */
434 song_info->priv->rating = GTK_WIDGET (rb_rating_new ());
435 g_signal_connect_object (song_info->priv->rating, "rated",
436 G_CALLBACK (rb_song_info_rated_cb),
437 G_OBJECT (song_info), 0);
438 gtk_container_add (GTK_CONTAINER (glade_xml_get_widget (xml, "song_info_rating_container")),
439 song_info->priv->rating);
441 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->artist), editable);
442 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->album), editable);
443 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->genre), editable);
444 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->year), editable);
445 gtk_editable_set_editable (GTK_EDITABLE (song_info->priv->disc_cur), editable);
447 /* Finish construction */
448 if (song_info->priv->current_entry) {
449 rb_song_info_construct_single (song_info, xml, editable);
450 rb_song_info_populate_dialog (song_info);
451 } else {
452 rb_song_info_construct_multiple (song_info, xml, editable);
453 rb_song_info_populate_dialog_multiple (song_info);
455 g_object_get (G_OBJECT (song_info->priv->source), "shell", &shell, NULL);
456 g_signal_emit_by_name (G_OBJECT (shell), "create_song_info", song_info, (song_info->priv->current_entry == NULL));
457 g_object_unref (G_OBJECT (shell));
459 gtk_dialog_add_button (GTK_DIALOG (song_info),
460 GTK_STOCK_CLOSE,
461 GTK_RESPONSE_CLOSE);
463 gtk_dialog_set_default_response (GTK_DIALOG (song_info),
464 GTK_RESPONSE_CLOSE);
466 g_object_unref (G_OBJECT (xml));
467 return G_OBJECT (song_info);
470 static void
471 rb_song_info_finalize (GObject *object)
473 RBSongInfo *song_info;
475 g_return_if_fail (object != NULL);
476 g_return_if_fail (RB_IS_SONG_INFO (object));
478 song_info = RB_SONG_INFO (object);
480 g_return_if_fail (song_info->priv != NULL);
482 g_signal_handlers_disconnect_by_func (song_info->priv->source,
483 G_CALLBACK (rb_song_info_query_model_changed_cb),
484 song_info);
486 g_object_unref (song_info->priv->albums);
487 g_object_unref (song_info->priv->artists);
488 g_object_unref (song_info->priv->genres);
490 g_object_unref (song_info->priv->db);
491 g_object_unref (song_info->priv->source);
492 g_object_unref (song_info->priv->query_model);
494 if (song_info->priv->selected_entries != NULL) {
495 g_list_foreach (song_info->priv->selected_entries, (GFunc)rhythmdb_entry_unref, NULL);
496 g_list_free (song_info->priv->selected_entries);
499 G_OBJECT_CLASS (rb_song_info_parent_class)->finalize (object);
502 static void
503 rb_song_info_set_source_internal (RBSongInfo *song_info,
504 RBSource *source)
506 RhythmDB *old_db = song_info->priv->db;
508 if (song_info->priv->source != NULL) {
509 g_signal_handlers_disconnect_by_func (song_info->priv->source,
510 rb_song_info_query_model_changed_cb,
511 song_info);
512 g_object_unref (song_info->priv->source);
513 g_object_unref (song_info->priv->query_model);
514 g_object_unref (song_info->priv->db);
517 song_info->priv->source = source;
519 g_object_ref (song_info->priv->source);
521 g_object_get (G_OBJECT (song_info->priv->source), "query-model", &song_info->priv->query_model, NULL);
523 g_signal_connect_object (G_OBJECT (song_info->priv->source),
524 "notify::query-model",
525 G_CALLBACK (rb_song_info_query_model_changed_cb),
526 song_info, 0);
528 g_object_get (G_OBJECT (song_info->priv->query_model), "db", &song_info->priv->db, NULL);
530 if (old_db != song_info->priv->db) {
531 if (song_info->priv->albums) {
532 g_object_unref (song_info->priv->albums);
534 if (song_info->priv->artists) {
535 g_object_unref (song_info->priv->artists);
537 if (song_info->priv->genres) {
538 g_object_unref (song_info->priv->genres);
541 song_info->priv->albums = rhythmdb_property_model_new (song_info->priv->db, RHYTHMDB_PROP_ALBUM);
542 song_info->priv->artists = rhythmdb_property_model_new (song_info->priv->db, RHYTHMDB_PROP_ARTIST);
543 song_info->priv->genres = rhythmdb_property_model_new (song_info->priv->db, RHYTHMDB_PROP_GENRE);
545 g_object_set (song_info->priv->albums, "query-model", song_info->priv->query_model, NULL);
546 g_object_set (song_info->priv->artists, "query-model", song_info->priv->query_model, NULL);
547 g_object_set (song_info->priv->genres, "query-model", song_info->priv->query_model, NULL);
549 if(song_info->priv->album) {
550 GtkEntryCompletion *comp = gtk_entry_get_completion (GTK_ENTRY (song_info->priv->album));
551 gtk_entry_completion_set_model (comp, GTK_TREE_MODEL (song_info->priv->albums));
554 if(song_info->priv->artist) {
555 GtkEntryCompletion *comp = gtk_entry_get_completion (GTK_ENTRY (song_info->priv->artist));
556 gtk_entry_completion_set_model (comp, GTK_TREE_MODEL (song_info->priv->artist));
559 if(song_info->priv->genre) {
560 GtkEntryCompletion *comp = gtk_entry_get_completion (GTK_ENTRY (song_info->priv->genre));
561 gtk_entry_completion_set_model (comp, GTK_TREE_MODEL (song_info->priv->genre));
566 static void
567 rb_song_info_set_property (GObject *object,
568 guint prop_id,
569 const GValue *value,
570 GParamSpec *pspec)
572 RBSongInfo *song_info = RB_SONG_INFO (object);
574 switch (prop_id) {
575 case PROP_SOURCE:
576 rb_song_info_set_source_internal (song_info, g_value_get_object (value));
577 break;
578 case PROP_ENTRY_VIEW:
579 song_info->priv->entry_view = g_value_get_object (value);
580 break;
581 default:
582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
583 break;
587 static void
588 rb_song_info_get_property (GObject *object,
589 guint prop_id,
590 GValue *value,
591 GParamSpec *pspec)
593 RBSongInfo *song_info = RB_SONG_INFO (object);
595 switch (prop_id) {
596 case PROP_SOURCE:
597 g_value_set_object (value, song_info->priv->source);
598 break;
599 case PROP_ENTRY_VIEW:
600 g_value_set_object (value, song_info->priv->entry_view);
601 break;
602 case PROP_CURRENT_ENTRY:
603 g_value_set_boxed (value, song_info->priv->current_entry);
604 break;
605 default:
606 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
607 break;
611 GtkWidget *
612 rb_song_info_new (RBSource *source, RBEntryView *entry_view)
614 RBSongInfo *song_info;
616 g_return_val_if_fail (RB_IS_SOURCE (source), NULL);
617 if (!entry_view)
618 entry_view = rb_source_get_entry_view (source);
620 if (rb_entry_view_have_selection (entry_view) == FALSE)
621 return NULL;
623 /* create the dialog */
624 song_info = g_object_new (RB_TYPE_SONG_INFO,
625 "source", source,
626 "entry-view", entry_view,
627 NULL);
629 g_return_val_if_fail (song_info->priv != NULL, NULL);
631 return GTK_WIDGET (song_info);
634 guint
635 rb_song_info_append_page (RBSongInfo *info, const char *title, GtkWidget *page)
637 GtkWidget *label;
638 guint page_num;
640 label = gtk_label_new (title);
641 page_num = gtk_notebook_append_page (GTK_NOTEBOOK (info->priv->notebook),
642 page,
643 label);
644 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (info->priv->notebook), TRUE);
646 return page_num;
649 typedef void (*RBSongInfoSelectionFunc)(RBSongInfo *info,
650 RhythmDBEntry *entry,
651 void *data);
653 static void
654 rb_song_info_selection_for_each (RBSongInfo *info, RBSongInfoSelectionFunc func,
655 void *data)
657 if (info->priv->current_entry)
658 func (info, info->priv->current_entry, data);
659 else {
660 GList *tem;
661 for (tem = info->priv->selected_entries; tem ; tem = tem->next)
662 func (info, tem->data, data);
666 static void
667 rb_song_info_response_cb (GtkDialog *dialog,
668 int response_id,
669 RBSongInfo *song_info)
671 if (response_id == GTK_RESPONSE_CLOSE) {
672 rb_song_info_sync_entries (RB_SONG_INFO (dialog));
673 gtk_widget_destroy (GTK_WIDGET (dialog));
677 static void
678 rb_song_info_set_entry_rating (RBSongInfo *info,
679 RhythmDBEntry *entry,
680 void *data)
682 GValue value = {0, };
683 double trouble = *((double*) data);
685 /* set the new value for the song */
686 g_value_init (&value, G_TYPE_DOUBLE);
687 g_value_set_double (&value, trouble);
688 rhythmdb_entry_set (info->priv->db, entry, RHYTHMDB_PROP_RATING, &value);
689 g_value_unset (&value);
692 static void
693 rb_song_info_rated_cb (RBRating *rating,
694 double score,
695 RBSongInfo *song_info)
697 g_return_if_fail (RB_IS_RATING (rating));
698 g_return_if_fail (RB_IS_SONG_INFO (song_info));
699 g_return_if_fail (score >= 0 && score <= 5 );
701 rb_song_info_selection_for_each (song_info,
702 rb_song_info_set_entry_rating,
703 &score);
704 rhythmdb_commit (song_info->priv->db);
706 g_object_set (G_OBJECT (song_info->priv->rating),
707 "rating", score,
708 NULL);
711 static void
712 rb_song_info_mnemonic_cb (GtkWidget *target)
714 g_return_if_fail (GTK_IS_EDITABLE (target) || GTK_IS_TEXT_VIEW (target));
716 gtk_widget_grab_focus (target);
718 if (GTK_IS_EDITABLE (target)) {
719 gtk_editable_select_region (GTK_EDITABLE (target), 0, -1);
720 } else { /* GtkTextViews need special treatment */
721 g_signal_emit_by_name (G_OBJECT (target), "select-all");
725 static void
726 rb_song_info_populate_num_field (GtkEntry *field, gulong num)
728 char *tmp;
729 if (num > 0)
730 tmp = g_strdup_printf ("%.2ld", num);
731 else
732 tmp = g_strdup (_("Unknown"));
733 gtk_entry_set_text (field, tmp);
734 g_free (tmp);
737 static void
738 rb_song_info_populate_dialog_multiple (RBSongInfo *song_info)
740 gboolean mixed_artists = FALSE;
741 gboolean mixed_albums = FALSE;
742 gboolean mixed_genres = FALSE;
743 gboolean mixed_years = FALSE;
744 gboolean mixed_disc_numbers = FALSE;
745 gboolean mixed_ratings = FALSE;
746 const char *artist = NULL;
747 const char *album = NULL;
748 const char *genre = NULL;
749 int year = 0;
750 int disc_number = 0;
751 double rating = 0.0; /* Zero is used for both "unrated" and "mixed ratings" too */
752 GList *l;
754 g_assert (song_info->priv->selected_entries);
756 for (l = song_info->priv->selected_entries; l != NULL; l = g_list_next (l)) {
757 RhythmDBEntry *entry;
758 const char *entry_artist;
759 const char *entry_album;
760 const char *entry_genre;
761 int entry_year;
762 int entry_disc_number;
763 double entry_rating;
765 entry = (RhythmDBEntry*)l->data;
766 entry_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
767 entry_album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
768 entry_genre = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE);
769 entry_year = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_YEAR);
770 entry_disc_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER);
771 entry_rating = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING);
773 /* grab first valid values */
774 if (artist == NULL)
775 artist = entry_artist;
776 if (album == NULL)
777 album = entry_album;
778 if (genre == NULL)
779 genre = entry_genre;
780 if (year == 0)
781 year = entry_year;
782 if (disc_number == 0)
783 disc_number = entry_disc_number;
784 if (fabs(rating) < EPSILON)
785 rating = entry_rating;
787 /* locate mixed values */
788 if (artist != entry_artist)
789 mixed_artists = TRUE;
790 if (album != entry_album)
791 mixed_albums = TRUE;
792 if (genre != entry_genre)
793 mixed_genres = TRUE;
794 if (year != entry_year)
795 mixed_years = TRUE;
796 if (disc_number != entry_disc_number)
797 mixed_disc_numbers = TRUE;
798 if (fabs(rating - entry_rating) >= EPSILON)
799 mixed_ratings = TRUE;
801 /* don't continue search if everything is mixed */
802 if (mixed_artists && mixed_albums && mixed_genres &&
803 mixed_years && mixed_disc_numbers && mixed_ratings)
804 break;
807 if (!mixed_artists && artist != NULL)
808 gtk_entry_set_text (GTK_ENTRY (song_info->priv->artist), artist);
809 if (!mixed_albums && album != NULL)
810 gtk_entry_set_text (GTK_ENTRY (song_info->priv->album), album);
811 if (!mixed_genres && genre != NULL)
812 gtk_entry_set_text (GTK_ENTRY (song_info->priv->genre), genre);
813 if (!mixed_years && year != 0)
814 rb_song_info_populate_num_field (GTK_ENTRY (song_info->priv->year), year);
815 if (!mixed_disc_numbers && disc_number != 0)
816 rb_song_info_populate_num_field (GTK_ENTRY (song_info->priv->disc_cur), disc_number);
817 if (!mixed_ratings && fabs(rating) >= EPSILON)
818 g_object_set (G_OBJECT (song_info->priv->rating), "rating", rating, NULL);
821 static void
822 rb_song_info_populate_dialog (RBSongInfo *song_info)
824 const char *text;
825 char *tmp;
826 gulong num;
828 g_assert (song_info->priv->current_entry);
830 /* update the buttons sensitivity */
831 rb_song_info_update_buttons (song_info);
833 text = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_TITLE);
834 gtk_entry_set_text (GTK_ENTRY (song_info->priv->title), text);
836 tmp = g_strdup_printf (_("%s Properties"), text);
837 gtk_window_set_title (GTK_WINDOW (song_info), tmp);
838 g_free (tmp);
840 text = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_ARTIST);
841 gtk_entry_set_text (GTK_ENTRY (song_info->priv->artist), text);
842 text = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_ALBUM);
843 gtk_entry_set_text (GTK_ENTRY (song_info->priv->album), text);
844 text = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_GENRE);
845 gtk_entry_set_text (GTK_ENTRY (song_info->priv->genre), text);
847 num = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_TRACK_NUMBER);
848 rb_song_info_populate_num_field (GTK_ENTRY (song_info->priv->track_cur), num);
849 num = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_DISC_NUMBER);
850 rb_song_info_populate_num_field (GTK_ENTRY (song_info->priv->disc_cur), num);
852 rb_song_info_update_duration (song_info);
853 rb_song_info_update_location (song_info);
854 rb_song_info_update_filesize (song_info);
855 rb_song_info_update_play_count (song_info);
856 rb_song_info_update_last_played (song_info);
857 rb_song_info_update_bitrate (song_info);
858 rb_song_info_update_rating (song_info);
859 rb_song_info_update_year (song_info);
860 rb_song_info_update_playback_error (song_info);
863 static void
864 rb_song_info_update_playback_error (RBSongInfo *song_info)
866 char *message = NULL;
868 if (!song_info->priv->current_entry)
869 return;
871 message = rhythmdb_entry_dup_string (song_info->priv->current_entry, RHYTHMDB_PROP_PLAYBACK_ERROR);
873 if (message) {
874 gtk_label_set_text (GTK_LABEL (song_info->priv->playback_error_label),
875 message);
876 gtk_widget_show (song_info->priv->playback_error_box);
877 } else {
878 gtk_label_set_text (GTK_LABEL (song_info->priv->playback_error_label),
879 "No errors");
880 gtk_widget_hide (song_info->priv->playback_error_box);
883 g_free (message);
886 static void
887 rb_song_info_update_bitrate (RBSongInfo *song_info)
889 char *tmp = NULL;
890 gulong bitrate = 0;
891 bitrate = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_BITRATE);
893 if (bitrate > 0)
894 tmp = g_strdup_printf (_("%lu kbps"), bitrate);
895 else
896 tmp = g_strdup (_("Unknown"));
897 gtk_label_set_text (GTK_LABEL (song_info->priv->bitrate),
898 tmp);
899 g_free (tmp);
902 static void
903 rb_song_info_update_duration (RBSongInfo *song_info)
905 char *text = NULL;
906 long duration = 0;
907 int minutes, seconds;
908 duration = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_DURATION);
909 minutes = duration / 60;
910 seconds = duration % 60;
911 text = g_strdup_printf ("%d:%02d", minutes, seconds);
912 gtk_label_set_text (GTK_LABEL (song_info->priv->duration), text);
913 g_free (text);
916 static void
917 rb_song_info_update_filesize (RBSongInfo *song_info)
919 char *text = NULL;
920 guint64 filesize = 0;
921 filesize = rhythmdb_entry_get_uint64 (song_info->priv->current_entry, RHYTHMDB_PROP_FILE_SIZE);
922 text = gnome_vfs_format_file_size_for_display (filesize);
923 gtk_label_set_text (GTK_LABEL (song_info->priv->filesize), text);
924 g_free (text);
927 static void
928 rb_song_info_update_location (RBSongInfo *song_info)
930 const char *text;
932 g_return_if_fail (song_info != NULL);
934 text = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_LOCATION);
936 if (text != NULL) {
937 char *tmp, *tmp_utf8;
938 char *basename, *dir, *desktopdir;
940 basename = g_path_get_basename (text);
941 tmp = gnome_vfs_unescape_string_for_display (basename);
942 g_free (basename);
943 tmp_utf8 = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
944 g_free (tmp);
945 if (tmp_utf8 != NULL) {
946 gtk_entry_set_text (GTK_ENTRY (song_info->priv->name),
947 tmp_utf8);
948 } else {
949 gtk_entry_set_text (GTK_ENTRY (song_info->priv->name),
950 _("Unknown file name"));
953 g_free (tmp_utf8);
955 tmp = gnome_vfs_get_local_path_from_uri (text);
956 if (tmp == NULL)
957 tmp = g_strdup (text);
958 dir = g_path_get_dirname (tmp);
959 g_free (tmp);
960 tmp = gnome_vfs_unescape_string_for_display (dir);
961 g_free (dir);
962 tmp_utf8 = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
963 g_free (tmp);
965 desktopdir = g_build_filename (g_get_home_dir (), "Desktop", NULL);
966 if ((tmp_utf8 != NULL) && (strcmp (tmp_utf8, desktopdir) == 0))
968 g_free (tmp_utf8);
969 tmp_utf8 = g_strdup (_("On the desktop"));
971 g_free (desktopdir);
973 if (tmp_utf8 != NULL) {
974 gtk_entry_set_text (GTK_ENTRY (song_info->priv->location),
975 tmp_utf8);
976 } else {
977 gtk_entry_set_text (GTK_ENTRY (song_info->priv->location),
978 _("Unknown location"));
980 g_free (tmp_utf8);
984 static void
985 rb_song_info_backward_clicked_cb (GtkWidget *button,
986 RBSongInfo *song_info)
988 RhythmDBEntry *new_entry;
990 rb_song_info_sync_entries (RB_SONG_INFO (song_info));
991 new_entry = rhythmdb_query_model_get_previous_from_entry (song_info->priv->query_model,
992 song_info->priv->current_entry);
993 g_return_if_fail (new_entry != NULL);
995 song_info->priv->current_entry = new_entry;
996 rb_entry_view_select_entry (song_info->priv->entry_view, new_entry);
997 rb_entry_view_scroll_to_entry (song_info->priv->entry_view, new_entry);
999 rb_song_info_populate_dialog (song_info);
1000 g_object_notify (G_OBJECT (song_info), "current-entry");
1001 rhythmdb_entry_unref (new_entry);
1004 static void
1005 rb_song_info_forward_clicked_cb (GtkWidget *button,
1006 RBSongInfo *song_info)
1008 RhythmDBEntry *new_entry;
1010 rb_song_info_sync_entries (RB_SONG_INFO (song_info));
1011 new_entry = rhythmdb_query_model_get_next_from_entry (song_info->priv->query_model,
1012 song_info->priv->current_entry);
1013 g_return_if_fail (new_entry != NULL);
1015 song_info->priv->current_entry = new_entry;
1016 rb_entry_view_select_entry (song_info->priv->entry_view, new_entry);
1017 rb_entry_view_scroll_to_entry (song_info->priv->entry_view, new_entry);
1019 rb_song_info_populate_dialog (song_info);
1020 g_object_notify (G_OBJECT (song_info), "current-entry");
1022 rhythmdb_entry_unref (new_entry);
1026 * rb_song_info_update_buttons: update back/forward sensitivity
1028 static void
1029 rb_song_info_update_buttons (RBSongInfo *song_info)
1031 RhythmDBEntry *entry = NULL;
1033 g_return_if_fail (song_info != NULL);
1034 g_return_if_fail (song_info->priv->query_model != NULL);
1036 if (!song_info->priv->current_entry)
1037 return;
1039 /* backward */
1040 entry = rhythmdb_query_model_get_previous_from_entry (song_info->priv->query_model,
1041 song_info->priv->current_entry);
1043 gtk_widget_set_sensitive (song_info->priv->backward, entry != NULL);
1044 if (entry != NULL)
1045 rhythmdb_entry_unref (entry);
1047 /* forward */
1048 entry = rhythmdb_query_model_get_next_from_entry (song_info->priv->query_model,
1049 song_info->priv->current_entry);
1051 gtk_widget_set_sensitive (song_info->priv->forward, entry != NULL);
1052 if (entry != NULL)
1053 rhythmdb_entry_unref (entry);
1056 static void
1057 rb_song_info_query_model_inserted_cb (RhythmDBQueryModel *model,
1058 GtkTreePath *path,
1059 GtkTreeIter *iter,
1060 RBSongInfo *song_info)
1062 rb_song_info_update_buttons (song_info);
1065 static void
1066 rb_song_info_query_model_deleted_cb (RhythmDBQueryModel *model,
1067 RhythmDBEntry*entry,
1068 RBSongInfo *song_info)
1070 rb_song_info_update_buttons (song_info);
1073 static void
1074 rb_song_info_query_model_reordered_cb (RhythmDBQueryModel *model,
1075 GtkTreePath *path,
1076 GtkTreeIter *iter,
1077 gpointer *map,
1078 RBSongInfo *song_info)
1080 rb_song_info_update_buttons (song_info);
1083 static void
1084 rb_song_info_query_model_changed_cb (GObject *source,
1085 GParamSpec *whatever,
1086 RBSongInfo *song_info)
1088 if (song_info->priv->query_model) {
1089 g_signal_handlers_disconnect_by_func (G_OBJECT (song_info->priv->query_model),
1090 G_CALLBACK (rb_song_info_query_model_inserted_cb),
1091 song_info);
1092 g_signal_handlers_disconnect_by_func (G_OBJECT (song_info->priv->query_model),
1093 G_CALLBACK (rb_song_info_query_model_deleted_cb),
1094 song_info);
1095 g_signal_handlers_disconnect_by_func (G_OBJECT (song_info->priv->query_model),
1096 G_CALLBACK (rb_song_info_query_model_reordered_cb),
1097 song_info);
1099 g_object_unref (G_OBJECT (song_info->priv->query_model));
1102 g_object_get (source, "query-model", &song_info->priv->query_model, NULL);
1104 g_signal_connect_object (G_OBJECT (song_info->priv->query_model),
1105 "row-inserted", G_CALLBACK (rb_song_info_query_model_inserted_cb),
1106 song_info, 0);
1107 g_signal_connect_object (G_OBJECT (song_info->priv->query_model),
1108 "row-changed", G_CALLBACK (rb_song_info_query_model_inserted_cb),
1109 song_info, 0);
1110 g_signal_connect_object (G_OBJECT (song_info->priv->query_model),
1111 "entry-deleted", G_CALLBACK (rb_song_info_query_model_deleted_cb),
1112 song_info, 0);
1113 g_signal_connect_object (G_OBJECT (song_info->priv->query_model),
1114 "rows-reordered", G_CALLBACK (rb_song_info_query_model_reordered_cb),
1115 song_info, 0);
1117 /* update next button sensitivity */
1118 rb_song_info_update_buttons (song_info);
1121 static void
1122 rb_song_info_update_play_count (RBSongInfo *song_info)
1124 gulong num;
1125 char *text;
1127 num = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_PLAY_COUNT);
1128 text = g_strdup_printf ("%ld", num);
1129 gtk_label_set_text (GTK_LABEL (song_info->priv->play_count), text);
1130 g_free (text);
1133 static void
1134 rb_song_info_update_last_played (RBSongInfo *song_info)
1136 const char *str;
1137 str = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_LAST_PLAYED_STR);
1138 if (!strcmp ("", str))
1139 str = _("Never");
1140 gtk_label_set_text (GTK_LABEL (song_info->priv->last_played), str);
1143 static void
1144 rb_song_info_update_rating (RBSongInfo *song_info)
1146 double rating;
1148 g_return_if_fail (RB_IS_SONG_INFO (song_info));
1150 rating = rhythmdb_entry_get_double (song_info->priv->current_entry, RHYTHMDB_PROP_RATING);
1151 g_object_set (song_info->priv->rating,
1152 "rating", rating,
1153 NULL);
1156 static void
1157 rb_song_info_update_year (RBSongInfo *song_info)
1159 gulong year;
1160 char *text;
1162 year = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_YEAR);
1163 if (year > 0) {
1164 text = g_strdup_printf ("%lu", year);
1165 } else {
1166 text = g_strdup (_("Unknown"));
1168 gtk_entry_set_text (GTK_ENTRY (song_info->priv->year), text);
1169 g_free (text);
1172 static void
1173 rb_song_info_sync_entries_multiple (RBSongInfo *dialog)
1175 const char *genre = gtk_entry_get_text (GTK_ENTRY (dialog->priv->genre));
1176 const char *artist = gtk_entry_get_text (GTK_ENTRY (dialog->priv->artist));
1177 const char *album = gtk_entry_get_text (GTK_ENTRY (dialog->priv->album));
1178 const char *year_str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->year));
1179 const char *discn_str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->disc_cur));
1181 char *endptr;
1182 GValue val = {0,};
1183 GList *tem;
1184 gint year;
1185 gint discn;
1186 gboolean changed = FALSE;
1187 RhythmDBEntry *entry;
1189 if (strlen (album) > 0) {
1190 g_value_init (&val, G_TYPE_STRING);
1191 g_value_set_string (&val, album);
1192 for (tem = dialog->priv->selected_entries; tem; tem = tem->next) {
1193 const char *entry_album;
1195 entry = (RhythmDBEntry *)tem->data;
1196 entry_album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
1198 if (strcmp (album, entry_album) == 0)
1199 continue;
1200 rhythmdb_entry_set (dialog->priv->db, entry,
1201 RHYTHMDB_PROP_ALBUM, &val);
1202 changed = TRUE;
1204 g_value_unset (&val);
1207 if (strlen (artist) > 0) {
1208 g_value_init (&val, G_TYPE_STRING);
1209 g_value_set_string (&val, artist);
1210 for (tem = dialog->priv->selected_entries; tem; tem = tem->next) {
1211 const char *entry_artist;
1213 entry = (RhythmDBEntry *)tem->data;
1214 entry_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
1216 if (strcmp (artist, entry_artist) == 0)
1217 continue;
1218 rhythmdb_entry_set (dialog->priv->db, entry,
1219 RHYTHMDB_PROP_ARTIST, &val);
1220 changed = TRUE;
1222 g_value_unset (&val);
1225 if (strlen (genre) > 0) {
1226 g_value_init (&val, G_TYPE_STRING);
1227 g_value_set_string (&val, genre);
1228 for (tem = dialog->priv->selected_entries; tem; tem = tem->next) {
1229 const char *entry_genre;
1231 entry = (RhythmDBEntry *)tem->data;
1232 entry_genre = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE);
1234 if (strcmp (genre, entry_genre) == 0)
1235 continue;
1236 rhythmdb_entry_set (dialog->priv->db, entry,
1237 RHYTHMDB_PROP_GENRE, &val);
1238 changed = TRUE;
1240 g_value_unset (&val);
1243 if (strlen (year_str) > 0) {
1244 GDate *date = NULL;
1245 GType type;
1247 /* note: this will reset the day-of-year to Jan 1 for all entries */
1248 year = g_ascii_strtoull (year_str, &endptr, 10);
1249 if (year > 0)
1250 date = g_date_new_dmy (1, G_DATE_JANUARY, year);
1252 type = rhythmdb_get_property_type (dialog->priv->db,
1253 RHYTHMDB_PROP_DATE);
1255 g_value_init (&val, type);
1256 g_value_set_ulong (&val, (date ? g_date_get_julian (date) : 0));
1258 for (tem = dialog->priv->selected_entries; tem; tem = tem->next) {
1259 entry = (RhythmDBEntry *)tem->data;
1260 rhythmdb_entry_set (dialog->priv->db, entry,
1261 RHYTHMDB_PROP_DATE, &val);
1262 changed = TRUE;
1264 g_value_unset (&val);
1265 if (date)
1266 g_date_free (date);
1270 discn = g_ascii_strtoull (discn_str, &endptr, 10);
1271 if (endptr != discn_str) {
1272 GType type;
1273 type = rhythmdb_get_property_type (dialog->priv->db,
1274 RHYTHMDB_PROP_DISC_NUMBER);
1275 g_value_init (&val, type);
1276 g_value_set_ulong (&val, discn);
1278 for (tem = dialog->priv->selected_entries; tem; tem = tem->next) {
1279 gulong entry_disc_num;
1281 entry = (RhythmDBEntry *)tem->data;
1282 entry_disc_num = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER);
1284 if (discn != entry_disc_num) {
1285 rhythmdb_entry_set (dialog->priv->db, entry,
1286 RHYTHMDB_PROP_DISC_NUMBER, &val);
1287 changed = TRUE;
1290 g_value_unset (&val);
1293 if (changed)
1294 rhythmdb_commit (dialog->priv->db);
1297 static void
1298 rb_song_info_sync_entry_single (RBSongInfo *dialog)
1300 const char *title;
1301 const char *genre;
1302 const char *artist;
1303 const char *album;
1304 const char *tracknum_str;
1305 const char *discnum_str;
1306 const char *year_str;
1307 const char *entry_string;
1308 char *endptr;
1309 GType type;
1310 gulong tracknum;
1311 gulong discnum;
1312 gulong year;
1313 gulong entry_val;
1314 GValue val = {0,};
1315 gboolean changed = FALSE;
1316 RhythmDBEntry *entry = dialog->priv->current_entry;
1318 title = gtk_entry_get_text (GTK_ENTRY (dialog->priv->title));
1319 genre = gtk_entry_get_text (GTK_ENTRY (dialog->priv->genre));
1320 artist = gtk_entry_get_text (GTK_ENTRY (dialog->priv->artist));
1321 album = gtk_entry_get_text (GTK_ENTRY (dialog->priv->album));
1322 tracknum_str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->track_cur));
1323 discnum_str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->disc_cur));
1324 year_str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->year));
1326 g_signal_emit (dialog, rb_song_info_signals[PRE_METADATA_CHANGE], 0,
1327 entry);
1329 tracknum = g_ascii_strtoull (tracknum_str, &endptr, 10);
1330 entry_val = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER);
1331 if ((endptr != tracknum_str) && (tracknum != entry_val)) {
1332 type = rhythmdb_get_property_type (dialog->priv->db,
1333 RHYTHMDB_PROP_TRACK_NUMBER);
1334 g_value_init (&val, type);
1335 g_value_set_ulong (&val, tracknum);
1336 rhythmdb_entry_set (dialog->priv->db, entry, RHYTHMDB_PROP_TRACK_NUMBER, &val);
1337 g_value_unset (&val);
1338 changed = TRUE;
1341 discnum = g_ascii_strtoull (discnum_str, &endptr, 10);
1342 entry_val = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER);
1343 if ((endptr != discnum_str) && (discnum != entry_val)) {
1344 type = rhythmdb_get_property_type (dialog->priv->db,
1345 RHYTHMDB_PROP_DISC_NUMBER);
1346 g_value_init (&val, type);
1347 g_value_set_ulong (&val, discnum);
1348 rhythmdb_entry_set (dialog->priv->db, entry, RHYTHMDB_PROP_DISC_NUMBER, &val);
1349 g_value_unset (&val);
1350 changed = TRUE;
1353 year = g_ascii_strtoull (year_str, &endptr, 10);
1354 entry_val = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_YEAR);
1355 if ((endptr != year_str) &&
1356 (year != entry_val ||
1357 (entry_val == 0 && year > 0))) {
1358 GDate *date = NULL;
1360 if (year > 0) {
1361 if (entry_val > 0) {
1362 gulong julian;
1364 julian = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE);
1365 date = g_date_new_julian (julian);
1366 g_date_set_year (date, year);
1367 } else {
1368 date = g_date_new_dmy (1, G_DATE_JANUARY, year);
1372 type = rhythmdb_get_property_type (dialog->priv->db,
1373 RHYTHMDB_PROP_DATE);
1374 g_value_init (&val, type);
1375 g_value_set_ulong (&val, (date ? g_date_get_julian (date) : 0));
1376 rhythmdb_entry_set (dialog->priv->db, entry, RHYTHMDB_PROP_DATE, &val);
1377 changed = TRUE;
1379 g_value_unset (&val);
1380 if (date)
1381 g_date_free (date);
1384 entry_string = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
1385 if (strcmp (title, entry_string)) {
1386 type = rhythmdb_get_property_type (dialog->priv->db,
1387 RHYTHMDB_PROP_TITLE);
1388 g_value_init (&val, type);
1389 g_value_set_string (&val, title);
1390 rhythmdb_entry_set (dialog->priv->db, entry,
1391 RHYTHMDB_PROP_TITLE, &val);
1392 g_value_unset (&val);
1393 changed = TRUE;
1396 entry_string = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
1397 if (strcmp (album, entry_string)) {
1398 type = rhythmdb_get_property_type (dialog->priv->db,
1399 RHYTHMDB_PROP_ALBUM);
1400 g_value_init (&val, type);
1401 g_value_set_string (&val, album);
1402 rhythmdb_entry_set (dialog->priv->db, entry,
1403 RHYTHMDB_PROP_ALBUM, &val);
1404 g_value_unset (&val);
1405 changed = TRUE;
1408 entry_string = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
1409 if (strcmp (artist, entry_string)) {
1410 type = rhythmdb_get_property_type (dialog->priv->db,
1411 RHYTHMDB_PROP_ARTIST);
1412 g_value_init (&val, type);
1413 g_value_set_string (&val, artist);
1414 rhythmdb_entry_set (dialog->priv->db, entry,
1415 RHYTHMDB_PROP_ARTIST, &val);
1416 g_value_unset (&val);
1417 changed = TRUE;
1420 entry_string = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE);
1421 if (strcmp (genre, entry_string)) {
1422 type = rhythmdb_get_property_type (dialog->priv->db,
1423 RHYTHMDB_PROP_GENRE);
1424 g_value_init (&val, type);
1425 g_value_set_string (&val, genre);
1426 rhythmdb_entry_set (dialog->priv->db, entry,
1427 RHYTHMDB_PROP_GENRE, &val);
1428 g_value_unset (&val);
1429 changed = TRUE;
1432 /* FIXME: when an entry is SYNCed, a changed signal is emitted, and
1433 * this signal is also emitted, aren't they redundant?
1435 g_signal_emit (G_OBJECT (dialog), rb_song_info_signals[POST_METADATA_CHANGE], 0,
1436 entry);
1438 if (changed)
1439 rhythmdb_commit (dialog->priv->db);
1442 static void
1443 rb_song_info_sync_entries (RBSongInfo *dialog)
1445 if (!dialog->priv->editable)
1446 return;
1448 if (dialog->priv->current_entry)
1449 rb_song_info_sync_entry_single (dialog);
1450 else
1451 rb_song_info_sync_entries_multiple (dialog);