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