1 /*
2 * Copyright (c) 2005-2007 Jean-François Wauthy (pollux@xfce.org)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif /* !HAVE_CONFIG_H */
22
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30
31 #include <errno.h>
32
33 #include "xfburn-global.h"
34 #include "xfburn-utils.h"
35 #include "xfburn-settings.h"
36 #include "xfburn-settings.h"
37
38 #include "xfburn-device-box.h"
39 #include "xfburn-burn-audio-cd-composition-dialog.h"
40 #include "xfburn-progress-dialog.h"
41 #include "xfburn-perform-burn.h"
42 #include "xfburn-audio-composition.h"
43 #include "xfburn-transcoder.h"
44
45 #define XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG_GET_PRIVATE(obj) (xfburn_burn_audio_cd_composition_dialog_get_instance_private (XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG (obj)))
46
47 typedef struct
48 {
49 GSList *track_list;
50
51 GtkWidget *frame_device;
52 GtkWidget *device_box;
53 GtkWidget *combo_mode;
54
55 GtkWidget *entry;
56 GtkWidget *check_eject;
57 GtkWidget *check_burnfree;
58 GtkWidget *check_dummy;
59 GtkWidget *button_proceed;
60
61 gint response;
62 } XfburnBurnAudioCdCompositionDialogPrivate;
63
64 enum {
65 PROP_0,
66 PROP_TRACK_LIST,
67 };
68
69 /* prototypes */
70 static GObject * xfburn_burn_audio_cd_composition_dialog_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties);
71 static void xfburn_burn_audio_cd_composition_dialog_finalize (GObject * object);
72
73 static void xfburn_burn_audio_cd_composition_dialog_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
74 static void xfburn_burn_audio_cd_composition_dialog_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
75
76 static void cb_volume_changed (GtkWidget *device_box, gboolean device_changed, XfburnDevice *device, XfburnBurnAudioCdCompositionDialog * dialog);
77 static void cb_dialog_response (XfburnBurnAudioCdCompositionDialog * dialog, gint response_id,
78 XfburnBurnAudioCdCompositionDialogPrivate * priv);
79
80 /* globals */
81 static XfceTitledDialogClass *parent_class = NULL;
82
83 G_DEFINE_TYPE_WITH_PRIVATE(XfburnBurnAudioCdCompositionDialog, xfburn_burn_audio_cd_composition_dialog, XFCE_TYPE_TITLED_DIALOG);
84
85 static void
xfburn_burn_audio_cd_composition_dialog_class_init(XfburnBurnAudioCdCompositionDialogClass * klass)86 xfburn_burn_audio_cd_composition_dialog_class_init (XfburnBurnAudioCdCompositionDialogClass * klass)
87 {
88 GObjectClass *object_class = G_OBJECT_CLASS (klass);
89
90 parent_class = g_type_class_peek_parent (klass);
91
92 // object_class->constructor = xfburn_burn_audio_cd_composition_dialog_constructor;
93 object_class->finalize = xfburn_burn_audio_cd_composition_dialog_finalize;
94 object_class->get_property = xfburn_burn_audio_cd_composition_dialog_get_property;
95 object_class->set_property = xfburn_burn_audio_cd_composition_dialog_set_property;
96
97 /* properties */
98 g_object_class_install_property (object_class, PROP_TRACK_LIST,
99 g_param_spec_pointer ("track-list", "Track List", "Track List", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
100 }
101
102 static void
xfburn_burn_audio_cd_composition_dialog_init(XfburnBurnAudioCdCompositionDialog * obj)103 xfburn_burn_audio_cd_composition_dialog_init(XfburnBurnAudioCdCompositionDialog *obj)
104 {
105 XfburnBurnAudioCdCompositionDialogPrivate *priv = XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG_GET_PRIVATE(obj);
106
107 GdkPixbuf *icon = NULL;
108 GtkBox *box;
109 GtkWidget *frame;
110 GtkWidget *vbox;
111 GtkWidget *align;
112 GtkWidget *button;
113 //const char *comp_name;
114
115 box = GTK_BOX (gtk_dialog_get_content_area((GTK_DIALOG (obj))));
116
117 gtk_window_set_title (GTK_WINDOW (obj), _("Burn Composition"));
118 gtk_window_set_destroy_with_parent (GTK_WINDOW (obj), TRUE);
119 icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stock_xfburn", GTK_ICON_SIZE_DIALOG, 0, NULL);
120 gtk_window_set_icon (GTK_WINDOW (obj), icon);
121 g_object_unref (icon);
122
123 /* burning devices list */
124 priv->device_box = xfburn_device_box_new (SHOW_CD_WRITERS | SHOW_CDRW_WRITERS |
125 SHOW_SPEED_SELECTION | SHOW_MODE_SELECTION);
126 g_signal_connect (G_OBJECT (priv->device_box), "volume-changed", G_CALLBACK (cb_volume_changed), obj);
127 gtk_widget_show (priv->device_box);
128
129 priv->frame_device = xfce_gtk_frame_box_new_with_content (_("Burning device"), priv->device_box);
130 gtk_widget_show (priv->frame_device);
131 gtk_box_pack_start (box, priv->frame_device, FALSE, FALSE, BORDER);
132
133 /* composition name */
134 /*
135 comp_name = iso_image_get_volume_id (priv->image);
136 if (strcmp (comp_name, _(DATA_COMPOSITION_DEFAULT_NAME)) == 0) {
137 GtkWidget *label;
138 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
139 gtk_widget_show (vbox);
140
141 frame = xfce_gtk_frame_box_new_with_content (_("Composition name"), vbox);
142 gtk_widget_show (frame);
143 gtk_box_pack_start (box, frame, FALSE, FALSE, BORDER);
144
145 label = gtk_label_new (NULL);
146 gtk_label_set_markup (GTK_LABEL (label), _("<small>Would you like to change the default composition name?</small>"));
147 gtk_widget_show (label);
148 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, BORDER/2);
149
150 priv->entry = gtk_entry_new ();
151 gtk_entry_set_text (GTK_ENTRY (priv->entry), comp_name);
152 gtk_box_pack_start (GTK_BOX (vbox), priv->entry, FALSE, FALSE, BORDER);
153 gtk_widget_show (priv->entry);
154 } else {
155 priv->entry = NULL;
156 }
157 */
158
159 /* options */
160 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
161 gtk_widget_show (vbox);
162
163 frame = xfce_gtk_frame_box_new_with_content (_("Options"), vbox);
164 gtk_widget_show (frame);
165 gtk_box_pack_start (box, frame, FALSE, FALSE, BORDER);
166
167 priv->check_eject = gtk_check_button_new_with_mnemonic (_("E_ject disk"));
168 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_eject), TRUE);
169 gtk_widget_show (priv->check_eject);
170 gtk_box_pack_start (GTK_BOX (vbox), priv->check_eject, FALSE, FALSE, BORDER);
171
172 priv->check_dummy = gtk_check_button_new_with_mnemonic (_("_Dummy write"));
173 gtk_widget_show (priv->check_dummy);
174 gtk_box_pack_start (GTK_BOX (vbox), priv->check_dummy, FALSE, FALSE, BORDER);
175
176 priv->check_burnfree = gtk_check_button_new_with_mnemonic (_("Burn_Free"));
177 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_burnfree), TRUE);
178 gtk_widget_show (priv->check_burnfree);
179 gtk_box_pack_start (GTK_BOX (vbox), priv->check_burnfree, FALSE, FALSE, BORDER);
180
181 /*
182 align = gtk_alignment_new (0, 0, 0, 0);
183 gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, BORDER * 4, 0);
184 gtk_widget_show (align);
185 gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
186 */
187 /* action buttons */
188 button = gtk_button_new_with_mnemonic (_("_Cancel"));
189 gtk_widget_show (button);
190 gtk_dialog_add_action_widget (GTK_DIALOG (obj), button, GTK_RESPONSE_CANCEL);
191
192 priv->button_proceed = button = xfce_gtk_button_new_mixed ("stock_xfburn", _("_Burn Composition"));
193
194 gtk_widget_show (button);
195 gtk_dialog_add_action_widget (GTK_DIALOG (obj), button, GTK_RESPONSE_OK);
196 //gtk_box_pack_start (GTK_BOX (GTK_DIALOG(obj)->action_area), button, TRUE, TRUE, 0);
197 gtk_widget_set_can_default (button, TRUE);
198 gtk_widget_grab_focus (button);
199 gtk_widget_grab_default (button);
200
201 cb_volume_changed (priv->device_box, TRUE, xfburn_device_box_get_selected_device (XFBURN_DEVICE_BOX (priv->device_box)), obj);
202 g_signal_connect (G_OBJECT (obj), "response", G_CALLBACK (cb_dialog_response), priv);
203 }
204
205 static void
xfburn_burn_audio_cd_composition_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)206 xfburn_burn_audio_cd_composition_dialog_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
207 {
208 XfburnBurnAudioCdCompositionDialogPrivate *priv = XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG_GET_PRIVATE (object);
209
210 switch (prop_id) {
211 case PROP_TRACK_LIST:
212 g_value_set_pointer (value, priv->track_list);
213 break;
214 default:
215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216 break;
217 }
218 }
219
220 static void
xfburn_burn_audio_cd_composition_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)221 xfburn_burn_audio_cd_composition_dialog_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
222 {
223 XfburnBurnAudioCdCompositionDialogPrivate *priv = XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG_GET_PRIVATE (object);
224
225 switch (prop_id) {
226 case PROP_TRACK_LIST:
227 priv->track_list= g_value_get_pointer (value);
228 break;
229 default:
230 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231 break;
232 }
233 }
234
235 static void
xfburn_burn_audio_cd_composition_dialog_finalize(GObject * object)236 xfburn_burn_audio_cd_composition_dialog_finalize (GObject * object)
237 {
238 //XfburnBurnAudioCdCompositionDialogPrivate *priv = XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG_GET_PRIVATE (object);
239
240 G_OBJECT_CLASS (parent_class)->finalize (object);
241 }
242
243 /* internals */
244 static void
cb_volume_changed(GtkWidget * device_box,gboolean device_changed,XfburnDevice * device,XfburnBurnAudioCdCompositionDialog * dialog)245 cb_volume_changed (GtkWidget *device_box, gboolean device_changed, XfburnDevice *device, XfburnBurnAudioCdCompositionDialog * dialog)
246 {
247 XfburnBurnAudioCdCompositionDialogPrivate *priv = XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG_GET_PRIVATE (dialog);
248 gboolean valid_disc;
249
250 g_object_get (G_OBJECT (priv->device_box), "valid", &valid_disc, NULL);
251
252 gtk_widget_set_sensitive (priv->button_proceed, valid_disc);
253 }
254
255 typedef struct {
256 GtkWidget *dialog_progress;
257 XfburnDevice *device;
258 GSList *tracks;
259 gint speed;
260 XfburnWriteMode write_mode;
261 gboolean eject;
262 gboolean dummy;
263 gboolean burnfree;
264 } ThreadBurnCompositionParams;
265
266 static void
thread_burn_prep_and_burn(ThreadBurnCompositionParams * params,struct burn_drive * drive,struct burn_disc * disc,struct burn_session * session,int n_tracks,int track_sectors[],struct burn_track ** tracks,struct burn_source ** srcs)267 thread_burn_prep_and_burn (ThreadBurnCompositionParams * params, struct burn_drive *drive,
268 struct burn_disc *disc, struct burn_session *session, int n_tracks,
269 int track_sectors[], struct burn_track **tracks, struct burn_source **srcs)
270 {
271 GtkWidget *dialog_progress = params->dialog_progress;
272
273 struct burn_write_opts * burn_options;
274 gint ret,i;
275
276 ret = burn_disc_add_session (disc, session, BURN_POS_END);
277 if (ret == 0) {
278 g_warning ("Unable to create disc object");
279 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("A problem with the burn backend occurred."));
280 return;
281 }
282
283 //DBG ("Adding %d tracks to the session", n_tracks);
284 for (i=0; i<n_tracks; i++) {
285 //DBG ("Track %d has %d sectors", i, track_sectors[i]);
286 burn_session_add_track (session, tracks[i], BURN_POS_END);
287 }
288
289 burn_options = burn_write_opts_new (drive);
290 burn_write_opts_set_perform_opc (burn_options, 0);
291 burn_write_opts_set_multi (burn_options, 0);
292 burn_write_opts_set_simulate(burn_options, params->dummy ? 1 : 0);
293 burn_write_opts_set_underrun_proof (burn_options, params->burnfree ? 1 : 0);
294
295 if (!xfburn_set_write_mode (burn_options, params->write_mode, disc, WRITE_MODE_SAO)) {
296 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("The write mode is not supported currently."));
297 } else {
298
299 DBG ("Set speed to %d kb/s", params->speed);
300 burn_drive_set_speed (drive, 0, params->speed);
301 burn_drive_set_buffer_waiting (drive, 1, -1, -1, -1, 75, 95);
302
303 xfburn_perform_burn_write (dialog_progress, drive, params->write_mode, burn_options, AUDIO_BYTES_PER_SECTOR, disc, srcs, track_sectors);
304 }
305
306 burn_write_opts_free (burn_options);
307 }
308
309 static void*
thread_burn_composition(ThreadBurnCompositionParams * params)310 thread_burn_composition (ThreadBurnCompositionParams * params)
311 {
312 GtkWidget *dialog_progress = params->dialog_progress;
313
314 struct burn_disc *disc;
315 struct burn_session *session;
316 struct burn_track **tracks;
317 struct burn_source **srcs;
318 int n_tracks;
319 int i,j;
320 GSList *track_list;
321 int *track_sectors;
322 gboolean abort_burn = FALSE;
323 XfburnTranscoder *trans;
324 GError *error = NULL;
325
326 struct burn_drive_info *drive_info = NULL;
327
328 disc = burn_disc_create ();
329 session = burn_session_create ();
330
331 n_tracks = g_slist_length (params->tracks);
332 track_sectors = g_new (int, n_tracks);
333 tracks = g_new (struct burn_track *, n_tracks);
334 srcs = g_new (struct burn_source *, n_tracks);
335 trans = xfburn_transcoder_get_global ();
336
337 track_list = params->tracks;
338 for (i=0; i<n_tracks; i++) {
339 XfburnAudioTrack *atrack = track_list->data;
340
341 tracks[i] = xfburn_transcoder_create_burn_track (trans, atrack, &error);
342 if (tracks[i] == NULL) {
343 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), error->message);
344 g_error_free (error);
345 abort_burn = TRUE;
346 break;
347 }
348 srcs[i] = atrack->src;
349
350 track_sectors[i] = atrack->sectors;
351
352 track_list = g_slist_next (track_list);
353 }
354
355 if (!abort_burn) {
356 if (!xfburn_transcoder_prepare (trans, &error)) {
357 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), error->message);
358 g_error_free (error);
359 abort_burn = TRUE;
360 }
361 }
362
363 if (!abort_burn) {
364 if (!xfburn_device_grab (params->device, &drive_info)) {
365 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("Unable to grab the drive."));
366 } else {
367 thread_burn_prep_and_burn (params, drive_info->drive, disc, session, n_tracks, track_sectors, tracks, srcs);
368 xfburn_device_release (drive_info, params->eject);
369 }
370 }
371
372 xfburn_transcoder_finish (trans);
373
374 for (j=0; j<i; j++) {
375 burn_track_free (tracks[j]);
376 }
377 g_free (srcs);
378 g_free (tracks);
379 g_free (track_sectors);
380
381 g_object_unref (trans);
382
383 burn_session_free (session);
384 burn_disc_free (disc);
385
386 /* FIXME: free track_list here? */
387 g_free (params);
388 return NULL;
389 }
390
391 static void
cb_dialog_response(XfburnBurnAudioCdCompositionDialog * dialog,gint response_id,XfburnBurnAudioCdCompositionDialogPrivate * priv)392 cb_dialog_response (XfburnBurnAudioCdCompositionDialog * dialog, gint response_id, XfburnBurnAudioCdCompositionDialogPrivate * priv)
393 {
394 if (response_id == GTK_RESPONSE_OK) {
395 GtkWidget *dialog_progress;
396
397 ThreadBurnCompositionParams *params = NULL;
398 XfburnDevice *device;
399 gint speed;
400 XfburnWriteMode write_mode;
401 //struct burn_source * src_fifo = NULL;
402
403 /* If the name was the default, update the image volume id and volset id */
404 /*
405 if (priv->entry != NULL) {
406 const gchar * comp_name = gtk_entry_get_text (GTK_ENTRY (priv->entry));
407 iso_image_set_volume_id (priv->image, comp_name);
408 iso_image_set_volset_id (priv->image, comp_name);
409 }
410 */
411
412 dialog_progress = xfburn_progress_dialog_new (GTK_WINDOW (dialog));
413 gtk_window_set_transient_for (GTK_WINDOW (dialog_progress), gtk_window_get_transient_for (GTK_WINDOW (dialog)));
414 gtk_widget_hide (GTK_WIDGET (dialog));
415
416 gtk_widget_show (dialog_progress);
417
418 device = xfburn_device_box_get_selected_device (XFBURN_DEVICE_BOX (priv->device_box));
419 speed = xfburn_device_box_get_speed (XFBURN_DEVICE_BOX (priv->device_box));
420 /* cdrskin burns audio with SAO, but we assume auto knows that */
421 write_mode = WRITE_MODE_AUTO;
422
423 /* burn composition */
424 params = g_new0 (ThreadBurnCompositionParams, 1);
425 params->dialog_progress = dialog_progress;
426 params->device = device;
427 params->tracks = priv->track_list;
428 params->speed = speed;
429 params->write_mode = write_mode;
430 params->eject = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_eject));
431 params->dummy = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_dummy));
432 params->burnfree = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_burnfree));
433 g_thread_new ("audio_cd_burn", (GThreadFunc) thread_burn_composition, params);
434 }
435 }
436
437 /* public */
438 GtkWidget *
xfburn_burn_audio_cd_composition_dialog_new(GSList * track_list)439 xfburn_burn_audio_cd_composition_dialog_new (GSList *track_list)
440 {
441 XfburnBurnAudioCdCompositionDialog *obj;
442
443 obj = XFBURN_BURN_AUDIO_CD_COMPOSITION_DIALOG (g_object_new (XFBURN_TYPE_BURN_AUDIO_CD_COMPOSITION_DIALOG, "track-list", track_list, NULL));
444
445 return GTK_WIDGET (obj);
446 }
447