1 /* $Id$ */
2 /*-
3  * Copyright (c) 2006 Jannis Pohlmann <jannis@xfce.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18  * MA 02111-1307 USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #ifdef HAVE_MEMORY_H
26 #include <memory.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 
32 #include <taglib/tag_c.h>
33 
34 #include <exo/exo.h>
35 
36 #include <audio-tags-page.h>
37 
38 
39 
40 /* Property identifiers */
41 enum
42 {
43   PROP_0,
44   PROP_FILE,
45   PROP_TAGLIB_FILE,
46   PROP_TRACK,
47   PROP_ARTIST,
48   PROP_TITLE,
49   PROP_ALBUM,
50   PROP_COMMENT,
51   PROP_GENRE,
52   PROP_YEAR,
53   PROP_SHOW_SAVE_BUTTON,
54 };
55 
56 
57 
58 static void     audio_tags_page_finalize            (GObject            *object);
59 static void     audio_tags_page_get_property        (GObject            *object,
60                                                      guint               prop_id,
61                                                      GValue             *value,
62                                                      GParamSpec         *pspec);
63 static void     audio_tags_page_set_property        (GObject            *object,
64                                                      guint               prop_id,
65                                                      const GValue       *value,
66                                                      GParamSpec         *pspec);
67 static void     audio_tags_page_file_changed        (ThunarxFileInfo    *file,
68                                                      AudioTagsPage      *page);
69 static void     audio_tags_page_taglib_file_changed (TagLib_File        *taglib_file,
70                                                      AudioTagsPage      *page);
71 static gboolean audio_tags_page_activate            (AudioTagsPage      *page);
72 static gboolean audio_tags_page_info_activate       (GSimpleAction      *action,
73                                                      GVariant           *parameter,
74                                                      AudioTagsPage      *page);
75 static gboolean audio_tags_page_load_tags           (gpointer            data);
76 
77 
78 
79 struct _AudioTagsPageClass
80 {
81   ThunarxPropertyPageClass __parent__;
82 };
83 
84 
85 
86 struct _AudioTagsPage
87 {
88   ThunarxPropertyPage __parent__;
89 
90   /* Widgets */
91   GtkWidget       *grid;
92   GtkWidget       *save_button;
93   GtkWidget       *info_button;
94 
95   /* Timeouts */
96   guint            changed_idle;
97 
98   /* Properties */
99   ThunarxFileInfo *file;
100   TagLib_File     *taglib_file;
101   guint            track;
102   gchar           *artist;
103   gchar           *title;
104   gchar           *album;
105   gchar           *comment;
106   gchar           *genre;
107   guint            year;
108 
109   /* <private> */
110   GActionGroup    *action_group;
111 
112   /* garbage collector of files */
113   GSList           *files_gc;
114 };
115 
116 
117 
118 
119 THUNARX_DEFINE_TYPE (AudioTagsPage, audio_tags_page, THUNARX_TYPE_PROPERTY_PAGE);
120 
121 
122 
123 static void
124 audio_tags_page_class_init (AudioTagsPageClass *klass)
125 {
126   GObjectClass             *gobject_class;
127 
128   gobject_class = G_OBJECT_CLASS (klass);
129   gobject_class->finalize = audio_tags_page_finalize;
130   gobject_class->get_property = audio_tags_page_get_property;
131   gobject_class->set_property = audio_tags_page_set_property;
132 
133   /**
134    * TagRenamerPropertiesPage:file:
135    *
136    * The #ThunarxFileInfo modified on this page.
137    **/
138   g_object_class_install_property (gobject_class,
139                                    PROP_FILE,
140                                    g_param_spec_object ("file",
141                                                         "file",
142                                                         "file",
143                                                         THUNARX_TYPE_FILE_INFO,
144                                                         G_PARAM_READWRITE));
145 
146   /**
147    * TagRenamerPropertiesPage:taglib-file:
148    *
149    * The #TagLib_File used on this page.
150    **/
151   g_object_class_install_property (gobject_class,
152                                    PROP_TAGLIB_FILE,
153                                    g_param_spec_pointer ("taglib-file",
154                                                          "taglib-file",
155                                                          "taglib-file",
156                                                          G_PARAM_READWRITE));
157 
158   /**
159    * TagRenamerPropertiesPage:track:
160    *
161    * The track number entered by the user.
162    **/
163   g_object_class_install_property (gobject_class,
164                                    PROP_TRACK,
165                                    g_param_spec_double ("track",
166                                                         "track",
167                                                         "track",
168                                                         1,
169                                                         999,
170                                                         1,
171                                                         G_PARAM_READWRITE));
172 
173   /**
174    * TagRenamerPropertiesPage:artist:
175    *
176    * The artist entered by the user.
177    **/
178   g_object_class_install_property (gobject_class,
179                                    PROP_ARTIST,
180                                    g_param_spec_string ("artist",
181                                                         "artist",
182                                                         "artist",
183                                                         _("Unknown Artist"),
184                                                         G_PARAM_READWRITE));
185 
186   /**
187    * TagRenamerPropertiesPage:album:
188    *
189    * The album title entered by the user.
190    **/
191   g_object_class_install_property (gobject_class,
192                                    PROP_ALBUM,
193                                    g_param_spec_string ("album",
194                                                         "album",
195                                                         "album",
196                                                         _("Unknown Album"),
197                                                         G_PARAM_READWRITE));
198 
199   /**
200    * TagRenamerPropertiesPage:title:
201    *
202    * The song title entered by the user.
203    **/
204   g_object_class_install_property (gobject_class,
205                                    PROP_TITLE,
206                                    g_param_spec_string ("title",
207                                                         "title",
208                                                         "title",
209                                                         _("Unknown Title"),
210                                                         G_PARAM_READWRITE));
211 
212   /**
213    * TagRenamerPropertiesPage:comment:
214    *
215    * The comment entered by the user.
216    **/
217   g_object_class_install_property (gobject_class,
218                                    PROP_COMMENT,
219                                    g_param_spec_string ("comment",
220                                                         "comment",
221                                                         "comment",
222                                                         "",
223                                                         G_PARAM_READWRITE));
224 
225   /**
226    * TagRenamerPropertiesPage:genre:
227    *
228    * The music genre entered by the user.
229    **/
230   g_object_class_install_property (gobject_class,
231                                    PROP_GENRE,
232                                    g_param_spec_string ("genre",
233                                                         "genre",
234                                                         "genre",
235                                                         "",
236                                                         G_PARAM_READWRITE));
237 
238   /**
239    * TagRenamerPropertiesPage:year:
240    *
241    * The year entered by the user.
242    **/
243   g_object_class_install_property (gobject_class,
244                                    PROP_YEAR,
245                                    g_param_spec_double ("year",
246                                                         "year",
247                                                         "year",
248                                                         1700,
249                                                         9999,
250                                                         2006,
251                                                         G_PARAM_READWRITE));
252 
253   /**
254    * TagRenamerPropertiesPage:show-save-button:
255    *
256    * Whether the page contains a save button or not.
257    **/
258   g_object_class_install_property (gobject_class,
259                                    PROP_SHOW_SAVE_BUTTON,
260                                    g_param_spec_boolean ("show-save-button",
261                                                          "show-save-button",
262                                                          "show-save-button",
263                                                          FALSE,
264                                                          G_PARAM_READWRITE));
265 }
266 
267 
268 
269 static void
270 audio_tags_page_init (AudioTagsPage *page)
271 {
272   GtkAdjustment *adjustment;
273   GtkWidget     *vbox;
274   GtkWidget     *label;
275   GtkWidget     *entry;
276   GtkWidget     *combo;
277   GtkWidget     *spin;
278   GtkWidget     *toplevel;
279   GSimpleAction *action;
280   guint          i;
281 
282   /* Default genre list */
283   static const gchar *genres[] = {
284     "A Cappella", "Acid", "Acid Jazz", "Acid Punk", "Acoustic", "Alt. Rock",
285     "Alternative", "Ambient", "Anime", "Avantgarde", "Ballad", "Bass",
286     "Beat", "Bebob", "Big Band", "Black Metal", "Bluegrass", "Blues",
287     "Booty Bass", "BritPop", "Cabaret", "Celtic", "Chamber music", "Chanson",
288     "Chorus", "Christian Gangsta Rap", "Christian Rap", "Christian Rock", "Classic Rock", "Classical",
289     "Club", "Club-House", "Comedy", "Contemporary Christian", "Country", "Crossover",
290     "Cult", "Dance", "Dance Hall", "Darkwave", "Death Metal", "Disco",
291     "Dream", "Drum & Bass", "Drum Solo", "Duet", "Easy Listening", "Electronic",
292     "Ethnic", "Euro-House", "Euro-Techno", "Eurodance", "Fast Fusion", "Folk",
293     "Folk-Rock", "Folklore", "Freestyle", "Funk", "Fusion", "Game",
294     "Gangsta", "Goa", "Gospel", "Gothic", "Gothic Rock", "Grunge",
295     "Hard Rock", "Hardcore", "Heavy Metal", "Hip-Hop", "House", "Humour",
296     "Indie", "Industrial", "Instrumental", "Instrumental pop", "Instrumental rock", "JPop",
297     "Jazz", "Jazz+Funk", "Jungle", "Latin", "Lo-Fi", "Meditative",
298     "Merengue", "Metal", "Musical", "National Folk", "Native American", "Negerpunk",
299     "New Age", "New Wave", "Noise", "Oldies", "Opera", "Other",
300     "Polka", "Polsk Punk", "Pop", "Pop-Folk", "Pop/Funk", "Porn Groove",
301     "Power Ballad", "Pranks", "Primus", "Progressive Rock", "Psychedelic", "Psychedelic Rock",
302     "Punk", "Punk Rock", "R&B", "Rap", "Rave", "Reggae",
303     "Retro", "Revival", "Rhythmic soul", "Rock", "Rock & Roll", "Salsa",
304     "Samba", "Satire", "Showtunes", "Ska", "Slow Jam", "Slow Rock",
305     "Sonata", "Soul", "Sound Clip", "Soundtrack", "Southern Rock", "Space",
306     "Speech", "Swing", "Symphonic Rock", "Symphony", "Synthpop", "Tango",
307     "Techno", "Techno-Industrial", "Terror", "Thrash Metal", "Top 40", "Trailer",
308   };
309 
310   gtk_container_set_border_width (GTK_CONTAINER (page), 8);
311 
312   /* Main container */
313   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
314   gtk_container_add (GTK_CONTAINER (page), vbox);
315   gtk_widget_show (vbox);
316 
317   /* Tag editor layout grid */
318   page->grid = gtk_grid_new ();
319   gtk_grid_set_row_spacing (GTK_GRID (page->grid), 6);
320   gtk_grid_set_column_spacing (GTK_GRID (page->grid), 12);
321   gtk_container_add (GTK_CONTAINER (vbox), page->grid);
322   gtk_container_set_border_width (GTK_CONTAINER (page->grid), 12);
323   gtk_widget_show (page->grid);
324 
325   /* Track label */
326   label = gtk_label_new ("");
327   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
328   gtk_label_set_markup (GTK_LABEL (label), _("<b>Track:</b>"));
329   gtk_grid_attach (GTK_GRID (page->grid), label, 0, 0, 1, 1);
330   gtk_widget_show (label);
331 
332   /* Track adjustment */
333   adjustment = gtk_adjustment_new (0, 0, 999, 1, 5, 0);
334 
335   /* Track spin button */
336   spin = gtk_spin_button_new (adjustment, 0.0, 0);
337   exo_mutual_binding_new (G_OBJECT (adjustment), "value", G_OBJECT (page), "track");
338   gtk_widget_set_tooltip_text (spin, _("Enter the track number here."));
339   gtk_widget_set_halign (spin, GTK_ALIGN_START);
340   gtk_grid_attach (GTK_GRID (page->grid), spin, 1, 0, 1, 1);
341   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin);
342   gtk_widget_show (spin);
343 
344   /* Year label */
345   label = gtk_label_new ("");
346   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
347   gtk_label_set_markup (GTK_LABEL (label), _("<b>Year:</b>"));
348   gtk_grid_attach (GTK_GRID (page->grid), label, 2, 0, 1, 1);
349   gtk_widget_show (label);
350 
351   /* Year spin button range description */
352   adjustment = gtk_adjustment_new (2006, 1700, 9999, 1, 10, 0);
353 
354   /* Year spin button */
355   spin = gtk_spin_button_new (adjustment, 1, 0);
356   exo_mutual_binding_new (G_OBJECT (adjustment), "value", G_OBJECT (page), "year");
357   gtk_widget_set_tooltip_text (spin, _("Enter the release year here."));
358   gtk_widget_set_halign (spin, GTK_ALIGN_START);
359   gtk_grid_attach (GTK_GRID (page->grid), spin, 3, 0, 1, 1);
360   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin);
361   gtk_widget_show (spin);
362 
363   /* Artist label */
364   label = gtk_label_new ("");
365   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
366   gtk_label_set_markup (GTK_LABEL (label), _("<b>Artist:</b>"));
367   gtk_grid_attach (GTK_GRID (page->grid), label, 0, 1, 1, 1);
368   gtk_widget_show (label);
369 
370   /* Artist entry */
371   entry = gtk_entry_new ();
372   exo_mutual_binding_new (G_OBJECT (entry), "text", G_OBJECT (page), "artist");
373   gtk_widget_set_tooltip_text (GTK_WIDGET (entry), _("Enter the name of the artist or author of this file here."));
374   gtk_widget_set_hexpand (entry, TRUE);
375   gtk_grid_attach (GTK_GRID (page->grid), entry, 1, 1, 3, 1);
376   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
377   gtk_widget_show (entry);
378 
379   /* Title label */
380   label = gtk_label_new ("");
381   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
382   gtk_label_set_markup (GTK_LABEL (label), _("<b>Title:</b>"));
383   gtk_grid_attach (GTK_GRID (page->grid), label, 0, 2, 1, 1);
384   gtk_widget_show (label);
385 
386   /* Title entry */
387   entry = gtk_entry_new ();
388   exo_mutual_binding_new (G_OBJECT (entry), "text", G_OBJECT (page), "title");
389   gtk_widget_set_tooltip_text (GTK_WIDGET (entry), _("Enter the song title here."));
390   gtk_widget_set_hexpand (entry, TRUE);
391   gtk_grid_attach (GTK_GRID (page->grid), entry, 1, 2, 3, 1);
392   gtk_widget_show (entry);
393 
394   /* Album label */
395   label = gtk_label_new ("");
396   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
397   gtk_label_set_markup (GTK_LABEL (label), _("<b>Album:</b>"));
398   gtk_grid_attach (GTK_GRID (page->grid), label, 0, 3, 1, 1);
399   gtk_widget_show (label);
400 
401   /* Album entry */
402   entry = gtk_entry_new ();
403   exo_mutual_binding_new (G_OBJECT (entry), "text", G_OBJECT (page), "album");
404   gtk_widget_set_tooltip_text (GTK_WIDGET (entry), _("Enter the album/record title here."));
405   gtk_widget_set_hexpand (entry, TRUE);
406   gtk_grid_attach (GTK_GRID (page->grid), entry, 1, 3, 3, 1);
407   gtk_widget_show (entry);
408 
409   /* Comment label */
410   label = gtk_label_new ("");
411   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
412   gtk_label_set_markup (GTK_LABEL (label), _("<b>Comment:</b>"));
413   gtk_grid_attach (GTK_GRID (page->grid), label, 0, 4, 1, 1);
414   gtk_widget_show (label);
415 
416   /* Comment entry */
417   entry = gtk_entry_new ();
418   exo_mutual_binding_new (G_OBJECT (entry), "text", G_OBJECT (page), "comment");
419   gtk_widget_set_tooltip_text (GTK_WIDGET (entry), _("Enter your comments here."));
420   gtk_widget_set_hexpand (entry, TRUE);
421   gtk_grid_attach (GTK_GRID (page->grid), entry, 1, 4, 3, 1);
422   gtk_widget_show (entry);
423 
424   /* Genre label */
425   label = gtk_label_new ("");
426   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
427   gtk_label_set_markup (GTK_LABEL (label), _("<b>Genre:</b>"));
428   gtk_grid_attach (GTK_GRID (page->grid), label, 0, 5, 1, 1);
429   gtk_widget_show (label);
430 
431   /* Genre combo box */
432   combo = gtk_combo_box_text_new_with_entry ();
433   exo_mutual_binding_new (G_OBJECT (gtk_bin_get_child (GTK_BIN (combo))), "text", G_OBJECT (page), "genre");
434   gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select or enter the genre of this song here."));
435   gtk_grid_attach (GTK_GRID (page->grid), combo, 1, 5, 3, 1);
436   gtk_widget_show (combo);
437 
438   for (i=0; i<G_N_ELEMENTS (genres); i++)
439     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), genres[i]);
440 
441   /* Create action group for the page */
442   page->action_group = G_ACTION_GROUP (g_simple_action_group_new ());
443   gtk_widget_insert_action_group (GTK_WIDGET (page), "audio-tags-page-actions", page->action_group);
444 
445   /* Create and add the save action */
446   action = g_simple_action_new ("save", NULL);
447   g_action_map_add_action (G_ACTION_MAP (page->action_group), G_ACTION (action));
448 
449   /* Connect to save action */
450   g_signal_connect_swapped (G_OBJECT (action), "activate", G_CALLBACK (audio_tags_page_activate), page);
451 
452   /* Create and add the info action */
453   action = g_simple_action_new ("info", NULL);
454   g_action_map_add_action (G_ACTION_MAP (page->action_group), G_ACTION (action));
455 
456   /* Determine parent window and assign it to the action */
457   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page));
458   if (gtk_widget_is_toplevel (toplevel))
459     g_object_set_data_full (G_OBJECT (action), "window", g_object_ref (G_OBJECT (toplevel)), (GDestroyNotify)g_object_unref);
460 
461   /* Connect to the info action */
462   g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (audio_tags_page_info_activate), page);
463 
464   /* Reset the save button */
465   audio_tags_page_set_show_save_button (page, FALSE);
466 }
467 
468 
469 
470 static void
471 audio_tags_page_finalize (GObject *object)
472 {
473   AudioTagsPage *page = AUDIO_TAGS_PAGE (object);
474 
475   /* Destroy action group */
476   if (G_LIKELY (page->action_group != NULL))
477     g_object_unref (G_OBJECT (page->action_group));
478 
479   /* TODO Do I need to free the actions explicitely? */
480 
481   /* Unregister the changed_idle */
482   if (G_UNLIKELY (page->changed_idle != 0))
483     g_source_remove (page->changed_idle);
484 
485   /* Free file reference */
486   audio_tags_page_set_file (page, NULL);
487 
488   /* Free taglib file reference */
489   audio_tags_page_set_taglib_file (page, NULL);
490 
491   /* Cleanup garbage */
492   g_slist_foreach (page->files_gc, (GFunc) taglib_file_free, NULL);
493   g_slist_free (page->files_gc);
494 
495   /* Free strings */
496   if (G_LIKELY (page->artist != NULL))
497     g_free (page->artist);
498 
499   if (G_LIKELY (page->title != NULL))
500     g_free (page->title);
501 
502   if (G_LIKELY (page->album != NULL))
503     g_free (page->album);
504 
505   if (G_LIKELY (page->comment != NULL))
506     g_free (page->comment);
507 
508   if (G_LIKELY (page->genre != NULL))
509     g_free (page->genre);
510 
511   (*G_OBJECT_CLASS (audio_tags_page_parent_class)->finalize) (object);
512 }
513 
514 
515 
516 AudioTagsPage*
517 audio_tags_page_new (void)
518 {
519   AudioTagsPage *page = g_object_new (TYPE_AUDIO_TAGS_PAGE, NULL);
520 
521   thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (page), _("Audio"));
522 
523   return page;
524 }
525 
526 
527 
528 AudioTagsPage*
529 audio_tags_page_new_with_save_button (void)
530 {
531   AudioTagsPage *page = audio_tags_page_new ();
532 
533   /* Add save button */
534   audio_tags_page_set_show_save_button (page, TRUE);
535 
536   return page;
537 }
538 
539 
540 
541 GtkWidget*
542 audio_tags_page_dialog_new (GtkWindow *window,
543                             ThunarxFileInfo *file)
544 {
545   GtkWidget     *dialog;
546   GtkWidget     *button;
547   AudioTagsPage *page;
548 
549   /* Create the audio tags page */
550   page = audio_tags_page_new ();
551 
552   /* Set audio file */
553   audio_tags_page_set_file (page, file);
554 
555   /* Set up the dialog */
556   dialog = gtk_dialog_new_with_buttons (_("Edit Tags"),
557                                         window,
558                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
559                                         _("_Cancel"),
560                                         GTK_RESPONSE_CANCEL,
561                                         NULL);
562 
563   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
564 
565   /* Add page to the dialog */
566   gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), GTK_WIDGET (page));
567   gtk_widget_show (GTK_WIDGET (page));
568 
569   /* Create save button */
570   button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Save"), GTK_RESPONSE_OK);
571 
572   /* Connect save button to the "save" action */
573   gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "audio-tags-page-actions.save");
574   gtk_widget_insert_action_group (GTK_WIDGET (dialog), "audio-tags-page-actions", page->action_group);
575 
576   return dialog;
577 }
578 
579 
580 
581 static void
582 audio_tags_page_get_property (GObject    *object,
583                               guint       prop_id,
584                               GValue     *value,
585                               GParamSpec *pspec)
586 {
587   AudioTagsPage *page = AUDIO_TAGS_PAGE (object);
588 
589   switch (prop_id)
590     {
591     case PROP_FILE:
592       g_value_set_object (value, audio_tags_page_get_file (page));
593       break;
594 
595     case PROP_TAGLIB_FILE:
596       g_value_set_pointer (value, audio_tags_page_get_taglib_file (page));
597       break;
598 
599     case PROP_TRACK:
600       g_value_set_double (value, page->track);
601       break;
602 
603     case PROP_ARTIST:
604       g_value_set_string (value, page->artist);
605       break;
606 
607     case PROP_TITLE:
608       g_value_set_string (value, page->title);
609       break;
610 
611     case PROP_ALBUM:
612       g_value_set_string (value, page->album);
613       break;
614 
615     case PROP_COMMENT:
616       g_value_set_string (value, page->comment);
617       break;
618 
619     case PROP_GENRE:
620       g_value_set_string (value, page->genre);
621       break;
622 
623     case PROP_YEAR:
624       g_value_set_double (value, page->year);
625       break;
626 
627     case PROP_SHOW_SAVE_BUTTON:
628       g_value_set_boolean (value, page->save_button != NULL);
629       break;
630 
631     default:
632       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
633       break;
634     }
635 }
636 
637 
638 
639 static void
640 audio_tags_page_set_property (GObject      *object,
641                               guint         prop_id,
642                               const GValue *value,
643                               GParamSpec   *pspec)
644 {
645   AudioTagsPage *page = AUDIO_TAGS_PAGE (object);
646 
647   switch (prop_id)
648     {
649     case PROP_FILE:
650       audio_tags_page_set_file (page, g_value_get_object (value));
651       break;
652 
653     case PROP_TAGLIB_FILE:
654       audio_tags_page_set_taglib_file (page, g_value_get_pointer (value));
655       break;
656 
657     case PROP_TRACK:
658       page->track = g_value_get_double (value);
659       break;
660 
661     case PROP_ARTIST:
662       if (G_LIKELY (page->artist != NULL))
663         g_free (page->artist);
664       page->artist = g_strstrip (g_strdup (g_value_get_string (value)));
665       break;
666 
667     case PROP_TITLE:
668       if (G_LIKELY (page->title != NULL))
669         g_free (page->title);
670       page->title = g_strstrip (g_strdup (g_value_get_string (value)));
671       break;
672 
673     case PROP_ALBUM:
674       if (G_LIKELY (page->album != NULL))
675         g_free (page->album);
676       page->album = g_strstrip (g_strdup (g_value_get_string (value)));
677       break;
678 
679     case PROP_COMMENT:
680       if (G_LIKELY (page->comment != NULL))
681         g_free (page->comment);
682       page->comment = g_strstrip (g_strdup (g_value_get_string (value)));
683       break;
684 
685     case PROP_GENRE:
686       if (G_LIKELY (page->genre != NULL))
687         g_free (page->genre);
688       page->genre = g_strstrip (g_strdup (g_value_get_string (value)));
689       break;
690 
691     case PROP_YEAR:
692       page->year = g_value_get_double (value);
693       break;
694 
695     case PROP_SHOW_SAVE_BUTTON:
696       audio_tags_page_set_show_save_button (page, g_value_get_boolean (value));
697       break;
698 
699     default:
700       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
701       break;
702     }
703 }
704 
705 
706 
707 /**
708  * audio_tags_page_get_file:
709  * @page : a #AudioTagsPage.
710  *
711  * Returns the current #ThunarxFileInfo
712  * for the @page.
713  *
714  * Return value: the file associated with this property page.
715  **/
716 ThunarxFileInfo*
717 audio_tags_page_get_file (AudioTagsPage *page)
718 {
719   g_return_val_if_fail (IS_AUDIO_TAGS_PAGE (page), NULL);
720   return page->file;
721 }
722 
723 
724 
725 /**
726  * audio_tags_page_set_file:
727  * @page : a #AudioTagsPage.
728  * @file : a #ThunarxFileInfo
729  *
730  * Sets the #ThunarxFileInfo for this @page.
731  **/
732 void
733 audio_tags_page_set_file (AudioTagsPage   *page,
734                           ThunarxFileInfo *file)
735 {
736   g_return_if_fail (IS_AUDIO_TAGS_PAGE (page));
737   g_return_if_fail (file == NULL || THUNARX_IS_FILE_INFO (file));
738 
739   /* Check if we already use this file */
740   if (G_UNLIKELY (page->file == file))
741     return;
742 
743   /* Disconnect from the previous file (if any) */
744   if (G_LIKELY (page->file != NULL))
745   {
746     g_signal_handlers_disconnect_by_func (G_OBJECT (page->file), audio_tags_page_file_changed, page);
747     g_object_unref (G_OBJECT (page->file));
748   }
749 
750   /* Assign the value */
751   page->file = file;
752 
753   /* Connect to the new file (if any) */
754   if (G_LIKELY (file != NULL))
755     {
756       /* Take a reference on the info file */
757       g_object_ref (G_OBJECT (page->file));
758 
759       audio_tags_page_file_changed (file, page);
760 
761       g_signal_connect (G_OBJECT (file), "changed", G_CALLBACK (audio_tags_page_file_changed), page);
762     }
763 }
764 
765 
766 
767 /**
768  * audio_tags_page_get_taglib_file:
769  * @page : a #AudioTagsPage.
770  *
771  * Returns the current #TagLib_File
772  * for the @page.
773  *
774  * Return value: the taglib file associated with this property page.
775  **/
776 TagLib_File*
777 audio_tags_page_get_taglib_file (AudioTagsPage *page)
778 {
779   g_return_val_if_fail (IS_AUDIO_TAGS_PAGE (page), NULL);
780   return page->taglib_file;
781 }
782 
783 
784 
785 /**
786  * audio_tags_page_set_taglib_file:
787  * @page : a #AudioTagsPage.
788  * @file : a #TagLib_File
789  *
790  * Sets the #TagLib_File for this @page.
791  **/
792 void
793 audio_tags_page_set_taglib_file (AudioTagsPage   *page,
794                                  TagLib_File     *taglib_file)
795 {
796   g_return_if_fail (IS_AUDIO_TAGS_PAGE (page));
797 
798   /* Check if we already use this file */
799   if (G_UNLIKELY (page->taglib_file == taglib_file))
800     return;
801 
802   /* Freeing a file triggers a reload (taglib does that),
803    * so put it in the garbage collector */
804   if (G_LIKELY (page->taglib_file != NULL))
805     page->files_gc = g_slist_prepend (page->files_gc, page->taglib_file);
806 
807   /* Assign the value */
808   page->taglib_file = taglib_file;
809 
810   /* Connect to the new file (if any) */
811   if (G_LIKELY (taglib_file != NULL))
812       audio_tags_page_taglib_file_changed (taglib_file, page);
813 }
814 
815 
816 
817 static void
818 audio_tags_page_file_changed (ThunarxFileInfo *file,
819                               AudioTagsPage   *page)
820 {
821   g_return_if_fail (THUNARX_IS_FILE_INFO (file));
822   g_return_if_fail (IS_AUDIO_TAGS_PAGE (page));
823   g_return_if_fail (page->file == file);
824 
825   if (page->changed_idle == 0)
826       page->changed_idle = g_idle_add (audio_tags_page_load_tags, page);
827 }
828 
829 
830 
831 static void
832 audio_tags_page_taglib_file_changed (TagLib_File   *taglib_file,
833                                      AudioTagsPage *page)
834 {
835   TagLib_Tag *taglib_tag;
836 
837   /* Strings to be freed by taglib_tag_free_strings */
838   gchar      *title;
839   gchar      *artist;
840   gchar      *album;
841   gchar      *comment;
842   gchar      *genre;
843 
844   guint       track;
845   guint       year;
846 
847   g_return_if_fail (IS_AUDIO_TAGS_PAGE (page));
848   g_return_if_fail (page->taglib_file == taglib_file);
849   g_return_if_fail (taglib_file != NULL);
850 
851   /* Make page sensitive again */
852   gtk_widget_set_sensitive (GTK_WIDGET (page), FALSE);
853 
854   /* Load tag information */
855   taglib_tag = taglib_file_tag (taglib_file);
856 
857   if (G_LIKELY (taglib_tag != NULL))
858     {
859       /* Read values */
860       track = taglib_tag_track (taglib_tag);
861       title = taglib_tag_title (taglib_tag);
862       artist = taglib_tag_artist (taglib_tag);
863       album = taglib_tag_album (taglib_tag);
864       comment = taglib_tag_comment (taglib_tag);
865       genre = taglib_tag_genre (taglib_tag);
866       year = taglib_tag_year (taglib_tag);
867 
868       if (track == 0)
869         track = 1;
870 
871       if (year == 0)
872         year = 2006;
873 
874       /* Set page properties in order to sync with display */
875       g_object_set (G_OBJECT (page),
876                     "track", (gdouble)track,
877                     "year", (gdouble)year,
878                     "artist", artist,
879                     "title", title,
880                     "album", album,
881                     "comment", comment,
882                     "genre", genre,
883                     NULL);
884 
885       /* Free allocated strings */
886       taglib_tag_free_strings ();
887     }
888 
889   /* Make page sensitive again */
890   gtk_widget_set_sensitive (GTK_WIDGET (page), TRUE);
891 }
892 
893 
894 
895 static gboolean
896 audio_tags_page_activate (AudioTagsPage *page)
897 {
898   TagLib_Tag *tag;
899 
900   g_return_val_if_fail (page != NULL || IS_AUDIO_TAGS_PAGE (page), FALSE);
901   g_return_val_if_fail (page->file != NULL || THUNARX_IS_FILE_INFO (page->file), FALSE);
902   g_return_val_if_fail (page->taglib_file != NULL, FALSE);
903 
904   /* Get tag information */
905   tag = taglib_file_tag (page->taglib_file);
906 
907   if (G_LIKELY (tag != NULL))
908     {
909       /* Compare old to new values */
910       if (taglib_tag_track (tag) != page->track ||
911           taglib_tag_year (tag) != page->year ||
912           g_utf8_collate (taglib_tag_artist (tag), page->artist) ||
913           g_utf8_collate (taglib_tag_title (tag), page->title) ||
914           g_utf8_collate (taglib_tag_album (tag), page->album) ||
915           g_utf8_collate (taglib_tag_comment (tag), page->comment) ||
916           g_utf8_collate (taglib_tag_genre (tag), page->genre))
917         {
918           /* Make page insensitive */
919           gtk_widget_set_sensitive (GTK_WIDGET (page), FALSE);
920 
921           /* Store values */
922           taglib_tag_set_track (tag, page->track);
923           taglib_tag_set_year (tag, page->year);
924           taglib_tag_set_title (tag, page->title);
925           taglib_tag_set_artist (tag, page->artist);
926           taglib_tag_set_album (tag, page->album);
927           taglib_tag_set_comment (tag, page->comment);
928           taglib_tag_set_genre (tag, page->genre);
929 
930           /* Save file */
931           taglib_file_save (page->taglib_file);
932         }
933 
934       /* Free tag strings */
935       taglib_tag_free_strings ();
936     }
937 
938   return FALSE;
939 }
940 
941 
942 
943 static gboolean
944 audio_tags_page_info_activate (GSimpleAction      *action,
945                                GVariant           *parameter,
946                                AudioTagsPage      *page)
947 {
948   const TagLib_AudioProperties *properties;
949 
950   GtkWindow *window;
951   GtkWidget *dialog;
952   GtkWidget *grid;
953   GtkWidget *label;
954 
955   /* Audio information */
956   gchar         *length;
957   gchar         *bitrate;
958   gchar         *samplerate;
959   gchar         *channels;
960 
961   gchar         *mimetype;
962 
963   GFileInfo     *fileinfo;
964   const char    *filename;
965   gchar         *filesize;
966 
967   g_return_val_if_fail (page != NULL || IS_AUDIO_TAGS_PAGE (page), FALSE);
968   g_return_val_if_fail (page->file != NULL || THUNARX_IS_FILE_INFO (page->file), FALSE);
969   g_return_val_if_fail (page->taglib_file != NULL, FALSE);
970 
971   /* Determine parent window */
972   window = g_object_get_data (G_OBJECT (action), "window");
973 
974   /* Create dialog */
975   dialog = gtk_dialog_new_with_buttons (_("Audio Information"),
976                                         window,
977                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
978                                         _("_Close"),
979                                         GTK_RESPONSE_CLOSE,
980                                         NULL);
981 
982   /* Retrieve audio information */
983   properties = taglib_file_audioproperties (page->taglib_file);
984   length = g_strdup_printf (_("%d:%02d Minutes"), taglib_audioproperties_length (properties) / 60, taglib_audioproperties_length (properties) % 60);
985   bitrate = g_strdup_printf (_("%d KBit/s"), taglib_audioproperties_bitrate (properties));
986   samplerate = g_strdup_printf (_("%d Hz"), taglib_audioproperties_samplerate (properties));
987   channels = g_strdup_printf ("%d", taglib_audioproperties_channels (properties));
988 
989   /* Additional information */
990   mimetype = thunarx_file_info_get_mime_type (page->file);
991   fileinfo = thunarx_file_info_get_file_info (page->file);
992   filename = g_file_info_get_display_name (fileinfo);
993 #if GLIB_CHECK_VERSION (2, 30, 0)
994   filesize = g_format_size (g_file_info_get_size (fileinfo));
995 #else
996   filesize = g_format_size_for_display (g_file_info_get_size (fileinfo));
997 #endif
998 
999   /* Create layout grid */
1000   grid = gtk_grid_new ();
1001   gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
1002   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
1003   gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), grid);
1004   gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
1005   gtk_widget_show (grid);
1006 
1007   /* Filename label */
1008   label = gtk_label_new ("");
1009   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1010   gtk_label_set_markup (GTK_LABEL (label), _("<b>Filename:</b>"));
1011   gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
1012   gtk_widget_show (label);
1013 
1014   /* Filename display */
1015   label = gtk_label_new (filename);
1016   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1017   gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
1018   gtk_widget_show (label);
1019 
1020   /* Filesize label */
1021   label = gtk_label_new ("");
1022   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1023   gtk_label_set_markup (GTK_LABEL (label), _("<b>Filesize:</b>"));
1024   gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
1025   gtk_widget_show (label);
1026 
1027   /* Filesize display */
1028   label = gtk_label_new (filesize);
1029   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1030   gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
1031   gtk_widget_show (label);
1032 
1033   /* Mimetype label */
1034   label = gtk_label_new ("");
1035   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1036   gtk_label_set_markup (GTK_LABEL (label), _("<b>MIME Type:</b>"));
1037   gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
1038   gtk_widget_show (label);
1039 
1040   /* Mimetype display */
1041   label = gtk_label_new (mimetype);
1042   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1043   gtk_grid_attach (GTK_GRID (grid), label, 1, 2, 1, 1);
1044   gtk_widget_show (label);
1045 
1046   /* Bitrate label */
1047   label = gtk_label_new ("");
1048   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1049   gtk_label_set_markup (GTK_LABEL (label), _("<b>Bitrate:</b>"));
1050   gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 1, 1);
1051   gtk_widget_show (label);
1052 
1053   /* Bitrate display */
1054   label = gtk_label_new (bitrate);
1055   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1056   gtk_grid_attach (GTK_GRID (grid), label, 1, 3, 1, 1);
1057   gtk_widget_show (label);
1058 
1059   /* Samplerate label */
1060   label = gtk_label_new ("");
1061   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1062   gtk_label_set_markup (GTK_LABEL (label), _("<b>Samplerate:</b>"));
1063   gtk_grid_attach (GTK_GRID (grid), label, 0, 4, 1, 1);
1064   gtk_widget_show (label);
1065 
1066   /* Samplerate display */
1067   label = gtk_label_new (samplerate);
1068   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1069   gtk_grid_attach (GTK_GRID (grid), label, 1, 4, 1, 1);
1070   gtk_widget_show (label);
1071 
1072   /* Channels label */
1073   label = gtk_label_new ("");
1074   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1075   gtk_label_set_markup (GTK_LABEL (label), _("<b>Channels:</b>"));
1076   gtk_grid_attach (GTK_GRID (grid), label, 0, 5, 1, 1);
1077   gtk_widget_show (label);
1078 
1079   /* Channels display */
1080   label = gtk_label_new (channels);
1081   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1082   gtk_grid_attach (GTK_GRID (grid), label, 1, 5, 1, 1);
1083   gtk_widget_show (label);
1084 
1085   /* Length label */
1086   label = gtk_label_new ("");
1087   gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
1088   gtk_label_set_markup (GTK_LABEL (label), _("<b>Length:</b>"));
1089   gtk_grid_attach (GTK_GRID (grid), label, 0, 6, 1, 1);
1090   gtk_widget_show (label);
1091 
1092   /* Length display */
1093   label = gtk_label_new (length);
1094   gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
1095   gtk_grid_attach (GTK_GRID (grid), label, 1, 6, 1, 1);
1096   gtk_widget_show (label);
1097 
1098   /* Run dialog */
1099   gtk_dialog_run (GTK_DIALOG (dialog));
1100   gtk_widget_destroy (dialog);
1101 
1102   g_free (bitrate);
1103   g_free (samplerate);
1104   g_free (channels);
1105   g_free (length);
1106   g_free (filesize);
1107   g_free (mimetype);
1108 
1109   g_object_unref (fileinfo);
1110 
1111   return TRUE;
1112 }
1113 
1114 
1115 
1116 static gboolean
1117 audio_tags_page_load_tags (gpointer data)
1118 {
1119   AudioTagsPage *page = AUDIO_TAGS_PAGE (data);
1120   TagLib_File   *taglib_file;
1121   gchar         *uri;
1122   gchar         *filename;
1123 
1124   g_return_val_if_fail (page != NULL || IS_AUDIO_TAGS_PAGE (page), FALSE);
1125   g_return_val_if_fail (page->file != NULL || THUNARX_IS_FILE_INFO (page->file), FALSE);
1126 
1127   /* Determine filename */
1128   uri = thunarx_file_info_get_uri (page->file);
1129   filename = g_filename_from_uri (uri, NULL, NULL);
1130 
1131   /* Try to load the taglib file */
1132   taglib_file = taglib_file_new (filename);
1133 
1134   /* Set taglib file */
1135   if (G_LIKELY (taglib_file != NULL))
1136       audio_tags_page_set_taglib_file (page, taglib_file);
1137 
1138   /* Free strings */
1139   g_free (filename);
1140   g_free (uri);
1141 
1142   /* Reset timeout */
1143   page->changed_idle = 0;
1144 
1145   return FALSE;
1146 }
1147 
1148 
1149 
1150 /**
1151  * audio_tags_page_get_show_save_button:
1152  * @page : a #AudioTagsPage.
1153  *
1154  * Returns whether the @page contains a save button or not.
1155  *
1156  * Return value: whether a save button is shown in the page or not.
1157  **/
1158 gboolean
1159 audio_tags_page_get_show_save_button (AudioTagsPage *page)
1160 {
1161   g_return_val_if_fail (IS_AUDIO_TAGS_PAGE (page), FALSE);
1162   return page->save_button != NULL;
1163 }
1164 
1165 
1166 
1167 /**
1168  * audio_tags_page_set_show_save_button:
1169  * @page : a #AudioTagsPage.
1170  * @show : a #gboolean flag.
1171  *
1172  * Creates a save button on the page or removes it.
1173  **/
1174 void
1175 audio_tags_page_set_show_save_button (AudioTagsPage   *page,
1176                                       gboolean         show)
1177 {
1178   g_return_if_fail (IS_AUDIO_TAGS_PAGE (page));
1179   g_return_if_fail (page->grid != NULL || GTK_IS_GRID (page->grid));
1180 
1181   if (G_LIKELY (show))
1182     {
1183       /* Check if we already display the button */
1184       if (G_UNLIKELY (page->save_button != NULL))
1185         return;
1186 
1187       /* Info button */
1188       page->info_button = gtk_button_new_with_mnemonic (_("_Information"));
1189       gtk_widget_set_tooltip_text (page->info_button, _("Display more detailed information about this audio file."));
1190       gtk_grid_attach (GTK_GRID (page->grid), page->info_button, 2, 6, 1, 1);
1191       gtk_widget_show (page->info_button);
1192 
1193       /* Connect to info action */
1194       gtk_actionable_set_action_name (GTK_ACTIONABLE (page->info_button), "audio-tags-page-actions.info");
1195 
1196       /* Save button */
1197       page->save_button = gtk_button_new_with_mnemonic (_("_Save"));
1198       gtk_widget_set_tooltip_text (page->save_button, _("Save audio tags."));
1199       gtk_grid_attach (GTK_GRID (page->grid), page->save_button, 3, 6, 1, 1);
1200       gtk_widget_show (page->save_button);
1201 
1202       /* Connect to save action */
1203       gtk_actionable_set_action_name (GTK_ACTIONABLE (page->save_button), "audio-tags-page-actions.save");
1204     }
1205   else
1206     {
1207       /* Check if we already display the info button */
1208       if (G_LIKELY (page->info_button != NULL))
1209         gtk_widget_destroy (page->info_button);
1210 
1211       /* Check if we already display the button */
1212       if (G_LIKELY (page->save_button != NULL))
1213         gtk_widget_destroy (page->save_button);
1214 
1215       /* Reset the pointer */
1216       page->info_button = NULL;
1217       page->save_button = NULL;
1218     }
1219 }
1220