1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* arch-tag: Implementation of playlist source recorder object
5
* Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
6
* Copyright (C) 2003 Colin Walters <walters@gnome.org>
7
* Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu>
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation; either version 2 of the License, or
12
* (at your option) any later version.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software
21
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
33
#include <glib/gprintf.h>
34
#include <glib/gi18n.h>
36
#include <libgnomevfs/gnome-vfs-uri.h>
37
#include <libgnomevfs/gnome-vfs-utils.h>
38
#include <glade/glade.h>
39
#include <nautilus-burn-drive.h>
40
#include <nautilus-burn-drive-selection.h>
42
#ifndef NAUTILUS_BURN_CHECK_VERSION
43
#define NAUTILUS_BURN_CHECK_VERSION(a,b,c) FALSE
46
#if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
47
#include <nautilus-burn.h>
50
#include "rb-file-helpers.h"
51
#include "rb-glade-helpers.h"
52
#include "rb-preferences.h"
53
#include "rb-dialog.h"
56
#include "rb-playlist-source.h"
57
#include "rb-playlist-source-recorder.h"
59
#include "eel-gconf-extensions.h"
64
extern char *mkdtemp (char *template);
67
/* NAUTILUS_BURN_DRIVE_SIZE_TO_TIME was added in 2.12 */
68
#ifndef NAUTILUS_BURN_DRIVE_SIZE_TO_TIME
69
#define nautilus_burn_drive_eject _nautilus_burn_drive_eject
70
#define nautilus_burn_drive_new_from_path _nautilus_burn_drive_new_from_path
71
#define nautilus_burn_drive_media_type_get_string _nautilus_burn_drive_media_type_get_string
73
/* NAUTILUS_BURN_DRIVE_SIZE_TO_TIME was added in 2.14 */
74
#ifndef HAVE_BURN_DRIVE_UNREF
75
#define nautilus_burn_drive_unref nautilus_burn_drive_free
76
#define nautilus_burn_drive_ref nautilus_burn_drive_copy
79
#include "rb-recorder.h"
81
#define CONF_STATE_BURN_SPEED CONF_PREFIX "/state/burn_speed"
83
#define AUDIO_BYTERATE (2 * 44100 * 2)
84
#define MAX_PLAYLIST_DURATION 6000
86
static void rb_playlist_source_recorder_class_init (RBPlaylistSourceRecorderClass *klass);
87
static void rb_playlist_source_recorder_init (RBPlaylistSourceRecorder *source);
88
static void rb_playlist_source_recorder_dispose (GObject *object);
89
static void rb_playlist_source_recorder_finalize (GObject *object);
91
void rb_playlist_source_recorder_device_changed_cb (NautilusBurnDriveSelection *selection,
92
const char *device_path,
93
RBPlaylistSourceRecorder *source);
95
GtkWidget * rb_playlist_source_recorder_device_menu_create (void);
105
struct RBPlaylistSourceRecorderPrivate
113
RBRecorder *recorder;
121
GtkWidget *multiple_copies_checkbutton;
122
GtkWidget *cancel_button;
123
GtkWidget *burn_button;
124
GtkWidget *message_label;
125
GtkWidget *progress_label;
127
GtkWidget *device_menu;
128
GtkWidget *speed_combobox;
129
GtkWidget *options_box;
130
GtkWidget *progress_frame;
133
gboolean already_converted;
134
gboolean handling_error;
135
gboolean confirmed_exit;
145
} RBPlaylistSourceRecorderSignalType;
147
static guint rb_playlist_source_recorder_signals [LAST_SIGNAL] = { 0 };
149
#define RB_PLAYLIST_SOURCE_RECORDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYLIST_SOURCE_RECORDER, RBPlaylistSourceRecorderPrivate))
151
G_DEFINE_TYPE(RBPlaylistSourceRecorder, rb_playlist_source_recorder, GTK_TYPE_DIALOG)
154
rb_playlist_source_recorder_style_set (GtkWidget *widget,
155
GtkStyle *previous_style)
159
if (GTK_WIDGET_CLASS (rb_playlist_source_recorder_parent_class)->style_set)
160
GTK_WIDGET_CLASS (rb_playlist_source_recorder_parent_class)->style_set (widget, previous_style);
162
dialog = GTK_DIALOG (widget);
164
gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
165
gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
167
gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
168
gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
172
rb_playlist_source_recorder_class_init (RBPlaylistSourceRecorderClass *klass)
174
GObjectClass *object_class = G_OBJECT_CLASS (klass);
175
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
177
widget_class->style_set = rb_playlist_source_recorder_style_set;
179
object_class->dispose = rb_playlist_source_recorder_dispose;
180
object_class->finalize = rb_playlist_source_recorder_finalize;
182
rb_playlist_source_recorder_signals [NAME_CHANGED] =
183
g_signal_new ("name_changed",
184
G_OBJECT_CLASS_TYPE (object_class),
188
g_cclosure_marshal_VOID__STRING,
193
rb_playlist_source_recorder_signals [FILE_ADDED] =
194
g_signal_new ("file_added",
195
G_OBJECT_CLASS_TYPE (object_class),
199
g_cclosure_marshal_VOID__STRING,
204
g_type_class_add_private (klass, sizeof (RBPlaylistSourceRecorderPrivate));
208
rb_playlist_source_recorder_device_menu_create (void)
213
widget = nautilus_burn_drive_selection_new ();
214
g_object_set (widget, "file-image", FALSE, NULL);
215
g_object_set (widget, "show-recorders-only", TRUE, NULL);
217
value = eel_gconf_get_string (CONF_STATE_BURN_DEVICE);
219
nautilus_burn_drive_selection_set_device (NAUTILUS_BURN_DRIVE_SELECTION (widget),
224
gtk_widget_show (widget);
229
static const NautilusBurnDrive *
230
lookup_current_recorder (RBPlaylistSourceRecorder *source)
232
const NautilusBurnDrive *drive;
234
drive = nautilus_burn_drive_selection_get_drive (NAUTILUS_BURN_DRIVE_SELECTION (source->priv->device_menu));
240
get_speed_selection (GtkWidget *combobox)
248
if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &iter))
251
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox));
252
gtk_tree_model_get (model, &iter, 1, &speed, -1);
257
#ifndef HAVE_BURN_DRIVE_GET_WRITE_SPEEDS
259
#define nautilus_burn_drive_get_write_speeds get_write_speeds
262
get_write_speeds (NautilusBurnDrive *drive)
266
static int *write_speeds = NULL;
268
if (write_speeds == NULL) {
269
max_speed = drive->max_speed_write;
270
write_speeds = g_new0 (int, max_speed + 1);
272
for (i = 0; i < max_speed; i++) {
273
write_speeds [i] = max_speed - i;
277
return (const int*)write_speeds;
282
update_speed_combobox (RBPlaylistSourceRecorder *source)
288
int default_speed_index;
289
const NautilusBurnDrive *drive;
293
/* Find active recorder: */
294
drive = lookup_current_recorder (source);
296
/* add speed items: */
297
combobox = source->priv->speed_combobox;
298
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox));
299
gtk_list_store_clear (GTK_LIST_STORE (model));
301
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
302
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
303
0, _("Maximum possible"),
307
default_speed = eel_gconf_get_integer (CONF_STATE_BURN_SPEED);
308
default_speed_index = -1;
312
const int *write_speeds;
314
write_speeds = nautilus_burn_drive_get_write_speeds ((NautilusBurnDrive *)drive);
316
for (i = 0; write_speeds [i] > 0; i++) {
318
#ifdef NAUTILUS_BURN_DRIVE_CD_SPEED
319
name = g_strdup_printf ("%d \303\227", (int)NAUTILUS_BURN_DRIVE_CD_SPEED (write_speeds [i]));
321
name = g_strdup_printf ("%d \303\227", write_speeds [i]);
324
if (write_speeds [i] == default_speed) {
325
default_speed_index = i + 1;
328
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
329
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
338
/* Disable speed if no items in list */
339
gtk_widget_set_sensitive (combobox, i > 0);
341
/* if the default speed was not set then make it the minimum for safety */
342
if (default_speed_index == -1) {
343
default_speed_index = i;
346
/* for now assume equivalence between index in comboxbox and speed */
347
gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), default_speed_index);
351
rb_playlist_source_recorder_device_changed_cb (NautilusBurnDriveSelection *selection,
352
const char *device_path,
353
RBPlaylistSourceRecorder *source)
358
eel_gconf_set_string (CONF_STATE_BURN_DEVICE, device_path);
360
update_speed_combobox (source);
364
set_media_device (RBPlaylistSourceRecorder *source)
369
device = nautilus_burn_drive_selection_get_device (NAUTILUS_BURN_DRIVE_SELECTION (source->priv->device_menu));
371
if (device && strcmp (device, "")) {
372
rb_recorder_set_device (source->priv->recorder, device, &error);
374
g_warning (_("Invalid writer device: %s"), device);
375
/* ignore and let rb_recorder try to find a default */
381
set_message_text (RBPlaylistSourceRecorder *source,
389
va_start (args, message);
390
g_vasprintf (&text, message, args);
393
markup = g_strdup_printf ("%s", text);
395
gtk_label_set_text (GTK_LABEL (source->priv->message_label), markup);
401
response_idle_cb (RBPlaylistSourceRecorder *source)
403
GDK_THREADS_ENTER ();
404
gtk_dialog_response (GTK_DIALOG (source),
405
GTK_RESPONSE_CANCEL);
406
GDK_THREADS_LEAVE ();
412
error_dialog_response_cb (GtkWidget *dialog,
416
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
418
gtk_widget_destroy (GTK_WIDGET (dialog));
420
g_idle_add ((GSourceFunc)response_idle_cb, source);
426
error_dialog (RBPlaylistSourceRecorder *source,
428
const char *secondary,
435
va_start (args, secondary);
436
g_vasprintf (&text, secondary, args);
439
dialog = gtk_message_dialog_new (GTK_WINDOW (source),
440
GTK_DIALOG_DESTROY_WITH_PARENT,
445
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
448
gtk_window_set_title (GTK_WINDOW (dialog), "");
450
gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
452
g_signal_connect (dialog,
454
G_CALLBACK (error_dialog_response_cb),
457
gtk_widget_show (dialog);
462
/* Adapted from totem_time_to_string_text */
464
time_to_string_text (long time)
466
char *secs, *mins, *hours, *string;
471
min = (time % (60 * 60)) / 60;
472
time = time - (min * 60);
473
hour = time / (60 * 60);
475
hours = g_strdup_printf (ngettext ("%d hour", "%d hours", hour), hour);
477
mins = g_strdup_printf (ngettext ("%d minute",
478
"%d minutes", min), min);
480
secs = g_strdup_printf (ngettext ("%d second",
481
"%d seconds", sec), sec);
484
/* hour:minutes:seconds */
485
string = g_strdup_printf (_("%s %s %s"), hours, mins, secs);
486
} else if (min > 0) {
487
/* minutes:seconds */
488
string = g_strdup_printf (_("%s %s"), mins, secs);
489
} else if (sec > 0) {
491
string = g_strdup_printf (_("%s"), secs);
494
string = g_strdup (_("0 seconds"));
505
progress_set_time (GtkWidget *progress,
512
remaining = time_to_string_text (seconds);
513
text = g_strdup_printf (_("About %s left"), remaining);
516
text = g_strdup (" ");
519
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), text);
524
progress_set_fraction (GtkWidget *progress,
527
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), fraction);
530
#ifndef NAUTILUS_BURN_DRIVE_SIZE_TO_TIME
531
/* copied from nautilus-burn-drive 2.12 */
533
_nautilus_burn_drive_eject (NautilusBurnDrive *drive)
538
g_return_val_if_fail (drive != NULL, FALSE);
540
if (drive->device == NULL)
543
cmd = g_strdup_printf ("eject %s", drive->device);
544
res = g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL);
547
/* delay a bit to make sure eject finishes */
552
/* copied from nautilus-burn-drive 2.12 */
553
static NautilusBurnDrive *
554
_nautilus_burn_drive_new_from_path (const char *device)
557
NautilusBurnDrive *drive;
559
drives = nautilus_burn_drive_get_list (FALSE, FALSE);
563
for (l = drives; l != NULL; l = l->next) {
564
NautilusBurnDrive *d = l->data;
565
if (g_str_equal (device, d->device)) {
566
drive = nautilus_burn_drive_ref (d);
570
g_list_foreach (drives, (GFunc)nautilus_burn_drive_unref, NULL);
571
g_list_free (drives);
576
/* copied from nautilus-burn-drive 2.12 */
578
_nautilus_burn_drive_media_type_get_string (NautilusBurnMediaType type)
581
case NAUTILUS_BURN_MEDIA_TYPE_BUSY:
582
return _("Could not determine media type because CD drive is busy");
583
case NAUTILUS_BURN_MEDIA_TYPE_ERROR:
584
return _("Couldn't open media");
585
case NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN:
586
return _("Unknown Media");
587
case NAUTILUS_BURN_MEDIA_TYPE_CD:
588
return _("Commercial CD or Audio CD");
589
case NAUTILUS_BURN_MEDIA_TYPE_CDR:
591
case NAUTILUS_BURN_MEDIA_TYPE_CDRW:
593
case NAUTILUS_BURN_MEDIA_TYPE_DVD:
595
case NAUTILUS_BURN_MEDIA_TYPE_DVDR:
596
return _("DVD-R, or DVD-RAM");
597
case NAUTILUS_BURN_MEDIA_TYPE_DVDRW:
599
case NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM:
601
case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R:
603
case NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW:
609
return _("Broken media type");
611
#endif /* NAUTILUS_BURN_DRIVE_SIZE_TO_TIME */
614
burn_cd (RBPlaylistSourceRecorder *source,
622
set_media_device (source);
624
set_message_text (source, _("Writing audio to CD"));
626
speed = get_speed_selection (source->priv->speed_combobox);
628
progress_set_fraction (source->priv->progress, 0);
629
progress_set_time (source->priv->progress, -1);
631
source->priv->burning = TRUE;
632
res = rb_recorder_burn (source->priv->recorder, speed, error);
633
source->priv->burning = FALSE;
635
if (res == RB_RECORDER_RESULT_FINISHED) {
636
NautilusBurnDrive *drive;
638
const char *finished_msg;
640
finished_msg = _("Finished creating audio CD.");
642
rb_shell_hidden_notify (source->priv->shell, 0, finished_msg, source->priv->cd_icon, "", FALSE);
644
/* save the write speed that was used */
645
eel_gconf_set_integer (CONF_STATE_BURN_SPEED, speed);
647
/* Always eject the disk after writing. Too many drives mess up otherwise */
648
drive = (NautilusBurnDrive *)lookup_current_recorder (source);
649
nautilus_burn_drive_eject (drive);
651
do_another = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source->priv->multiple_copies_checkbutton));
653
set_message_text (source, finished_msg);
654
gtk_widget_set_sensitive (GTK_WIDGET (source), FALSE);
655
g_idle_add ((GSourceFunc)response_idle_cb, source);
658
set_message_text (source, _("Finished creating audio CD.\nCreate another copy?"));
659
} else if (res == RB_RECORDER_RESULT_ERROR) {
660
set_message_text (source, _("Writing failed. Try again?"));
662
set_message_text (source, _("Writing cancelled. Try again?"));
665
progress_set_fraction (source->priv->progress, 0);
666
progress_set_time (source->priv->progress, -1);
668
gtk_widget_set_sensitive (GTK_WIDGET (source->priv->burn_button), TRUE);
669
gtk_widget_set_sensitive (GTK_WIDGET (source->priv->options_box), TRUE);
675
get_song_description (RBRecorderSong *song)
679
if (song->artist && song->title)
680
desc = g_strdup_printf ("%s - %s", song->title, song->artist);
681
else if (song->title)
682
desc = g_strdup (song->title);
683
else if (song->artist)
684
desc = g_strdup (song->artist);
690
write_file (RBPlaylistSourceRecorder *source,
693
RBRecorderSong *song = source->priv->current->data;
697
gtk_widget_set_sensitive (source->priv->progress_frame, TRUE);
699
cdtext = get_song_description (song);
701
markup = g_markup_printf_escaped ("<i>Converting '%s'</i>", cdtext);
702
gtk_label_set_markup (GTK_LABEL (source->priv->progress_label), markup);
705
rb_recorder_open (source->priv->recorder, song->uri, cdtext, error);
709
if (error && *error) {
713
rb_recorder_write (source->priv->recorder, error);
714
if (error && *error) {
720
burn_cd_idle (RBPlaylistSourceRecorder *source)
722
GError *error = NULL;
725
GDK_THREADS_ENTER ();
727
res = burn_cd (source, &error);
729
error_dialog (source,
730
_("Audio recording error"),
732
g_error_free (error);
735
GDK_THREADS_LEAVE ();
740
eos_cb (RBRecorder *recorder,
743
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
744
GError *error = NULL;
746
rb_debug ("Caught eos!");
748
rb_recorder_close (source->priv->recorder, NULL);
750
gtk_label_set_text (GTK_LABEL (source->priv->progress_label), "");
752
if (source->priv->current->next) {
754
source->priv->current = source->priv->current->next;
756
write_file (source, &error);
758
error_dialog (source,
759
_("Audio Conversion Error"),
761
g_error_free (error);
765
if (source->priv->timer) {
766
g_timer_destroy (source->priv->timer);
767
source->priv->timer = NULL;
770
source->priv->already_converted = TRUE;
772
g_idle_add ((GSourceFunc)burn_cd_idle, source);
777
rb_playlist_source_recorder_error (RBPlaylistSourceRecorder *source,
781
if (source->priv->handling_error) {
782
rb_debug ("Ignoring error: %s", error->message);
786
rb_debug ("Error: %s", error->message);
788
error_dialog (source,
789
_("Recording error"),
792
source->priv->handling_error = FALSE;
793
rb_debug ("Exiting error hander");
797
error_cb (GObject *object,
801
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
803
if (source->priv->handling_error)
806
source->priv->handling_error = TRUE;
808
rb_playlist_source_recorder_error (source, error);
812
track_progress_changed_cb (GObject *object,
817
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
818
double album_fraction;
831
for (l = source->priv->songs; l; l = l->next) {
832
RBRecorderSong *song = l->data;
834
if (song == source->priv->current->data) {
836
song_length = song->duration;
838
total += song->duration;
841
position = prev_total + song_length * fraction;
842
if (! source->priv->timer) {
843
source->priv->timer = g_timer_new ();
844
source->priv->start_pos = position;
847
album_fraction = (float)position / (float)total;
849
elapsed = g_timer_elapsed (source->priv->timer, NULL);
851
rate = (double)(position - source->priv->start_pos) / elapsed;
854
album_secs = ceil ((total - position) / rate);
858
progress_set_time (source->priv->progress, album_secs);
859
progress_set_fraction (source->priv->progress, album_fraction);
862
interrupt_burn_dialog_response_cb (GtkDialog *dialog,
866
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
868
if (response_id == GTK_RESPONSE_ACCEPT) {
869
if (source->priv->burning) {
870
rb_recorder_burn_cancel (source->priv->recorder);
872
source->priv->confirmed_exit = TRUE;
873
gtk_dialog_response (GTK_DIALOG (source),
874
GTK_RESPONSE_CANCEL);
877
source->priv->confirmed_exit = FALSE;
880
gtk_widget_destroy (GTK_WIDGET (dialog));
884
response_cb (GtkDialog *dialog,
887
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (dialog);
888
GError *error = NULL;
890
/* Act only on response IDs we recognize */
891
if (!(response_id == GTK_RESPONSE_ACCEPT
892
|| response_id == GTK_RESPONSE_CANCEL
893
|| response_id == GTK_RESPONSE_DELETE_EVENT)) {
894
g_signal_stop_emission_by_name (dialog, "response");
898
if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_DELETE_EVENT) {
899
if (source->priv->burning
900
&& !source->priv->confirmed_exit) {
901
GtkWidget *interrupt_dialog;
903
source->priv->confirmed_exit = FALSE;
905
interrupt_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
906
GTK_DIALOG_DESTROY_WITH_PARENT,
907
GTK_MESSAGE_QUESTION,
909
_("Do you wish to interrupt writing this disc?"));
911
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (interrupt_dialog),
912
_("This may result in an unusable disc."));
914
gtk_window_set_title (GTK_WINDOW (interrupt_dialog), "");
916
gtk_container_set_border_width (GTK_CONTAINER (interrupt_dialog), 6);
918
gtk_dialog_add_buttons (GTK_DIALOG (interrupt_dialog),
919
_("_Cancel"), GTK_RESPONSE_CANCEL,
920
_("_Interrupt"), GTK_RESPONSE_ACCEPT,
923
gtk_dialog_set_default_response (GTK_DIALOG (interrupt_dialog),
924
GTK_RESPONSE_CANCEL);
926
g_signal_connect (interrupt_dialog,
928
G_CALLBACK (interrupt_burn_dialog_response_cb),
931
gtk_widget_show (interrupt_dialog);
933
g_signal_stop_emission_by_name (dialog, "response");
938
if (response_id == GTK_RESPONSE_ACCEPT) {
939
rb_playlist_source_recorder_start (source, &error);
941
error_dialog (source,
942
_("Could not create audio CD"),
944
g_error_free (error);
946
g_signal_stop_emission_by_name (dialog, "response");
951
insert_media_request_cb (RBRecorder *recorder,
953
gboolean can_rewrite,
957
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
964
msg = N_("Please make sure another application is not using the drive.");
965
title = N_("Drive is busy");
966
} else if (is_reload && can_rewrite) {
967
msg = N_("Please put a rewritable or blank CD in the drive.");
968
title = N_("Insert a rewritable or blank CD");
969
} else if (is_reload && !can_rewrite) {
970
msg = N_("Please put a blank CD in the drive.");
971
title = N_("Insert a blank CD");
972
} else if (can_rewrite) {
973
msg = N_("Please replace the disc in the drive with a rewritable or blank CD.");
974
title = N_("Reload a rewritable or blank CD");
976
msg = N_("Please replace the disc in the drive with a blank CD.");
977
title = N_("Reload a blank CD");
980
GDK_THREADS_ENTER ();
981
dialog = gtk_message_dialog_new (GTK_WINDOW (source),
982
GTK_DIALOG_DESTROY_WITH_PARENT,
984
GTK_BUTTONS_OK_CANCEL,
987
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), msg);
989
gtk_window_set_title (GTK_WINDOW (dialog), "");
991
gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
993
res = gtk_dialog_run (GTK_DIALOG (dialog));
995
gtk_widget_destroy (dialog);
996
GDK_THREADS_LEAVE ();
998
if (res == GTK_RESPONSE_CANCEL)
1005
burn_progress_changed_cb (RBRecorder *recorder,
1010
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
1012
progress_set_fraction (source->priv->progress, fraction);
1013
progress_set_time (source->priv->progress, secs);
1017
burn_action_changed_cb (RBRecorder *recorder,
1018
RBRecorderAction action,
1021
RBPlaylistSourceRecorder *source = RB_PLAYLIST_SOURCE_RECORDER (data);
1027
case RB_RECORDER_ACTION_FILE_CONVERTING:
1028
text = N_("Converting audio tracks");
1030
case RB_RECORDER_ACTION_DISC_PREPARING_WRITE:
1031
text = N_("Preparing to write CD");
1033
case RB_RECORDER_ACTION_DISC_WRITING:
1034
text = N_("Writing CD");
1036
case RB_RECORDER_ACTION_DISC_FIXATING:
1037
text = N_("Finishing write");
1039
case RB_RECORDER_ACTION_DISC_BLANKING:
1040
text = N_("Erasing CD");
1043
g_warning (_("Unhandled action in burn_action_changed_cb"));
1047
set_message_text (source, text);
1051
ask_rewrite_disc (RBPlaylistSourceRecorder *source,
1058
NautilusBurnMediaType type;
1060
NautilusBurnDrive *drive;
1062
#if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
1063
drive = nautilus_burn_drive_monitor_get_drive_for_device (nautilus_burn_get_drive_monitor (),
1066
drive = nautilus_burn_drive_new_from_path (device);
1069
type = nautilus_burn_drive_get_media_type (drive);
1071
msg = g_strdup_printf (_("This %s appears to have information already recorded on it."),
1072
nautilus_burn_drive_media_type_get_string (type));
1074
dialog = gtk_message_dialog_new (GTK_WINDOW (source),
1075
GTK_DIALOG_DESTROY_WITH_PARENT,
1076
GTK_MESSAGE_WARNING,
1078
"%s", _("Erase information on this disc?"));
1080
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), msg);
1083
gtk_window_set_title (GTK_WINDOW (dialog), "");
1085
image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON);
1086
gtk_widget_show (image);
1087
button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Try Another"), RB_RECORDER_RESPONSE_RETRY);
1088
g_object_set (button, "image", image, NULL);
1090
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, RB_RECORDER_RESPONSE_CANCEL);
1092
image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON);
1093
gtk_widget_show (image);
1094
button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Erase Disc"), RB_RECORDER_RESPONSE_ERASE);
1095
g_object_set (button, "image", image, NULL);
1097
gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1098
RB_RECORDER_RESPONSE_CANCEL);
1100
res = gtk_dialog_run (GTK_DIALOG (dialog));
1102
gtk_widget_destroy (dialog);
1104
if (res == RB_RECORDER_RESPONSE_RETRY) {
1105
nautilus_burn_drive_eject (drive);
1108
nautilus_burn_drive_unref (drive);
1114
warn_data_loss_cb (RBRecorder *recorder,
1115
RBPlaylistSourceRecorder *source)
1120
device = rb_recorder_get_device (recorder, NULL);
1121
GDK_THREADS_ENTER();
1122
res = ask_rewrite_disc (source, device);
1123
GDK_THREADS_LEAVE();
1131
setup_speed_combobox (GtkWidget *combobox)
1133
GtkCellRenderer *cell;
1134
GtkListStore *store;
1136
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
1138
gtk_combo_box_set_model (GTK_COMBO_BOX (combobox),
1139
GTK_TREE_MODEL (store));
1140
g_object_unref (store);
1142
cell = gtk_cell_renderer_text_new ();
1143
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), cell, TRUE);
1144
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), cell,
1150
delete_event_handler (GtkWidget *widget,
1154
/* emit response signal */
1155
gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
1157
/* Do the destroy by default */
1162
rb_playlist_source_recorder_init (RBPlaylistSourceRecorder *source)
1165
GError *error = NULL;
1169
PangoAttrList *pattrlist;
1170
PangoAttribute *attr;
1172
g_signal_connect (GTK_DIALOG (source),
1174
G_CALLBACK (delete_event_handler),
1177
source->priv = RB_PLAYLIST_SOURCE_RECORDER_GET_PRIVATE (source);
1179
gtk_window_set_resizable (GTK_WINDOW (source), FALSE);
1180
gtk_dialog_set_has_separator (GTK_DIALOG (source), FALSE);
1181
source->priv->cancel_button = gtk_dialog_add_button (GTK_DIALOG (source),
1182
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1184
source->priv->burn_button = gtk_button_new ();
1185
GTK_WIDGET_SET_FLAGS (source->priv->burn_button, GTK_CAN_DEFAULT);
1187
widget = gtk_alignment_new (0.5, 0.5, 0, 0);
1188
gtk_container_add (GTK_CONTAINER (source->priv->burn_button), widget);
1189
gtk_widget_show (widget);
1190
hbox = gtk_hbox_new (FALSE, 6);
1191
gtk_container_add (GTK_CONTAINER (widget), hbox);
1192
gtk_widget_show (hbox);
1193
widget = gtk_image_new_from_stock (GTK_STOCK_CDROM, GTK_ICON_SIZE_BUTTON);
1194
source->priv->cd_icon = widget;
1195
g_object_ref (source->priv->cd_icon);
1196
gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1197
gtk_widget_show (widget);
1198
widget = gtk_label_new_with_mnemonic (_("C_reate"));
1199
gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1200
gtk_widget_show (widget);
1201
gtk_dialog_add_action_widget (GTK_DIALOG (source),
1202
source->priv->burn_button,
1203
GTK_RESPONSE_ACCEPT);
1204
gtk_widget_show (source->priv->burn_button);
1206
gtk_dialog_set_default_response (GTK_DIALOG (source), GTK_RESPONSE_ACCEPT);
1208
xml = rb_glade_xml_new ("recorder.glade",
1212
source->priv->vbox = glade_xml_get_widget (xml, "recorder_vbox");
1214
source->priv->message_label = glade_xml_get_widget (xml, "message_label");
1215
source->priv->progress_label = glade_xml_get_widget (xml, "progress_label");
1217
source->priv->progress = glade_xml_get_widget (xml, "progress");
1218
gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (source->priv->progress), PANGO_ELLIPSIZE_END);
1219
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (source->priv->progress), " ");
1220
gtk_widget_set_size_request (source->priv->progress, 400, -1);
1222
source->priv->progress_frame = glade_xml_get_widget (xml, "progress_frame");
1224
source->priv->options_box = glade_xml_get_widget (xml, "options_box");
1225
source->priv->device_menu = glade_xml_get_widget (xml, "device_menu");
1226
source->priv->multiple_copies_checkbutton = glade_xml_get_widget (xml, "multiple_copies_checkbutton");
1228
source->priv->speed_combobox = glade_xml_get_widget (xml, "speed_combobox");
1229
setup_speed_combobox (source->priv->speed_combobox);
1231
widget = glade_xml_get_widget (xml, "device_label");
1232
gtk_label_set_mnemonic_widget (GTK_LABEL (widget), source->priv->device_menu);
1234
rb_glade_boldify_label (xml, "progress_frame_label");
1235
rb_glade_boldify_label (xml, "options_expander_label");
1237
pattrlist = pango_attr_list_new ();
1238
attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
1239
attr->start_index = 0;
1240
attr->end_index = G_MAXINT;
1241
pango_attr_list_insert (pattrlist, attr);
1243
font_size = pango_font_description_get_size (GTK_WIDGET (source->priv->message_label)->style->font_desc);
1244
attr = pango_attr_size_new (font_size * 1.2);
1245
attr->start_index = 0;
1246
attr->end_index = G_MAXINT;
1247
pango_attr_list_insert (pattrlist, attr);
1249
gtk_label_set_attributes (GTK_LABEL (source->priv->message_label), pattrlist);
1251
pango_attr_list_unref (pattrlist);
1253
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (source)->vbox),
1256
gtk_widget_show_all (source->priv->vbox);
1258
source->priv->recorder = rb_recorder_new (&error);
1261
char *msg = g_strdup_printf (_("Failed to create the recorder: %s"),
1263
g_error_free (error);
1264
dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
1268
gtk_dialog_run (GTK_DIALOG (dialog));
1273
update_speed_combobox (source);
1274
g_signal_connect (source->priv->device_menu, "device-changed",
1275
G_CALLBACK (rb_playlist_source_recorder_device_changed_cb),
1278
g_signal_connect_object (G_OBJECT (source->priv->recorder), "eos",
1279
G_CALLBACK (eos_cb), source, 0);
1281
g_signal_connect_object (G_OBJECT (source->priv->recorder), "error",
1282
G_CALLBACK (error_cb), source, 0);
1284
g_signal_connect_object (G_OBJECT (source->priv->recorder), "action-changed",
1285
G_CALLBACK (burn_action_changed_cb), source, 0);
1287
g_signal_connect_object (G_OBJECT (source->priv->recorder), "track-progress-changed",
1288
G_CALLBACK (track_progress_changed_cb), source, 0);
1290
g_signal_connect_object (G_OBJECT (source->priv->recorder), "insert-media-request",
1291
G_CALLBACK (insert_media_request_cb), source, 0);
1293
g_signal_connect_object (G_OBJECT (source->priv->recorder), "warn-data-loss",
1294
G_CALLBACK (warn_data_loss_cb), source, 0);
1296
g_signal_connect_object (G_OBJECT (source->priv->recorder), "burn-progress-changed",
1297
G_CALLBACK (burn_progress_changed_cb), source, 0);
1299
g_signal_connect (GTK_DIALOG (source), "response",
1300
G_CALLBACK (response_cb), NULL);
1303
static RBRecorderSong *
1304
recorder_song_new ()
1306
RBRecorderSong *song = g_new0 (RBRecorderSong, 1);
1311
recorder_song_free (RBRecorderSong *song)
1313
g_return_if_fail (song != NULL);
1315
g_free (song->title);
1321
free_song_list (GSList *songs)
1325
for (l = songs; l; l = l->next) {
1326
recorder_song_free ((RBRecorderSong *)l->data);
1329
g_slist_free (songs);
1334
rb_playlist_source_recorder_dispose (GObject *object)
1336
RBPlaylistSourceRecorder *source;
1338
g_return_if_fail (object != NULL);
1339
g_return_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (object));
1341
source = RB_PLAYLIST_SOURCE_RECORDER (object);
1343
g_return_if_fail (source->priv != NULL);
1345
if (source->priv->shell != NULL) {
1346
g_object_unref (source->priv->shell);
1347
source->priv->shell = NULL;
1350
if (source->priv->cd_icon != NULL) {
1351
g_object_unref (source->priv->cd_icon);
1352
source->priv->cd_icon = NULL;
1355
if (source->priv->recorder != NULL) {
1356
g_object_unref (source->priv->recorder);
1357
source->priv->recorder = NULL;
1360
G_OBJECT_CLASS (rb_playlist_source_recorder_parent_class)->dispose (object);
1364
rb_playlist_source_recorder_finalize (GObject *object)
1366
RBPlaylistSourceRecorder *source;
1368
g_return_if_fail (object != NULL);
1369
g_return_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (object));
1371
source = RB_PLAYLIST_SOURCE_RECORDER (object);
1373
g_return_if_fail (source->priv != NULL);
1375
rb_debug ("Finalize source recorder");
1377
g_free (source->priv->name);
1378
source->priv->name = NULL;
1380
free_song_list (source->priv->songs);
1382
if (source->priv->tmp_dir) {
1383
if (rmdir (source->priv->tmp_dir) < 0)
1384
g_warning (_("Could not remove temporary directory '%s': %s"),
1385
source->priv->tmp_dir,
1386
g_strerror (errno));
1387
g_free (source->priv->tmp_dir);
1388
source->priv->tmp_dir = NULL;
1391
G_OBJECT_CLASS (rb_playlist_source_recorder_parent_class)->finalize (object);
1395
rb_playlist_source_recorder_new (GtkWidget *parent,
1400
RBPlaylistSourceRecorder *source;
1402
result = g_object_new (RB_TYPE_PLAYLIST_SOURCE_RECORDER,
1403
"title", _("Create Audio CD"),
1406
source = RB_PLAYLIST_SOURCE_RECORDER (result);
1408
source->priv->parent = gtk_widget_get_toplevel (parent);
1410
gtk_window_set_transient_for (GTK_WINDOW (source),
1411
GTK_WINDOW (source->priv->parent));
1412
gtk_window_set_destroy_with_parent (GTK_WINDOW (source), TRUE);
1415
source->priv->shell = g_object_ref (shell);
1418
source->priv->name = g_strdup (name);
1420
set_message_text (source, _("Create audio CD from '%s'?"), name);
1427
rb_playlist_source_recorder_set_name (RBPlaylistSourceRecorder *source,
1431
g_return_if_fail (source != NULL);
1432
g_return_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (source));
1434
g_return_if_fail (name != NULL);
1436
g_free (source->priv->name);
1437
source->priv->name = g_strdup (name);
1439
g_signal_emit (G_OBJECT (source),
1440
rb_playlist_source_recorder_signals [NAME_CHANGED],
1446
rb_playlist_source_recorder_add_from_model (RBPlaylistSourceRecorder *source,
1447
GtkTreeModel *model,
1448
RBPlaylistSourceIterFunc func,
1453
GSList *songs = NULL;
1457
g_return_val_if_fail (source != NULL, FALSE);
1458
g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (source), FALSE);
1460
g_return_val_if_fail (model != NULL, FALSE);
1462
if (! gtk_tree_model_get_iter_first (model, &iter)) {
1465
RB_RECORDER_ERROR_GENERAL,
1466
_("Unable to build an audio track list."));
1471
/* Make sure we can use all of the songs before we
1472
modify the song list */
1475
RBRecorderSong *song = recorder_song_new ();
1478
res = func (model, &iter, &song->uri, &song->artist, &song->title, &song->duration);
1483
RB_RECORDER_ERROR_GENERAL,
1484
_("Unable to build an audio track list."));
1488
length += song->duration;
1489
if (length > MAX_PLAYLIST_DURATION) {
1493
RB_RECORDER_ERROR_GENERAL,
1494
_("This playlist is too long to write to an audio CD."));
1498
songs = g_slist_append (songs, song);
1499
} while (gtk_tree_model_iter_next (model, &iter));
1502
free_song_list (songs);
1507
/* now that we've checked all the songs, add them to the song list */
1508
for (l = songs; l; l = l->next) {
1509
RBRecorderSong *song = l->data;
1511
source->priv->songs = g_slist_append (source->priv->songs, song);
1513
g_signal_emit (G_OBJECT (source),
1514
rb_playlist_source_recorder_signals [FILE_ADDED],
1523
rb_playlist_source_recorder_get_total_duration (RBPlaylistSourceRecorder *source)
1528
for (l = source->priv->songs; l; l = l->next) {
1529
RBRecorderSong *song = l->data;
1530
length += song->duration;
1537
rb_playlist_source_recorder_estimate_total_size (RBPlaylistSourceRecorder *source)
1541
length = rb_playlist_source_recorder_get_total_duration (source);
1543
return length * AUDIO_BYTERATE;
1547
check_dir_has_space (const char *path,
1548
guint64 bytes_needed)
1550
GnomeVFSResult result = GNOME_VFS_OK;
1551
GnomeVFSURI *dir_uri = NULL;
1552
GnomeVFSFileSize free_bytes = 0;
1554
if (!g_file_test (path, G_FILE_TEST_IS_DIR))
1557
dir_uri = gnome_vfs_uri_new (path);
1558
if (dir_uri == NULL) {
1559
g_warning (_("Cannot get free space at %s"), path);
1563
result = gnome_vfs_get_volume_free_space (dir_uri, &free_bytes);
1565
gnome_vfs_uri_unref (dir_uri);
1567
if (result != GNOME_VFS_OK) {
1568
g_warning (_("Cannot get free space at %s"), path);
1572
if (bytes_needed >= free_bytes)
1579
find_tmp_dir (RBPlaylistSourceRecorder *source,
1580
guint64 bytes_needed,
1585
g_return_val_if_fail (source != NULL, NULL);
1586
g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (source), NULL);
1588
/* Use a configurable temporary directory? */
1590
if (path && strcmp (path, "")
1591
&& check_dir_has_space (path, bytes_needed))
1593
else if (g_get_tmp_dir () &&
1594
check_dir_has_space (g_get_tmp_dir (), bytes_needed))
1595
return g_strdup (g_get_tmp_dir ());
1596
else if (g_get_home_dir () &&
1597
check_dir_has_space (g_get_home_dir (), bytes_needed))
1598
return g_strdup (g_get_home_dir ());
1604
check_tmp_dir (RBPlaylistSourceRecorder *source,
1610
guint64 bytes_needed;
1612
g_return_val_if_fail (source != NULL, FALSE);
1613
g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (source), FALSE);
1615
bytes_needed = rb_playlist_source_recorder_estimate_total_size (source);
1617
path = find_tmp_dir (source, bytes_needed, error);
1621
template = g_build_filename (path, "rb-burn-tmp-XXXXXX", NULL);
1622
subdir = mkdtemp (template);
1627
g_free (source->priv->tmp_dir);
1628
source->priv->tmp_dir = subdir;
1629
rb_recorder_set_tmp_dir (source->priv->recorder,
1630
source->priv->tmp_dir,
1633
if (error && *error)
1640
check_media_length (RBPlaylistSourceRecorder *source,
1643
gint64 duration = rb_playlist_source_recorder_get_total_duration (source);
1644
char *message = NULL;
1645
gint64 media_duration;
1646
char *duration_string;
1648
media_duration = rb_recorder_get_media_length (source->priv->recorder, NULL);
1649
duration_string = g_strdup_printf ("%" G_GINT64_FORMAT, duration / 60);
1651
/* Only check if the playlist is greater than 74 minutes */
1652
if ((media_duration < 0) && (duration > 4440)) {
1653
message = g_strdup_printf (_("This playlist is %s minutes long. "
1654
"This exceeds the length of a standard audio CD. "
1655
"If the destination media is larger than a standard audio CD "
1656
"please insert it in the drive and try again."),
1660
g_free (duration_string);
1663
error_dialog (source,
1664
_("Playlist too long"),
1675
rb_playlist_source_recorder_start (RBPlaylistSourceRecorder *source,
1678
g_return_if_fail (source != NULL);
1679
g_return_if_fail (RB_IS_PLAYLIST_SOURCE_RECORDER (source));
1681
source->priv->current = source->priv->songs;
1683
gtk_widget_set_sensitive (source->priv->burn_button, FALSE);
1684
gtk_widget_set_sensitive (source->priv->options_box, FALSE);
1686
if (source->priv->already_converted) {
1687
g_idle_add ((GSourceFunc)burn_cd_idle, source);
1691
set_media_device (source);
1693
is_ok = check_media_length (source, error);
1698
is_ok = check_tmp_dir (source, error);
1700
guint64 mib_needed = rb_playlist_source_recorder_estimate_total_size (source) / 1048576;
1701
char *mib_needed_string = g_strdup_printf ("%" G_GUINT64_FORMAT, mib_needed);
1703
error_dialog (source,
1704
_("Could not find temporary space!"),
1705
_("Could not find enough temporary space to convert audio tracks. %s MiB required."),
1707
g_free (mib_needed_string);
1712
write_file (source, error);