1 /*
2 * Copyright (c) 2005-2006 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 #include <libxfce4ui/libxfce4ui.h>
24
25 #include <libburn.h>
26
27 #include "xfburn-global.h"
28 #include "xfburn-utils.h"
29 #include "xfburn-progress-dialog.h"
30 #include "xfburn-device-box.h"
31 #include "xfburn-device-list.h"
32 #include "xfburn-udev-manager.h"
33 #include "xfburn-main.h"
34
35 #include "xfburn-blank-dialog.h"
36
37 #define XFBURN_BLANK_DIALOG_GET_PRIVATE(obj) (xfburn_blank_dialog_get_instance_private (obj))
38
39 #define XFBURN_BLANK_DIALOG_EJECT_DEFAULT TRUE
40
41 enum {
42 PROP_0,
43 PROP_EJECT,
44 };
45
46 typedef struct
47 {
48 GtkWidget *device_box;
49 GtkWidget *combo_type;
50 GtkWidget *button_blank;
51
52 GtkWidget *check_eject;
53 gboolean eject;
54 } XfburnBlankDialogPrivate;
55
56 /* FIXME: the 128MB comes from cdrskin, but why? Is this really complete? */
57 #define XFBURN_FORMAT_COMPLETE_SIZE 128*1024*1024
58
59 typedef enum {
60 XFBURN_BLANK_FAST, /* erase w/ fast flag */
61 XFBURN_BLANK_COMPLETE, /* erase, no flag */
62 XFBURN_FORMAT_FAST, /* DVD+RW sequential (0x13) to overwritable (0x14), zero size */
63 XFBURN_FORMAT_COMPLETE, /* DVD+RW sequential (0x13) to overwritable (0x14), 128MB, flag=1*/
64 XFBURN_DEFORMAT_FAST, /* same as fast blank */
65 XFBURN_DEFORMAT_COMPLETE, /* same as complete blank */
66 XFBURN_BLANK_MODE_LAST,
67 } XfburnBlankMode;
68
69 static char * blank_mode_names[] = {
70 N_("Quick Blank"),
71 N_("Full Blank (slow)"),
72 N_("Quick Format"),
73 N_("Full Format"),
74 N_("Quick Deformat"),
75 N_("Full Deformat (slow)"),
76 };
77
78 enum {
79 BLANK_COMBO_NAME_COLUMN,
80 BLANK_COMBO_MODE_COLUMN,
81 BLANK_COMBO_N_COLUMNS,
82 };
83
84 typedef struct {
85 GtkWidget *dialog_progress;
86 XfburnDevice *device;
87 XfburnBlankMode blank_mode;
88 gboolean eject;
89 } ThreadBlankParams;
90
91 /* internal prototypes */
92
93 static void xfburn_blank_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
94 static void xfburn_blank_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
95
96 static gboolean is_valid_blank_mode (XfburnDevice *device, XfburnBlankMode mode);
97 static void fill_combo_mode (XfburnBlankDialog *dialog);
98 //static GList * get_valid_blank_modes (XfburnDevice *device);
99 static XfburnBlankMode get_selected_mode (XfburnBlankDialogPrivate *priv);
100 static gboolean thread_blank_perform_blank (ThreadBlankParams * params, struct burn_drive_info *drive_info);
101 static void* thread_blank (ThreadBlankParams * params);
102 static void xfburn_blank_dialog_response_cb (XfburnBlankDialog * dialog, gint response_id, gpointer user_data);
103 static void cb_volume_changed (GtkWidget *device_box, gboolean device_changed, XfburnDevice *device, XfburnBlankDialog * dialog);
104
105 static XfceTitledDialogClass *parent_class = NULL;
106
107 G_DEFINE_TYPE_WITH_PRIVATE(XfburnBlankDialog, xfburn_blank_dialog, XFCE_TYPE_TITLED_DIALOG);
108
109 static void
xfburn_blank_dialog_class_init(XfburnBlankDialogClass * klass)110 xfburn_blank_dialog_class_init (XfburnBlankDialogClass * klass)
111 {
112 GObjectClass *object_class = G_OBJECT_CLASS (klass);
113
114 parent_class = g_type_class_peek_parent (klass);
115 object_class->set_property = xfburn_blank_dialog_set_property;
116 object_class->get_property = xfburn_blank_dialog_get_property;
117
118 g_object_class_install_property (object_class, PROP_EJECT,
119 g_param_spec_boolean ("eject", _("Eject the disc"),
120 _("Default value for eject checkbox"), XFBURN_BLANK_DIALOG_EJECT_DEFAULT, G_PARAM_READWRITE));
121 }
122
123 static void
xfburn_blank_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)124 xfburn_blank_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
125 {
126 XfburnBlankDialogPrivate *priv = XFBURN_BLANK_DIALOG_GET_PRIVATE (XFBURN_BLANK_DIALOG (object));
127
128 switch (prop_id) {
129 case PROP_EJECT:
130 g_value_set_boolean (value, priv->eject);
131 break;
132 default:
133 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
134 break;
135 }
136 }
137
138 static void
xfburn_blank_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)139 xfburn_blank_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
140 {
141 XfburnBlankDialogPrivate *priv = XFBURN_BLANK_DIALOG_GET_PRIVATE (XFBURN_BLANK_DIALOG (object));
142
143 switch (prop_id) {
144 case PROP_EJECT:
145 priv->eject = g_value_get_boolean (value);
146 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_eject), priv->eject);
147 break;
148 default:
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150 break;
151 }
152 }
153
154 static void
xfburn_blank_dialog_init(XfburnBlankDialog * obj)155 xfburn_blank_dialog_init (XfburnBlankDialog * obj)
156 {
157 XfburnBlankDialogPrivate *priv = XFBURN_BLANK_DIALOG_GET_PRIVATE (obj);
158 GtkBox *box = GTK_BOX (gtk_dialog_get_content_area((GTK_DIALOG (obj))));
159 GdkPixbuf *icon = NULL;
160 GtkWidget *frame;
161 GtkWidget *vbox;
162 GtkWidget *button;
163 gint x,y;
164
165 GtkListStore *store = NULL;
166 GtkCellRenderer *cell;
167
168 gtk_window_set_title (GTK_WINDOW (obj), _("Blank Disc"));
169 gtk_window_set_destroy_with_parent (GTK_WINDOW (obj), TRUE);
170
171 gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &x, &y);
172 icon = gtk_icon_theme_load_icon ( gtk_icon_theme_get_default(), "stock_xfburn-blank-cdrw", x, GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);
173
174 gtk_window_set_icon (GTK_WINDOW (obj), icon);
175 g_object_unref (icon);
176
177 /* devices list */
178 priv->device_box = xfburn_device_box_new (SHOW_CDRW_WRITERS | BLANK_MODE);
179 g_signal_connect (G_OBJECT (priv->device_box), "volume-changed", G_CALLBACK (cb_volume_changed), obj);
180 gtk_widget_show (priv->device_box);
181
182 frame = xfce_gtk_frame_box_new_with_content (_("Burning device"), priv->device_box);
183 gtk_widget_show (frame);
184 gtk_box_pack_start (box, frame, FALSE, FALSE, BORDER);
185
186 /* blank mode */
187 store = gtk_list_store_new (BLANK_COMBO_N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
188 priv->combo_type = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
189 g_object_unref (store);
190 cell = gtk_cell_renderer_text_new ();
191 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_type), cell, TRUE);
192 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->combo_type), cell, "text", BLANK_COMBO_NAME_COLUMN, NULL);
193 gtk_widget_show (priv->combo_type);
194
195 frame = xfce_gtk_frame_box_new_with_content (_("Blank mode"), priv->combo_type);
196 gtk_widget_show (frame);
197 gtk_box_pack_start (box, frame, FALSE, FALSE, BORDER);
198
199 /* options */
200 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
201 gtk_widget_show (vbox);
202
203 frame = xfce_gtk_frame_box_new_with_content (_("Options"), vbox);
204 gtk_widget_show (frame);
205 gtk_box_pack_start (box, frame, FALSE, FALSE, BORDER);
206
207 priv->check_eject = gtk_check_button_new_with_mnemonic (_("E_ject disk"));
208 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_eject), XFBURN_BLANK_DIALOG_EJECT_DEFAULT);
209 gtk_widget_show (priv->check_eject);
210 gtk_box_pack_start (GTK_BOX (vbox), priv->check_eject, FALSE, FALSE, BORDER);
211
212 /* action buttons */
213 button = gtk_button_new_with_mnemonic (_("_Cancel"));
214 gtk_widget_show (button);
215 gtk_dialog_add_action_widget (GTK_DIALOG (obj), button, GTK_RESPONSE_CANCEL);
216
217 button = xfce_gtk_button_new_mixed ("stock_xfburn-blank-cdrw", _("_Blank"));
218 gtk_widget_show (button);
219 gtk_dialog_add_action_widget (GTK_DIALOG (obj), button, GTK_RESPONSE_OK);
220 gtk_widget_set_can_default (button, TRUE);
221 gtk_widget_grab_focus (button);
222 gtk_widget_grab_default (button);
223 priv->button_blank = button;
224
225 g_signal_connect (G_OBJECT (obj), "response", G_CALLBACK (xfburn_blank_dialog_response_cb), obj);
226 fill_combo_mode (obj);
227 }
228
fill_combo_mode(XfburnBlankDialog * dialog)229 static void fill_combo_mode (XfburnBlankDialog *dialog)
230 {
231 XfburnBlankDialogPrivate *priv = XFBURN_BLANK_DIALOG_GET_PRIVATE (dialog);
232 XfburnBlankMode mode = XFBURN_BLANK_FAST;
233 GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_type));
234 int n = 0;
235
236 gtk_list_store_clear (GTK_LIST_STORE (model));
237
238 while (mode < XFBURN_BLANK_MODE_LAST) {
239 if (is_valid_blank_mode (NULL, mode)) {
240 GtkTreeIter iter;
241
242 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
243 gtk_list_store_set (GTK_LIST_STORE (model), &iter, BLANK_COMBO_NAME_COLUMN, _(blank_mode_names[mode]), BLANK_COMBO_MODE_COLUMN, mode, -1);
244 n++;
245 }
246 mode++;
247 }
248 gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_type), 0);
249 gtk_widget_set_sensitive (priv->button_blank, n > 0);
250 }
251
is_valid_blank_mode(XfburnDevice * device,XfburnBlankMode mode)252 static gboolean is_valid_blank_mode (XfburnDevice *device, XfburnBlankMode mode)
253 {
254 int profile_no = 0;
255 gboolean erasable = FALSE;
256 enum burn_disc_status disc_state;
257
258 XfburnDeviceList *devlist = xfburn_device_list_new ();
259
260 g_object_get (G_OBJECT (xfburn_device_list_get_current_device (devlist)), "profile-no", &profile_no, "erasable", &erasable, "disc-status", &disc_state, NULL);
261 g_object_unref (devlist);
262
263 if (profile_no == 0x13) {
264 /* in 0x14 no blanking is needed, we can only deformat */
265 if (mode == XFBURN_DEFORMAT_FAST || mode == XFBURN_DEFORMAT_COMPLETE)
266 return TRUE;
267 else
268 return FALSE;
269 }
270
271 if (profile_no == 0x14 && (mode == XFBURN_FORMAT_FAST || mode == XFBURN_FORMAT_COMPLETE))
272 return TRUE;
273
274 if (erasable && (disc_state != BURN_DISC_BLANK) && (mode == XFBURN_BLANK_FAST || mode == XFBURN_BLANK_COMPLETE))
275 return TRUE;
276
277 return FALSE;
278 }
279
280 /*
281 static GList * get_valid_blank_modes (XfburnDevice *device)
282 {
283 XfburnBlankMode mode = XFBURN_BLANK_FAST;
284 GList *modes = NULL;
285
286 while (mode < XFBURN_BLANK_MODE_LAST) {
287 if (is_valid_blank_mode (device, mode))
288 modes = g_list_append (modes, GINT_TO_POINTER (mode));
289 mode++;
290 }
291
292 return modes;
293 }
294 */
295
296 static gboolean
thread_blank_perform_blank(ThreadBlankParams * params,struct burn_drive_info * drive_info)297 thread_blank_perform_blank (ThreadBlankParams * params, struct burn_drive_info *drive_info)
298 {
299 GtkWidget *dialog_progress = params->dialog_progress;
300
301 struct burn_drive *drive;
302 enum burn_disc_status disc_state;
303 enum burn_drive_status drive_state;
304 struct burn_progress progress;
305
306 int ret;
307 gboolean error = FALSE;
308 int error_code;
309 char msg_text[BURN_MSGS_MESSAGE_LEN];
310 int os_errno;
311 char severity[80];
312 const char *final_status_text;
313 XfburnProgressDialogStatus final_status;
314 gchar *final_message = NULL;
315
316 drive = drive_info->drive;
317
318 while (burn_drive_get_status (drive, NULL) != BURN_DRIVE_IDLE) {
319 usleep (1001);
320 }
321
322 while ( (disc_state = burn_disc_get_status (drive)) == BURN_DISC_UNREADY)
323 usleep (1001);
324
325 switch (disc_state) {
326 case BURN_DISC_BLANK:
327 if (params->blank_mode == XFBURN_BLANK_FAST || params->blank_mode == XFBURN_BLANK_COMPLETE) {
328 /* blanking can only be performed on blank discs, format and deformat are allowed to be blank ones */
329 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("The inserted disc is already blank."));
330 return FALSE;
331 } // fall through
332 case BURN_DISC_FULL:
333 case BURN_DISC_APPENDABLE:
334 /* these ones we can blank */
335 xfburn_progress_dialog_set_status_with_text (XFBURN_PROGRESS_DIALOG (dialog_progress), XFBURN_PROGRESS_DIALOG_STATUS_RUNNING, _("Ready"));
336 break;
337 case BURN_DISC_EMPTY:
338 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("No disc detected in the drive."));
339 return FALSE;
340 default:
341 //xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("Cannot recognize drive and disc state."));
342 //return FALSE;
343 break;
344 }
345
346 if (!burn_disc_erasable (drive)) {
347 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("Disc is not erasable."));
348 return FALSE;
349 }
350
351 /* set us up to receive fatal errors */
352 ret = burn_msgs_set_severities ("ALL", "NEVER", "libburn");
353
354 if (ret <= 0)
355 g_warning ("Failed to set libburn message severities, burn errors might not get detected!");
356
357 switch (params->blank_mode) {
358 case XFBURN_BLANK_FAST:
359 //DBG ("blank_fast");
360 burn_disc_erase(drive, 1);
361 break;
362 case XFBURN_BLANK_COMPLETE:
363 //DBG ("blank_complete");
364 burn_disc_erase(drive, 0);
365 break;
366 case XFBURN_FORMAT_FAST:
367 //DBG ("format_fast");
368 burn_disc_format(drive, 0, 0);
369 break;
370 case XFBURN_FORMAT_COMPLETE:
371 //DBG ("format_complete");
372 burn_disc_format(drive, XFBURN_FORMAT_COMPLETE_SIZE, 1);
373 break;
374 case XFBURN_DEFORMAT_FAST:
375 //DBG ("deformat_fast");
376 burn_disc_erase(drive, 1);
377 break;
378 case XFBURN_DEFORMAT_COMPLETE:
379 //DBG ("deformat_complete");
380 burn_disc_erase(drive, 0);
381 break;
382 default:
383 g_error ("Invalid blank mode %d, this is a bug.", params->blank_mode);
384 }
385 sleep(1);
386
387 xfburn_progress_dialog_set_status_with_text (XFBURN_PROGRESS_DIALOG (dialog_progress), XFBURN_PROGRESS_DIALOG_STATUS_RUNNING, _("Blanking disc..."));
388
389 while ((drive_state = burn_drive_get_status (drive, &progress)) != BURN_DRIVE_IDLE) {
390 //DBG ("drive_state = %d", drive_state);
391 if(progress.sectors>0 && progress.sector>=0) {
392 gdouble percent = 1.0 + ((gdouble) progress.sector+1.0) / ((gdouble) progress.sectors) * 98.0;
393
394 xfburn_progress_dialog_set_progress_bar_fraction (XFBURN_PROGRESS_DIALOG (dialog_progress), percent);
395 }
396 usleep(500000);
397 }
398
399 /* check the libburn message queue for errors */
400 while ((ret = burn_msgs_obtain ("FAILURE", &error_code, msg_text, &os_errno, severity)) == 1) {
401 g_warning ("[%s] %d: %s (%d)", severity, error_code, msg_text, os_errno);
402 error = TRUE;
403 }
404 #ifdef DEBUG
405 while ((ret = burn_msgs_obtain ("ALL", &error_code, msg_text, &os_errno, severity)) == 1) {
406 g_warning ("[%s] %d: %s (%d)", severity, error_code, msg_text, os_errno);
407 }
408 #endif
409
410 if (ret < 0)
411 g_warning ("Fatal error while trying to retrieve libburn message!");
412
413 if (G_LIKELY (!error)) {
414 final_message = g_strdup_printf (_("Done"));
415 final_status = XFBURN_PROGRESS_DIALOG_STATUS_COMPLETED;
416 } else {
417 final_status_text = _("Failure");
418 final_status = XFBURN_PROGRESS_DIALOG_STATUS_FAILED;
419 final_message = g_strdup_printf ("%s: %s", final_status_text, msg_text);
420 }
421
422 xfburn_progress_dialog_set_status_with_text (XFBURN_PROGRESS_DIALOG (dialog_progress), final_status, final_message);
423 g_free (final_message);
424
425 return TRUE;
426 }
427
428 static void*
thread_blank(ThreadBlankParams * params)429 thread_blank (ThreadBlankParams * params)
430 {
431 struct burn_drive_info *drive_info = NULL;
432
433 if (!xfburn_device_grab (params->device, &drive_info)) {
434 xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (params->dialog_progress), _("Unable to grab the drive."));
435 } else {
436 thread_blank_perform_blank (params, drive_info);
437 xfburn_device_release (drive_info, params->eject);
438 }
439
440 g_free (params);
441
442 #ifdef HAVE_GUDEV
443 gdk_threads_enter ();
444 DBG ("blanking done!");
445 xfburn_udev_manager_send_volume_changed ();
446 gdk_threads_leave ();
447 #endif
448 return NULL;
449 }
450
451 static XfburnBlankMode
get_selected_mode(XfburnBlankDialogPrivate * priv)452 get_selected_mode (XfburnBlankDialogPrivate *priv)
453 {
454 GtkTreeModel *model;
455 GtkTreeIter iter;
456 XfburnBlankMode blank_mode;
457 gboolean ret;
458
459 model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_type));
460 ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->combo_type), &iter);
461 if (ret) {
462 gtk_tree_model_get (model, &iter, BLANK_COMBO_MODE_COLUMN, &blank_mode, -1);
463 } else {
464 g_warning("No blank mode selected, using default");
465 blank_mode = XFBURN_BLANK_FAST;
466 }
467 return blank_mode;
468 }
469
470 static void
xfburn_blank_dialog_response_cb(XfburnBlankDialog * dialog,gint response_id,gpointer user_data)471 xfburn_blank_dialog_response_cb (XfburnBlankDialog * dialog, gint response_id, gpointer user_data)
472 {
473 if (response_id == GTK_RESPONSE_OK) {
474 XfburnBlankDialogPrivate *priv = XFBURN_BLANK_DIALOG_GET_PRIVATE (dialog);
475 XfburnDevice *device;
476
477 GtkWidget *dialog_progress;
478 ThreadBlankParams *params = NULL;
479
480 device = xfburn_device_box_get_selected_device (XFBURN_DEVICE_BOX (priv->device_box));
481
482
483 dialog_progress = xfburn_progress_dialog_new (GTK_WINDOW (dialog));
484 g_object_set (dialog_progress, "animate", TRUE, NULL);
485
486 gtk_widget_hide (GTK_WIDGET (dialog));
487
488 gtk_widget_show (dialog_progress);
489
490 params = g_new0 (ThreadBlankParams, 1);
491 params->dialog_progress = dialog_progress;
492 params->device = device;
493 params->blank_mode = get_selected_mode (priv);
494 params->eject = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_eject));
495 g_thread_new ("xfburn_blank", (GThreadFunc) thread_blank, params);
496 } else {
497 xfburn_main_leave_window ();
498 }
499 }
500
501 static void
cb_volume_changed(GtkWidget * device_box,gboolean device_changed,XfburnDevice * device,XfburnBlankDialog * dialog)502 cb_volume_changed (GtkWidget *device_box, gboolean device_changed, XfburnDevice *device, XfburnBlankDialog * dialog)
503 {
504 fill_combo_mode (dialog);
505 }
506
507
508 /* public */
509 GtkWidget *
xfburn_blank_dialog_new(void)510 xfburn_blank_dialog_new (void)
511 {
512 GtkWidget *obj;
513
514 obj = GTK_WIDGET (g_object_new (XFBURN_TYPE_BLANK_DIALOG, NULL));
515 cb_volume_changed (NULL, TRUE, NULL, XFBURN_BLANK_DIALOG (obj));
516
517 xfburn_main_enter_window ();
518
519 return obj;
520 }
521
522 GtkWidget *
xfburn_blank_dialog_new_eject(gboolean eject)523 xfburn_blank_dialog_new_eject (gboolean eject)
524 {
525 GtkWidget *obj;
526
527 obj = xfburn_blank_dialog_new ();
528
529 g_object_set (G_OBJECT (obj), "eject", eject, NULL);
530
531 return obj;
532 }
533