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