1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Libbrasero-burn
4  * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5  *
6  * Libbrasero-burn is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * The Libbrasero-burn authors hereby grant permission for non-GPL compatible
12  * GStreamer plugins to be used and distributed together with GStreamer
13  * and Libbrasero-burn. This permission is above and beyond the permissions granted
14  * by the GPL license by which Libbrasero-burn is covered. If you modify this code
15  * you may extend this exception to your version of the code, but you are not
16  * obligated to do so. If you do not wish to do so, delete this exception
17  * statement from your version.
18  *
19  * Libbrasero-burn is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to:
26  * 	The Free Software Foundation, Inc.,
27  * 	51 Franklin Street, Fifth Floor
28  * 	Boston, MA  02110-1301, USA.
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #  include <config.h>
33 #endif
34 
35 #include <string.h>
36 
37 #include <glib.h>
38 #include <glib-object.h>
39 #include <glib/gi18n-lib.h>
40 
41 #include <gtk/gtk.h>
42 
43 #include "burn-basics.h"
44 #include "burn-plugin-manager.h"
45 #include "brasero-medium-selection-priv.h"
46 #include "brasero-session-helper.h"
47 
48 #include "brasero-dest-selection.h"
49 
50 #include "brasero-drive.h"
51 #include "brasero-medium.h"
52 #include "brasero-volume.h"
53 
54 #include "brasero-burn-lib.h"
55 #include "brasero-tags.h"
56 #include "brasero-track.h"
57 #include "brasero-session.h"
58 #include "brasero-session-cfg.h"
59 
60 typedef struct _BraseroDestSelectionPrivate BraseroDestSelectionPrivate;
61 struct _BraseroDestSelectionPrivate
62 {
63 	BraseroBurnSession *session;
64 
65 	BraseroDrive *locked_drive;
66 
67 	guint user_changed:1;
68 };
69 
70 #define BRASERO_DEST_SELECTION_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_DEST_SELECTION, BraseroDestSelectionPrivate))
71 
72 enum {
73 	PROP_0,
74 	PROP_SESSION
75 };
76 
77 G_DEFINE_TYPE (BraseroDestSelection, brasero_dest_selection, BRASERO_TYPE_MEDIUM_SELECTION);
78 
79 static void
brasero_dest_selection_lock(BraseroDestSelection * self,gboolean locked)80 brasero_dest_selection_lock (BraseroDestSelection *self,
81 			     gboolean locked)
82 {
83 	BraseroDestSelectionPrivate *priv;
84 
85 	priv = BRASERO_DEST_SELECTION_PRIVATE (self);
86 
87 	if (locked == (priv->locked_drive != NULL))
88 		return;
89 
90 	gtk_widget_set_sensitive (GTK_WIDGET (self), (locked != TRUE));
91 	gtk_widget_queue_draw (GTK_WIDGET (self));
92 
93 	if (priv->locked_drive) {
94 		brasero_drive_unlock (priv->locked_drive);
95 		g_object_unref (priv->locked_drive);
96 		priv->locked_drive = NULL;
97 	}
98 
99 	if (locked) {
100 		BraseroMedium *medium;
101 
102 		medium = brasero_medium_selection_get_active (BRASERO_MEDIUM_SELECTION (self));
103 		priv->locked_drive = brasero_medium_get_drive (medium);
104 
105 		if (priv->locked_drive) {
106 			g_object_ref (priv->locked_drive);
107 			brasero_drive_lock (priv->locked_drive,
108 					    _("Ongoing burning process"),
109 					    NULL);
110 		}
111 
112 		if (medium)
113 			g_object_unref (medium);
114 	}
115 }
116 
117 static void
brasero_dest_selection_valid_session(BraseroSessionCfg * session,BraseroDestSelection * self)118 brasero_dest_selection_valid_session (BraseroSessionCfg *session,
119 				      BraseroDestSelection *self)
120 {
121 	brasero_medium_selection_update_media_string (BRASERO_MEDIUM_SELECTION (self));
122 }
123 
124 static void
brasero_dest_selection_output_changed(BraseroSessionCfg * session,BraseroMedium * former,BraseroDestSelection * self)125 brasero_dest_selection_output_changed (BraseroSessionCfg *session,
126 				       BraseroMedium *former,
127 				       BraseroDestSelection *self)
128 {
129 	BraseroDestSelectionPrivate *priv;
130 	BraseroMedium *medium;
131 	BraseroDrive *burner;
132 
133 	priv = BRASERO_DEST_SELECTION_PRIVATE (self);
134 
135 	/* make sure the current displayed drive reflects that */
136 	burner = brasero_burn_session_get_burner (priv->session);
137 	medium = brasero_medium_selection_get_active (BRASERO_MEDIUM_SELECTION (self));
138 	if (burner != brasero_medium_get_drive (medium))
139 		brasero_medium_selection_set_active (BRASERO_MEDIUM_SELECTION (self),
140 						     brasero_drive_get_medium (burner));
141 
142 	if (medium)
143 		g_object_unref (medium);
144 }
145 
146 static void
brasero_dest_selection_flags_changed(BraseroBurnSession * session,GParamSpec * pspec,BraseroDestSelection * self)147 brasero_dest_selection_flags_changed (BraseroBurnSession *session,
148                                       GParamSpec *pspec,
149 				      BraseroDestSelection *self)
150 {
151 	BraseroDestSelectionPrivate *priv;
152 
153 	priv = BRASERO_DEST_SELECTION_PRIVATE (self);
154 
155 	brasero_dest_selection_lock (self, (brasero_burn_session_get_flags (BRASERO_BURN_SESSION (priv->session)) & BRASERO_BURN_FLAG_MERGE) != 0);
156 }
157 
158 static void
brasero_dest_selection_medium_changed(BraseroMediumSelection * selection,BraseroMedium * medium)159 brasero_dest_selection_medium_changed (BraseroMediumSelection *selection,
160 				       BraseroMedium *medium)
161 {
162 	BraseroDestSelectionPrivate *priv;
163 
164 	priv = BRASERO_DEST_SELECTION_PRIVATE (selection);
165 
166 	if (!priv->session)
167 		goto chain;
168 
169 	if (!medium) {
170 	    	gtk_widget_set_sensitive (GTK_WIDGET (selection), FALSE);
171 		goto chain;
172 	}
173 
174 	if (brasero_medium_get_drive (medium) == brasero_burn_session_get_burner (priv->session))
175 		goto chain;
176 
177 	if (priv->locked_drive && priv->locked_drive != brasero_medium_get_drive (medium)) {
178 		brasero_medium_selection_set_active (selection, medium);
179 		goto chain;
180 	}
181 
182 	brasero_burn_session_set_burner (priv->session, brasero_medium_get_drive (medium));
183 	gtk_widget_set_sensitive (GTK_WIDGET (selection), (priv->locked_drive == NULL));
184 
185 chain:
186 
187 	if (BRASERO_MEDIUM_SELECTION_CLASS (brasero_dest_selection_parent_class)->medium_changed)
188 		BRASERO_MEDIUM_SELECTION_CLASS (brasero_dest_selection_parent_class)->medium_changed (selection, medium);
189 }
190 
191 static void
brasero_dest_selection_user_change(BraseroDestSelection * selection,GParamSpec * pspec,gpointer NULL_data)192 brasero_dest_selection_user_change (BraseroDestSelection *selection,
193                                     GParamSpec *pspec,
194                                     gpointer NULL_data)
195 {
196 	gboolean shown = FALSE;
197 	BraseroDestSelectionPrivate *priv;
198 
199 	/* we are only interested when the menu is shown */
200 	g_object_get (selection,
201 	              "popup-shown", &shown,
202 	              NULL);
203 
204 	if (!shown)
205 		return;
206 
207 	priv = BRASERO_DEST_SELECTION_PRIVATE (selection);
208 	priv->user_changed = TRUE;
209 }
210 
211 static void
brasero_dest_selection_medium_removed(GtkTreeModel * model,GtkTreePath * path,gpointer user_data)212 brasero_dest_selection_medium_removed (GtkTreeModel *model,
213                                        GtkTreePath *path,
214                                        gpointer user_data)
215 {
216 	BraseroDestSelectionPrivate *priv;
217 
218 	priv = BRASERO_DEST_SELECTION_PRIVATE (user_data);
219 	if (priv->user_changed)
220 		return;
221 
222 	if (gtk_combo_box_get_active (GTK_COMBO_BOX (user_data)) == -1)
223 		brasero_dest_selection_choose_best (BRASERO_DEST_SELECTION (user_data));
224 }
225 
226 static void
brasero_dest_selection_medium_added(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)227 brasero_dest_selection_medium_added (GtkTreeModel *model,
228                                      GtkTreePath *path,
229                                      GtkTreeIter *iter,
230                                      gpointer user_data)
231 {
232 	BraseroDestSelectionPrivate *priv;
233 
234 	priv = BRASERO_DEST_SELECTION_PRIVATE (user_data);
235 	if (priv->user_changed)
236 		return;
237 
238 	brasero_dest_selection_choose_best (BRASERO_DEST_SELECTION (user_data));
239 }
240 
241 static void
brasero_dest_selection_constructed(GObject * object)242 brasero_dest_selection_constructed (GObject *object)
243 {
244 	G_OBJECT_CLASS (brasero_dest_selection_parent_class)->constructed (object);
245 
246 	/* Only show media on which we can write and which are in a burner.
247 	 * There is one exception though, when we're copying media and when the
248 	 * burning device is the same as the dest device. */
249 	brasero_medium_selection_show_media_type (BRASERO_MEDIUM_SELECTION (object),
250 						  BRASERO_MEDIA_TYPE_WRITABLE|
251 						  BRASERO_MEDIA_TYPE_FILE);
252 }
253 
254 static void
brasero_dest_selection_init(BraseroDestSelection * object)255 brasero_dest_selection_init (BraseroDestSelection *object)
256 {
257 	GtkTreeModel *model;
258 
259 	model = gtk_combo_box_get_model (GTK_COMBO_BOX (object));
260 	g_signal_connect (model,
261 	                  "row-inserted",
262 	                  G_CALLBACK (brasero_dest_selection_medium_added),
263 	                  object);
264 	g_signal_connect (model,
265 	                  "row-deleted",
266 	                  G_CALLBACK (brasero_dest_selection_medium_removed),
267 	                  object);
268 
269 	/* This is to know when the user changed it on purpose */
270 	g_signal_connect (object,
271 	                  "notify::popup-shown",
272 	                  G_CALLBACK (brasero_dest_selection_user_change),
273 	                  NULL);
274 }
275 
276 static void
brasero_dest_selection_clean(BraseroDestSelection * self)277 brasero_dest_selection_clean (BraseroDestSelection *self)
278 {
279 	BraseroDestSelectionPrivate *priv;
280 
281 	priv = BRASERO_DEST_SELECTION_PRIVATE (self);
282 
283 	if (priv->session) {
284 		g_signal_handlers_disconnect_by_func (priv->session,
285 						      brasero_dest_selection_valid_session,
286 						      self);
287 		g_signal_handlers_disconnect_by_func (priv->session,
288 						      brasero_dest_selection_output_changed,
289 						      self);
290 		g_signal_handlers_disconnect_by_func (priv->session,
291 						      brasero_dest_selection_flags_changed,
292 						      self);
293 
294 		g_object_unref (priv->session);
295 		priv->session = NULL;
296 	}
297 
298 	if (priv->locked_drive) {
299 		brasero_drive_unlock (priv->locked_drive);
300 		g_object_unref (priv->locked_drive);
301 		priv->locked_drive = NULL;
302 	}
303 }
304 
305 static void
brasero_dest_selection_finalize(GObject * object)306 brasero_dest_selection_finalize (GObject *object)
307 {
308 	brasero_dest_selection_clean (BRASERO_DEST_SELECTION (object));
309 	G_OBJECT_CLASS (brasero_dest_selection_parent_class)->finalize (object);
310 }
311 
312 static goffset
_get_medium_free_space(BraseroMedium * medium,goffset session_blocks)313 _get_medium_free_space (BraseroMedium *medium,
314                         goffset session_blocks)
315 {
316 	BraseroMedia media;
317 	goffset blocks = 0;
318 
319 	media = brasero_medium_get_status (medium);
320 	media = brasero_burn_library_get_media_capabilities (media);
321 
322 	/* NOTE: we always try to blank a medium when we can */
323 	brasero_medium_get_free_space (medium,
324 				       NULL,
325 				       &blocks);
326 
327 	if ((media & BRASERO_MEDIUM_REWRITABLE)
328 	&& blocks < session_blocks)
329 		brasero_medium_get_capacity (medium,
330 		                             NULL,
331 		                             &blocks);
332 
333 	return blocks;
334 }
335 
336 static gboolean
brasero_dest_selection_foreach_medium(BraseroMedium * medium,gpointer callback_data)337 brasero_dest_selection_foreach_medium (BraseroMedium *medium,
338 				       gpointer callback_data)
339 {
340 	BraseroBurnSession *session;
341 	goffset session_blocks = 0;
342 	goffset burner_blocks = 0;
343 	goffset medium_blocks;
344 	BraseroDrive *burner;
345 
346 	session = callback_data;
347 	burner = brasero_burn_session_get_burner (session);
348 	if (!burner) {
349 		brasero_burn_session_set_burner (session, brasero_medium_get_drive (medium));
350 		return TRUE;
351 	}
352 
353 	/* no need to deal with this case */
354 	if (brasero_drive_get_medium (burner) == medium)
355 		return TRUE;
356 
357 	/* The rule is:
358 	 * - blank media are our favourite since it avoids hiding/blanking data
359 	 * - take the medium that is closest to the size we need to burn
360 	 * - try to avoid a medium that is already our source for copying */
361 	/* NOTE: we could check if medium is bigger */
362 	if ((brasero_burn_session_get_dest_media (session) & BRASERO_MEDIUM_BLANK)
363 	&&  (brasero_medium_get_status (medium) & BRASERO_MEDIUM_BLANK))
364 		goto choose_closest_size;
365 
366 	if (brasero_burn_session_get_dest_media (session) & BRASERO_MEDIUM_BLANK)
367 		return TRUE;
368 
369 	if (brasero_medium_get_status (medium) & BRASERO_MEDIUM_BLANK) {
370 		brasero_burn_session_set_burner (session, brasero_medium_get_drive (medium));
371 		return TRUE;
372 	}
373 
374 	/* In case it is the same source/same destination, choose it this new
375 	 * medium except if the medium is a file. */
376 	if (brasero_burn_session_same_src_dest_drive (session)
377 	&& (brasero_medium_get_status (medium) & BRASERO_MEDIUM_FILE) == 0) {
378 		brasero_burn_session_set_burner (session, brasero_medium_get_drive (medium));
379 		return TRUE;
380 	}
381 
382 	/* Any possible medium is better than file even if it means copying to
383 	 * the same drive with a new medium later. */
384 	if (brasero_drive_is_fake (burner)
385 	&& (brasero_medium_get_status (medium) & BRASERO_MEDIUM_FILE) == 0) {
386 		brasero_burn_session_set_burner (session, brasero_medium_get_drive (medium));
387 		return TRUE;
388 	}
389 
390 
391 choose_closest_size:
392 
393 	brasero_burn_session_get_size (session, &session_blocks, NULL);
394 	medium_blocks = _get_medium_free_space (medium, session_blocks);
395 
396 	if (medium_blocks - session_blocks <= 0)
397 		return TRUE;
398 
399 	burner_blocks = _get_medium_free_space (brasero_drive_get_medium (burner), session_blocks);
400 	if (burner_blocks - session_blocks <= 0)
401 		brasero_burn_session_set_burner (session, brasero_medium_get_drive (medium));
402 	else if (burner_blocks - session_blocks > medium_blocks - session_blocks)
403 		brasero_burn_session_set_burner (session, brasero_medium_get_drive (medium));
404 
405 	return TRUE;
406 }
407 
408 void
brasero_dest_selection_choose_best(BraseroDestSelection * self)409 brasero_dest_selection_choose_best (BraseroDestSelection *self)
410 {
411 	BraseroDestSelectionPrivate *priv;
412 
413 	priv = BRASERO_DEST_SELECTION_PRIVATE (self);
414 
415 	priv->user_changed = FALSE;
416 	if (!priv->session)
417 		return;
418 
419 	if (!(brasero_burn_session_get_flags (priv->session) & BRASERO_BURN_FLAG_MERGE)) {
420 		BraseroDrive *drive;
421 
422 		/* Select the best fitting media */
423 		brasero_medium_selection_foreach (BRASERO_MEDIUM_SELECTION (self),
424 						  brasero_dest_selection_foreach_medium,
425 						  priv->session);
426 
427 		drive = brasero_burn_session_get_burner (BRASERO_BURN_SESSION (priv->session));
428 		if (drive)
429 			brasero_medium_selection_set_active (BRASERO_MEDIUM_SELECTION (self),
430 							     brasero_drive_get_medium (drive));
431 	}
432 }
433 
434 void
brasero_dest_selection_set_session(BraseroDestSelection * selection,BraseroBurnSession * session)435 brasero_dest_selection_set_session (BraseroDestSelection *selection,
436 				    BraseroBurnSession *session)
437 {
438 	BraseroDestSelectionPrivate *priv;
439 
440 	priv = BRASERO_DEST_SELECTION_PRIVATE (selection);
441 
442 	if (priv->session)
443 		brasero_dest_selection_clean (selection);
444 
445 	if (!session)
446 		return;
447 
448 	priv->session = g_object_ref (session);
449 	if (brasero_burn_session_get_flags (session) & BRASERO_BURN_FLAG_MERGE) {
450 		BraseroDrive *drive;
451 
452 		/* Prevent automatic resetting since a drive was set */
453 		priv->user_changed = TRUE;
454 
455 		drive = brasero_burn_session_get_burner (session);
456 		brasero_medium_selection_set_active (BRASERO_MEDIUM_SELECTION (selection),
457 						     brasero_drive_get_medium (drive));
458 	}
459 	else {
460 		BraseroDrive *burner;
461 
462 		/* Only try to set a better drive if there isn't one already set */
463 		burner = brasero_burn_session_get_burner (BRASERO_BURN_SESSION (priv->session));
464 		if (burner) {
465 			BraseroMedium *medium;
466 
467 			/* Prevent automatic resetting since a drive was set */
468 			priv->user_changed = TRUE;
469 
470 			medium = brasero_drive_get_medium (burner);
471 			brasero_medium_selection_set_active (BRASERO_MEDIUM_SELECTION (selection), medium);
472 		}
473 		else
474 			brasero_dest_selection_choose_best (BRASERO_DEST_SELECTION (selection));
475 	}
476 
477 	g_signal_connect (session,
478 			  "is-valid",
479 			  G_CALLBACK (brasero_dest_selection_valid_session),
480 			  selection);
481 	g_signal_connect (session,
482 			  "output-changed",
483 			  G_CALLBACK (brasero_dest_selection_output_changed),
484 			  selection);
485 	g_signal_connect (session,
486 			  "notify::flags",
487 			  G_CALLBACK (brasero_dest_selection_flags_changed),
488 			  selection);
489 
490 	brasero_medium_selection_update_media_string (BRASERO_MEDIUM_SELECTION (selection));
491 }
492 
493 static void
brasero_dest_selection_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)494 brasero_dest_selection_set_property (GObject *object,
495 				     guint property_id,
496 				     const GValue *value,
497 				     GParamSpec *pspec)
498 {
499 	BraseroBurnSession *session;
500 
501 	switch (property_id) {
502 	case PROP_SESSION: /* Readable and only writable at creation time */
503 		/* NOTE: no need to unref a potential previous session since
504 		 * it's only set at construct time */
505 		session = g_value_get_object (value);
506 		brasero_dest_selection_set_session (BRASERO_DEST_SELECTION (object), session);
507 		break;
508 
509 	default:
510 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
511 	}
512 }
513 
514 static void
brasero_dest_selection_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)515 brasero_dest_selection_get_property (GObject *object,
516 				     guint property_id,
517 				     GValue *value,
518 				     GParamSpec *pspec)
519 {
520 	BraseroDestSelectionPrivate *priv;
521 
522 	priv = BRASERO_DEST_SELECTION_PRIVATE (object);
523 
524 	switch (property_id) {
525 	case PROP_SESSION:
526 		g_object_ref (priv->session);
527 		g_value_set_object (value, priv->session);
528 		break;
529 
530 	default:
531 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
532 	}
533 }
534 
535 static gchar *
brasero_dest_selection_get_output_path(BraseroDestSelection * self)536 brasero_dest_selection_get_output_path (BraseroDestSelection *self)
537 {
538 	gchar *path = NULL;
539 	BraseroImageFormat format;
540 	BraseroDestSelectionPrivate *priv;
541 
542 	priv = BRASERO_DEST_SELECTION_PRIVATE (self);
543 
544 	format = brasero_burn_session_get_output_format (priv->session);
545 	switch (format) {
546 	case BRASERO_IMAGE_FORMAT_BIN:
547 		brasero_burn_session_get_output (priv->session,
548 						 &path,
549 						 NULL);
550 		break;
551 
552 	case BRASERO_IMAGE_FORMAT_CLONE:
553 	case BRASERO_IMAGE_FORMAT_CDRDAO:
554 	case BRASERO_IMAGE_FORMAT_CUE:
555 		brasero_burn_session_get_output (priv->session,
556 						 NULL,
557 						 &path);
558 		break;
559 
560 	default:
561 		break;
562 	}
563 
564 	return path;
565 }
566 
567 static gchar *
brasero_dest_selection_format_medium_string(BraseroMediumSelection * selection,BraseroMedium * medium)568 brasero_dest_selection_format_medium_string (BraseroMediumSelection *selection,
569 					     BraseroMedium *medium)
570 {
571 	guint used;
572 	gchar *label;
573 	goffset blocks = 0;
574 	gchar *medium_name;
575 	gchar *size_string;
576 	BraseroMedia media;
577 	BraseroBurnFlag flags;
578 	goffset size_bytes = 0;
579 	goffset data_blocks = 0;
580 	goffset session_bytes = 0;
581 	BraseroTrackType *input = NULL;
582 	BraseroDestSelectionPrivate *priv;
583 
584 	priv = BRASERO_DEST_SELECTION_PRIVATE (selection);
585 
586 	if (!priv->session)
587 		return NULL;
588 
589 	medium_name = brasero_volume_get_name (BRASERO_VOLUME (medium));
590 	if (brasero_medium_get_status (medium) & BRASERO_MEDIUM_FILE) {
591 		gchar *path;
592 
593 		input = brasero_track_type_new ();
594 		brasero_burn_session_get_input_type (priv->session, input);
595 
596 		/* There should be a special name for image in video context */
597 		if (brasero_track_type_get_has_stream (input)
598 		&&  BRASERO_STREAM_FORMAT_HAS_VIDEO (brasero_track_type_get_stream_format (input))) {
599 			BraseroImageFormat format;
600 
601 			format = brasero_burn_session_get_output_format (priv->session);
602 			if (format == BRASERO_IMAGE_FORMAT_CUE) {
603 				g_free (medium_name);
604 				if (brasero_burn_session_tag_lookup_int (priv->session, BRASERO_VCD_TYPE) == BRASERO_SVCD)
605 					medium_name = g_strdup (_("SVCD image"));
606 				else
607 					medium_name = g_strdup (_("VCD image"));
608 			}
609 			else if (format == BRASERO_IMAGE_FORMAT_BIN) {
610 				g_free (medium_name);
611 				medium_name = g_strdup (_("Video DVD image"));
612 			}
613 		}
614 		brasero_track_type_free (input);
615 
616 		/* get the set path for the image file */
617 		path = brasero_dest_selection_get_output_path (BRASERO_DEST_SELECTION (selection));
618 		if (!path)
619 			return medium_name;
620 
621 		/* NOTE for translators: the first %s is medium_name ("File
622 		 * Image") and the second the path for the image file */
623 		label = g_strdup_printf (_("%s: \"%s\""),
624 					 medium_name,
625 					 path);
626 		g_free (medium_name);
627 		g_free (path);
628 
629 		brasero_medium_selection_update_used_space (BRASERO_MEDIUM_SELECTION (selection),
630 							    medium,
631 							    0);
632 		return label;
633 	}
634 
635 	if (!priv->session) {
636 		g_free (medium_name);
637 		return NULL;
638 	}
639 
640 	input = brasero_track_type_new ();
641 	brasero_burn_session_get_input_type (priv->session, input);
642 	if (brasero_track_type_get_has_medium (input)) {
643 		BraseroMedium *src_medium;
644 
645 		src_medium = brasero_burn_session_get_src_medium (priv->session);
646 		if (src_medium == medium) {
647 			brasero_track_type_free (input);
648 
649 			/* Translators: this string is only used when the user
650 			 * wants to copy a disc using the same destination and
651 			 * source drive. It tells him that brasero will use as
652 			 * destination disc a new one (once the source has been
653 			 * copied) which is to be inserted in the drive currently
654 			 * holding the source disc */
655 			label = g_strdup_printf (_("New disc in the burner holding the source disc"));
656 			g_free (medium_name);
657 
658 			brasero_medium_selection_update_used_space (BRASERO_MEDIUM_SELECTION (selection),
659 								    medium,
660 								    0);
661 			return label;
662 		}
663 	}
664 
665 	media = brasero_medium_get_status (medium);
666 	flags = brasero_burn_session_get_flags (priv->session);
667 	brasero_burn_session_get_size (priv->session,
668 				       &data_blocks,
669 				       &session_bytes);
670 
671 	if (flags & (BRASERO_BURN_FLAG_MERGE|BRASERO_BURN_FLAG_APPEND))
672 		brasero_medium_get_free_space (medium, &size_bytes, &blocks);
673 	else if (brasero_burn_library_get_media_capabilities (media) & BRASERO_MEDIUM_REWRITABLE)
674 		brasero_medium_get_capacity (medium, &size_bytes, &blocks);
675 	else
676 		brasero_medium_get_free_space (medium, &size_bytes, &blocks);
677 
678 	if (blocks) {
679 		used = data_blocks * 100 / blocks;
680 		if (data_blocks && !used)
681 			used = 1;
682 
683 		used = MIN (100, used);
684 	}
685 	else
686 		used = 0;
687 
688 	brasero_medium_selection_update_used_space (BRASERO_MEDIUM_SELECTION (selection),
689 						    medium,
690 						    used);
691 	blocks -= data_blocks;
692 	if (blocks <= 0) {
693 		brasero_track_type_free (input);
694 
695 		/* NOTE for translators, the first %s is the medium name */
696 		label = g_strdup_printf (_("%s: not enough free space"), medium_name);
697 		g_free (medium_name);
698 		return label;
699 	}
700 
701 	/* format the size */
702 	if (brasero_track_type_get_has_stream (input)
703 	&& BRASERO_STREAM_FORMAT_HAS_VIDEO (brasero_track_type_get_stream_format (input))) {
704 		guint64 free_time;
705 
706 		/* This is an embarassing problem: this is an approximation
707 		 * based on the fact that 2 hours = 4.3GiB */
708 		free_time = size_bytes - session_bytes;
709 		free_time = free_time * 72000LL / 47LL;
710 		size_string = brasero_units_get_time_string (free_time,
711 							     TRUE,
712 							     TRUE);
713 	}
714 	else if (brasero_track_type_get_has_stream (input)
715 	|| (brasero_track_type_get_has_medium (input)
716 	&& (brasero_track_type_get_medium_type (input) & BRASERO_MEDIUM_HAS_AUDIO)))
717 		size_string = brasero_units_get_time_string (BRASERO_SECTORS_TO_DURATION (blocks),
718 							     TRUE,
719 							     TRUE);
720 	else
721 		size_string = g_format_size (size_bytes - session_bytes);
722 
723 	brasero_track_type_free (input);
724 
725 	/* NOTE for translators: the first %s is the medium name, the second %s
726 	 * is its available free space. "Free" here is the free space available. */
727 	label = g_strdup_printf (_("%s: %s of free space"), medium_name, size_string);
728 	g_free (medium_name);
729 	g_free (size_string);
730 
731 	return label;
732 }
733 
734 static void
brasero_dest_selection_class_init(BraseroDestSelectionClass * klass)735 brasero_dest_selection_class_init (BraseroDestSelectionClass *klass)
736 {
737 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
738 	BraseroMediumSelectionClass *medium_selection_class = BRASERO_MEDIUM_SELECTION_CLASS (klass);
739 
740 	g_type_class_add_private (klass, sizeof (BraseroDestSelectionPrivate));
741 
742 	object_class->finalize = brasero_dest_selection_finalize;
743 	object_class->set_property = brasero_dest_selection_set_property;
744 	object_class->get_property = brasero_dest_selection_get_property;
745 	object_class->constructed = brasero_dest_selection_constructed;
746 
747 	medium_selection_class->format_medium_string = brasero_dest_selection_format_medium_string;
748 	medium_selection_class->medium_changed = brasero_dest_selection_medium_changed;
749 	g_object_class_install_property (object_class,
750 					 PROP_SESSION,
751 					 g_param_spec_object ("session",
752 							      "The session",
753 							      "The session to work with",
754 							      BRASERO_TYPE_BURN_SESSION,
755 							      G_PARAM_READWRITE));
756 }
757 
758 GtkWidget *
brasero_dest_selection_new(BraseroBurnSession * session)759 brasero_dest_selection_new (BraseroBurnSession *session)
760 {
761 	return g_object_new (BRASERO_TYPE_DEST_SELECTION,
762 			     "session", session,
763 			     NULL);
764 }
765