hythmbox-2.98/shell/rb-track-transfer-queue.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
  2 /*
  3  *  Copyright (C) 2010 Jonathan Matthew  <jonathan@d14n.org>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grants permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 #include "config.h"
 30 
 31 #include "rb-track-transfer-queue.h"
 32 #include "rb-encoder.h"
 33 #include "rb-marshal.h"
 34 #include "rb-library-source.h"
 35 #include "rb-debug.h"
 36 #include "rb-dialog.h"
 37 #include "rb-alert-dialog.h"
 38 #include "rb-gst-media-types.h"
 39 #include "rb-missing-plugins.h"
 40 
 41 #include <glib/gi18n.h>
 42 
 43 #include <gst/gst.h>
 44 #include <gst/pbutils/install-plugins.h>
 45 
 46 enum
 47 {
 48 	PROP_0,
 49 	PROP_SHELL,
 50 	PROP_BATCH
 51 };
 52 
 53 enum
 54 {
 55 	TRANSFER_PROGRESS,
 56 	MISSING_PLUGINS,
 57 	LAST_SIGNAL
 58 };
 59 
 60 static void rb_track_transfer_queue_class_init	(RBTrackTransferQueueClass *klass);
 61 static void rb_track_transfer_queue_init	(RBTrackTransferQueue *queue);
 62 
 63 static void start_next_batch (RBTrackTransferQueue *queue);
 64 
 65 static guint signals[LAST_SIGNAL] = { 0 };
 66 
 67 struct _RBTrackTransferQueuePrivate
 68 {
 69 	RBShell *shell;
 70 
 71 	GQueue *batch_queue;
 72 	enum {
 73 		OVERWRITE_PROMPT,
 74 		OVERWRITE_ALL,
 75 		OVERWRITE_NONE
 76 	} overwrite_decision;
 77 	RBTrackTransferBatch *current;
 78 	time_t current_start_time;
 79 };
 80 
 81 G_DEFINE_TYPE (RBTrackTransferQueue, rb_track_transfer_queue, G_TYPE_OBJECT)
 82 
 83 /**
 84  * SECTION:rb-track-transfer-queue
 85  * @short_description: track transfer queue and surrounding junk
 86  *
 87  */
 88 
 89 /**
 90  * rb_track_transfer_queue_new:
 91  * @shell: the #RBShell
 92  *
 93  * Creates the #RBTrackTransferQueue instance
 94  *
 95  * Return value: the #RBTrackTransferQueue
 96  */
 97 RBTrackTransferQueue *
 98 rb_track_transfer_queue_new (RBShell *shell)
 99 {
100 	return g_object_new (RB_TYPE_TRACK_TRANSFER_QUEUE, "shell", shell, NULL);
101 }
102 
103 static void
104 overwrite_response_cb (GtkDialog *dialog, int response, RBTrackTransferQueue *queue)
105 {
106 	gtk_widget_destroy (GTK_WIDGET (dialog));
107 
108 	switch (response) {
109 	case GTK_RESPONSE_YES:
110 		rb_debug ("replacing existing file");
111 		_rb_track_transfer_batch_continue (queue->priv->current, TRUE);
112 		break;
113 
114 	case GTK_RESPONSE_NO:
115 		rb_debug ("skipping existing file");
116 		_rb_track_transfer_batch_continue (queue->priv->current, FALSE);
117 		break;
118 
119 	case GTK_RESPONSE_REJECT:
120 		rb_debug ("skipping all existing files");
121 		queue->priv->overwrite_decision = OVERWRITE_NONE;
122 		_rb_track_transfer_batch_continue (queue->priv->current, FALSE);
123 		break;
124 
125 	case GTK_RESPONSE_ACCEPT:
126 		rb_debug ("replacing all existing files");
127 		queue->priv->overwrite_decision = OVERWRITE_ALL;
128 		_rb_track_transfer_batch_continue (queue->priv->current, TRUE);
129 		break;
130 
131 	case GTK_RESPONSE_CANCEL:
132 	case GTK_RESPONSE_DELETE_EVENT:		/* not sure what the user really wants here */
133 		rb_debug ("cancelling batch");
134 		rb_track_transfer_queue_cancel_batch (queue, queue->priv->current);
135 		break;
136 
137 	default:
138 		g_assert_not_reached ();
139 		break;
140 	}
141 }
142 
143 static void
144 overwrite_prompt (RBTrackTransferBatch *batch, const char *uri, RBTrackTransferQueue *queue)
145 {
146 	switch (queue->priv->overwrite_decision) {
147 	case OVERWRITE_PROMPT:
148 	{
149 		GtkWindow *window;
150 		GtkWidget *dialog;
151 		GFile *file;
152 		GFileInfo *info;
153 		char *text;
154 		char *free_name;
155 		const char *display_name;
156 
157 		free_name = NULL;
158 		display_name = NULL;
159 		file = g_file_new_for_uri (uri);
160 		info = g_file_query_info (file,
161 					  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
162 					  G_FILE_QUERY_INFO_NONE,
163 					  NULL,
164 					  NULL);
165 		if (info != NULL) {
166 			display_name = g_file_info_get_display_name (info);
167 		}
168 
169 		if (display_name == NULL) {
170 			free_name = g_file_get_uri (file);
171 			display_name = free_name;
172 		}
173 
174 		g_object_get (queue->priv->shell, "window", &window, NULL);
175 		text = g_strdup_printf (_("The file \"%s\" already exists. Do you want to replace it?"),
176 					display_name);
177 		dialog = rb_alert_dialog_new (window,
178 					      0,
179 					      GTK_MESSAGE_WARNING,
180 					      GTK_BUTTONS_NONE,
181 					      text,
182 					      NULL);
183 		g_object_unref (window);
184 		g_free (text);
185 
186 		rb_alert_dialog_set_details_label (RB_ALERT_DIALOG (dialog), NULL);
187 		gtk_dialog_add_buttons (GTK_DIALOG (dialog),
188 					_("_Cancel"), GTK_RESPONSE_CANCEL,
189 					_("_Skip"), GTK_RESPONSE_NO,
190 					_("_Replace"), GTK_RESPONSE_YES,
191 					_("S_kip All"), GTK_RESPONSE_REJECT,
192 					_("Replace _All"), GTK_RESPONSE_ACCEPT,
193 					NULL);
194 
195 		g_signal_connect (dialog, "response", G_CALLBACK (overwrite_response_cb), queue);
196 		gtk_widget_show (GTK_WIDGET (dialog));
197 		g_free (free_name);
198 		if (info != NULL) {
199 			g_object_unref (info);
200 		}
201 		g_object_unref (file);
202 		break;
203 	}
204 
205 	case OVERWRITE_ALL:
206 		rb_debug ("already decided to replace all existing files");
207 		_rb_track_transfer_batch_continue (batch, TRUE);
208 		break;
209 
210 	case OVERWRITE_NONE:
211 		rb_debug ("already decided to skip all existing files");
212 		_rb_track_transfer_batch_continue (batch, FALSE);
213 		break;
214 
215 	default:
216 		g_assert_not_reached ();
217 	}
218 }
219 
220 static void
221 batch_complete (RBTrackTransferBatch *batch, RBTrackTransferQueue *queue)
222 {
223 	if (batch != queue->priv->current) {
224 		rb_debug ("what?");
225 		return;
226 	}
227 
228 	/* batch itself will ensure we get a progress signal showing the
229 	 * whole batch complete, so we don't need one here.
230 	 */
231 
232 	queue->priv->current = NULL;
233 	g_object_unref (batch);
234 
235 	start_next_batch (queue);
236 }
237 
238 static int
239 estimate_time_left (RBTrackTransferQueue *queue, double progress)
240 {
241 	time_t now;
242 	time_t elapsed;
243 	double total_time;
244 
245 	time (&now);
246 	elapsed = now - queue->priv->current_start_time;
247 	total_time = ((double)elapsed) / progress;
248 	return ((time_t) total_time) - elapsed;
249 }
250 
251 static void
252 batch_progress (RBTrackTransferBatch *batch,
253 		RhythmDBEntry *entry,
254 		const char *dest,
255 		int done,
256 		int total,
257 		double fraction,
258 		RBTrackTransferQueue *queue)
259 {
260 	g_signal_emit (queue, signals[TRANSFER_PROGRESS], 0, done, total, fraction, estimate_time_left (queue, fraction));
261 }
262 
263 static void
264 actually_start_batch (RBTrackTransferQueue *queue)
265 {
266 	g_signal_connect_object (queue->priv->current,
267 				 "overwrite-prompt",
268 				 G_CALLBACK (overwrite_prompt),
269 				 queue, 0);
270 	g_signal_connect_object (queue->priv->current,
271 				 "complete",
272 				 G_CALLBACK (batch_complete),
273 				 queue, 0);
274 	g_signal_connect_object (queue->priv->current,
275 				 "track-progress",
276 				 G_CALLBACK (batch_progress),
277 				 queue, 0);
278 	_rb_track_transfer_batch_start (queue->priv->current, G_OBJECT (queue));
279 }
280 
281 static GPtrArray *
282 get_missing_plugin_strings (GList *profiles, gboolean get_descriptions)
283 {
284 	RBEncoder *encoder;
285 	GPtrArray *strings;
286 	GList *l;
287 
288 	encoder = rb_encoder_new ();
289 	strings = g_ptr_array_new_with_free_func (g_free);
290 	for (l = profiles; l != NULL; l = l->next) {
291 		GstEncodingProfile *profile = GST_ENCODING_PROFILE (l->data);
292 		char **details, **descriptions;
293 		char **d;
294 		int i;
295 
296 		rb_encoder_get_missing_plugins (encoder, profile, &details, &descriptions);
297 		d = get_descriptions ? descriptions : details;
298 		for (i = 0; d[i] != NULL; i++) {
299 			g_ptr_array_add (strings, g_strdup (d[i]));
300 		}
301 		g_strfreev (details);
302 		g_strfreev (descriptions);
303 	}
304 	g_ptr_array_add (strings, NULL);
305 	g_object_unref (encoder);
306 
307 	return strings;
308 }
309 
310 static void
311 missing_plugins_retry_cb (gpointer inst, gboolean retry, RBTrackTransferQueue *queue)
312 {
313 	rb_debug ("plugin install finished (retry %d), checking media types again", retry);
314 	g_queue_push_head (queue->priv->batch_queue, queue->priv->current);
315 	queue->priv->current = NULL;
316 	start_next_batch (queue);
317 }
318 
319 static void
320 missing_encoder_response_cb (GtkDialog *dialog, gint response, RBTrackTransferQueue *queue)
321 {
322 	GClosure *retry;
323 	GstEncodingTarget *target;
324 	GPtrArray *details;
325 	GList *profiles;
326 	const GList *l;
327 	RBEncoder *encoder;
328 
329 	switch (response) {
330 	case GTK_RESPONSE_YES:
331 		/* 'continue' -> start the batch */
332 		rb_debug ("starting batch regardless of missing plugins");
333 		actually_start_batch (queue);
334 		break;
335 
336 	case GTK_RESPONSE_CANCEL:
337 	case GTK_RESPONSE_DELETE_EVENT:
338 		/* 'cancel' -> cancel the batch and start the next one */
339 		rb_debug ("cancelling batch");
340 		_rb_track_transfer_batch_cancel (queue->priv->current);
341 		g_object_unref (queue->priv->current);
342 		queue->priv->current = NULL;
343 
344 		start_next_batch (queue);
345 		break;
346 
347 	case GTK_RESPONSE_ACCEPT:
348 		/* 'install plugins' -> try to install encoder/muxer */
349 
350 		/* get profiles that need plugins installed */
351 		profiles = NULL;
352 		encoder = rb_encoder_new ();
353 		g_object_get (queue->priv->current, "encoding-target", &target, NULL);
354 		for (l = gst_encoding_target_get_profiles (target); l != NULL; l = l->next) {
355 			GstEncodingProfile *profile = GST_ENCODING_PROFILE (l->data);
356 			char *profile_media_type;
357 			profile_media_type = rb_gst_encoding_profile_get_media_type (profile);
358 			if (profile_media_type != NULL &&
359 			    (rb_gst_media_type_is_lossless (profile_media_type) == FALSE) &&
360 			    rb_encoder_get_missing_plugins (encoder, profile, NULL, NULL)) {
361 				profiles = g_list_append (profiles, profile);
362 			}
363 			g_free (profile_media_type);
364 		}
365 		g_object_unref (encoder);
366 		g_object_unref (target);
367 
368 		if (profiles == NULL) {
369 			rb_debug ("apparently we don't need any plugins any more");
370 			actually_start_batch (queue);
371 			break;
372 		}
373 
374 		rb_debug ("attempting plugin installation");
375 		details = get_missing_plugin_strings (profiles, FALSE);
376 		retry = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
377 					g_object_ref (queue),
378 					(GClosureNotify) g_object_unref);
379 		g_closure_set_marshal (retry, g_cclosure_marshal_VOID__BOOLEAN);
380 		if (rb_missing_plugins_install ((const char **)details->pdata, FALSE, retry)) {
381 			rb_debug ("attempting to install missing plugins for transcoding");
382 		} else {
383 			rb_debug ("proceeding without the missing plugins for transcoding");
384 			actually_start_batch (queue);
385 		}
386 
387 		g_closure_sink (retry);
388 		g_ptr_array_free (details, TRUE);
389 		g_list_free (profiles);
390 		break;
391 
392 	default:
393 		g_assert_not_reached ();
394 	}
395 
396 	gtk_widget_destroy (GTK_WIDGET (dialog));
397 }
398 
399 static void
400 start_next_batch (RBTrackTransferQueue *queue)
401 {
402 	int count;
403 	int total;
404 	gboolean can_continue;
405 	GtkWidget *dialog;
406 	GtkWindow *window;
407 	GList *profiles = NULL;
408 	char *message;
409 
410 	if (queue->priv->current != NULL) {
411 		return;
412 	}
413 
414 	queue->priv->current = RB_TRACK_TRANSFER_BATCH (g_queue_pop_head (queue->priv->batch_queue));
415 	g_object_notify (G_OBJECT (queue), "batch");
416 
417 	if (queue->priv->current == NULL) {
418 		/* indicate to anyone watching that we're not doing anything */
419 		g_signal_emit (queue, signals[TRANSFER_PROGRESS], 0, 0, 0, 0.0, 0);
420 		return;
421 	}
422 
423 	queue->priv->overwrite_decision = OVERWRITE_PROMPT;
424 	g_object_get (queue->priv->current, "total-entries", &total, NULL);
425 
426 	count = 0;
427 	can_continue = rb_track_transfer_batch_check_profiles (queue->priv->current,
428 							       &profiles,
429 							       &count);
430 
431 	if (can_continue && count == 0 && profiles == NULL) {
432 		/* no problems, go ahead */
433 		actually_start_batch (queue);
434 		return;
435 	}
436 
437 	if (profiles == NULL) {
438 		const char *str;
439 		str = ngettext ("%d file cannot be transferred as it must be converted into "
440 				"a format supported by the target device but no suitable "
441 				"encoding profiles are available",
442 				"%d files cannot be transferred as they must be converted into "
443 				"a format supported by the target device but no suitable "
444 				"encoding profiles are available",
445 				count);
446 		message = g_strdup_printf (str, count);
447 	} else {
448 		GPtrArray *descriptions;
449 		GstEncodingTarget *target;
450 		char *plugins;
451 		gboolean is_library;
452 
453 		descriptions = get_missing_plugin_strings (profiles, TRUE);
454 		plugins = g_strjoinv ("\n", (char **)descriptions->pdata);
455 
456 		/* this is a tiny bit hackish */
457 		g_object_get (queue->priv->current, "encoding-target", &target, NULL);
458 		is_library = (g_strcmp0 (gst_encoding_target_get_name (target), "rhythmbox-library") == 0);
459 		gst_encoding_target_unref (target);
460 
461 		if (is_library) {
462 			/* XXX should provide the option of picking a different format? */
463 			message = g_strdup_printf (_("Additional software is required to encode media "
464 						     "in your preferred format:\n%s"), plugins);
465 		} else {
466 			const char *str;
467 			str = ngettext ("Additional software is required to convert %d file "
468 					"into a format supported by the target device:\n%s",
469 					"Additional software is required to convert %d files "
470 					"into a format supported by the target device:\n%s",
471 					count);
472 			message = g_strdup_printf (str, count, plugins);
473 		}
474 
475 		g_free (plugins);
476 		g_ptr_array_free (descriptions, TRUE);
477 	}
478 
479 	g_object_get (queue->priv->shell, "window", &window, NULL);
480 	dialog = rb_alert_dialog_new (window,
481 				      0,
482 				      GTK_MESSAGE_ERROR,
483 				      GTK_BUTTONS_NONE,
484 				      _("Unable to transfer tracks"),
485 				      message);
486 	g_object_unref (window);
487 	g_free (message);
488 
489 	gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel the transfer"), GTK_RESPONSE_CANCEL);
490 	if (can_continue) {
491 		gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Skip these files"), GTK_RESPONSE_YES);
492 	}
493 	if (profiles != NULL && gst_install_plugins_supported ()) {
494 		gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Install"), GTK_RESPONSE_ACCEPT);
495 	}
496 
497 	rb_alert_dialog_set_details_label (RB_ALERT_DIALOG (dialog), NULL);
498 	g_signal_connect_object (dialog, "response", G_CALLBACK (missing_encoder_response_cb), queue, 0);
499 	gtk_widget_show (dialog);
500 
501 	if (profiles != NULL) {
502 		g_list_free (profiles);
503 	}
504 }
505 
506 /**
507  * rb_track_transfer_queue_start_batch:
508  * @queue: the #RBTrackTransferQueue
509  * @batch: the #RBTrackTransferBatch to add to the queue
510  *
511  * Adds a new transfer batch to the transfer queue; if the queue is currently
512  * empty, the transfer will start immediately, but not before the call returns.
513  */
514 void
515 rb_track_transfer_queue_start_batch (RBTrackTransferQueue *queue,
516 				     RBTrackTransferBatch *batch)
517 {
518 	g_queue_push_tail (queue->priv->batch_queue, g_object_ref (batch));
519 	start_next_batch (queue);
520 }
521 
522 /**
523  * rb_track_transfer_queue_cancel_batch:
524  * @queue: the #RBTrackTransferQueue
525  * @batch: the #RBTrackTransferBatch to cancel, or NULL for the current batch
526  *
527  * Removes a transfer batch from the queue.  If an entry from the
528  * batch is currently being transferred, the transfer will be
529  * aborted.
530  */
531 void
532 rb_track_transfer_queue_cancel_batch (RBTrackTransferQueue *queue,
533 				      RBTrackTransferBatch *batch)
534 {
535 	gboolean found = FALSE;
536 	if (batch == NULL || batch == queue->priv->current) {
537 		batch = queue->priv->current;
538 		queue->priv->current = NULL;
539 		found = TRUE;
540 	} else {
541 		if (g_queue_find (queue->priv->batch_queue, batch)) {
542 			g_queue_remove (queue->priv->batch_queue, batch);
543 			found = TRUE;
544 		}
545 	}
546 
547 	if (found) {
548 		_rb_track_transfer_batch_cancel (batch);
549 		g_object_unref (batch);
550 
551 		start_next_batch (queue);
552 	}
553 }
554 
555 /**
556  * rb_track_transfer_queue_get_status:
557  * @queue: the #RBTrackTransferQueue
558  * @text: returns the status bar text
559  * @progress_text: returns the progress bar text
560  * @progress: returns the progress fraction
561  * @time_left: returns the estimated number of seconds remaining
562  *
563  * Retrieves transfer status information.  Works similarly to
564  * #rb_source_get_status.
565  *
566  * Return value: TRUE if transfer status information is returned
567  */
568 gboolean
569 rb_track_transfer_queue_get_status (RBTrackTransferQueue *queue,
570 				    char **text,
571 				    char **progress_text,
572 				    float *progress,
573 				    int *time_left)
574 {
575 	int total;
576 	int done;
577 	double transfer_progress;
578 
579 	if (queue->priv->current == NULL) {
580 		return FALSE;
581 	}
582 
583 	g_object_get (queue->priv->current,
584 		      "total-entries", &total,
585 		      "done-entries", &done,
586 		      "progress", &transfer_progress,
587 		      NULL);
588 	if (total > 0) {
589 		char *s;
590 
591 		if (transfer_progress >= 0) {
592 			s = g_strdup_printf (_("Transferring track %d out of %d (%.0f%%)"),
593 					     done + 1, total, transfer_progress * 100);
594 		} else {
595 			s = g_strdup_printf (_("Transferring track %d out of %d"),
596 					     done + 1, total);
597 		}
598 
599 		g_free (*progress_text);
600 		*progress_text = s;
601 		*progress = transfer_progress;
602 
603 		*time_left = estimate_time_left (queue, transfer_progress);
604 
605 		return TRUE;
606 	}
607 	return FALSE;
608 }
609 
610 struct FindBatchData
611 {
612 	GList *results;
613 	RBSource *source;
614 };
615 
616 static void
617 find_batches (RBTrackTransferBatch *batch, struct FindBatchData *data)
618 {
619 	RBSource *src = NULL;
620 	RBSource *dest = NULL;
621 
622 	g_object_get (batch, "source", &src, "destination", &dest, NULL);
623 	if (src == data->source || dest == data->source) {
624 		data->results = g_list_prepend (data->results, batch);
625 	}
626 	g_object_unref (src);
627 	g_object_unref (dest);
628 }
629 
630 /**
631  * rb_track_transfer_queue_find_batch_by_source:
632  * @queue: the #RBTrackTransferQueue
633  * @source: the #RBSource to search for
634  *
635  * Finds all transfer batches where @source is the source or destination.
636  * This should be used to wait for transfers to finish (or cancel them) before
637  * ejecting a device.  The transfer batches are returned in the order they're
638  * found in the queue, so waiting for the @RBTrackTransferBatch::complete signal
639  * on the last one is sufficient to wait for them all to finish.
640  *
641  * Return value: (element-type RBTrackTransferBatch) (transfer container): #GList of #RBTrackTransferBatch objects, not referenced
642  */
643 GList *
644 rb_track_transfer_queue_find_batch_by_source (RBTrackTransferQueue *queue, RBSource *source)
645 {
646 	struct FindBatchData data;
647 	data.results = NULL;
648 	data.source = source;
649 
650 	/* check the current batch */
651 	find_batches (queue->priv->current, &data);
652 	g_queue_foreach (queue->priv->batch_queue, (GFunc) find_batches, &data);
653 	return data.results;
654 }
655 
656 static void
657 rb_track_transfer_queue_init (RBTrackTransferQueue *queue)
658 {
659 	queue->priv = G_TYPE_INSTANCE_GET_PRIVATE (queue,
660 						   RB_TYPE_TRACK_TRANSFER_QUEUE,
661 						   RBTrackTransferQueuePrivate);
662 
663 	queue->priv->batch_queue = g_queue_new ();
664 }
665 
666 
667 static void
668 impl_set_property (GObject *object,
669 		   guint prop_id,
670 		   const GValue *value,
671 		   GParamSpec *pspec)
672 {
673 	RBTrackTransferQueue *queue = RB_TRACK_TRANSFER_QUEUE (object);
674 
675 	switch (prop_id) {
676 	case PROP_SHELL:
677 		queue->priv->shell = g_value_get_object (value);
678 		break;
679 	default:
680 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
681 		break;
682 	}
683 }
684 
685 static void
686 impl_get_property (GObject *object,
687 		   guint prop_id,
688 		   GValue *value,
689 		   GParamSpec *pspec)
690 {
691 	RBTrackTransferQueue *queue = RB_TRACK_TRANSFER_QUEUE (object);
692 
693 	switch (prop_id) {
694 	case PROP_SHELL:
695 		g_value_set_object (value, queue->priv->shell);
696 		break;
697 	case PROP_BATCH:
698 		g_value_set_object (value, queue->priv->current);
699 		break;
700 	default:
701 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
702 		break;
703 	}
704 }
705 
706 static void
707 impl_dispose (GObject *object)
708 {
709 	RBTrackTransferQueue *queue = RB_TRACK_TRANSFER_QUEUE (object);
710 
711 	if (queue->priv->current != NULL) {
712 		_rb_track_transfer_batch_cancel (queue->priv->current);
713 		g_object_unref (queue->priv->current);
714 		queue->priv->current = NULL;
715 	}
716 
717 	if (queue->priv->batch_queue != NULL) {
718 		g_queue_foreach (queue->priv->batch_queue, (GFunc) _rb_track_transfer_batch_cancel, NULL);
719 		g_queue_foreach (queue->priv->batch_queue, (GFunc) g_object_unref, NULL);
720 		g_queue_free (queue->priv->batch_queue);
721 	}
722 
723 	if (queue->priv->shell != NULL) {
724 		/* we don't own a reference on the shell. */
725 		queue->priv->shell = NULL;
726 	}
727 
728 	G_OBJECT_CLASS (rb_track_transfer_queue_parent_class)->dispose (object);
729 }
730 
731 static void
732 rb_track_transfer_queue_class_init (RBTrackTransferQueueClass *klass)
733 {
734 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
735 
736 	object_class->set_property = impl_set_property;
737 	object_class->get_property = impl_get_property;
738 	object_class->dispose = impl_dispose;
739 
740 	/**
741 	 * RBTrackTransferQueue:shell:
742 	 *
743 	 * The #RBShell
744 	 */
745 	g_object_class_install_property (object_class,
746 					 PROP_SHELL,
747 					 g_param_spec_object ("shell",
748 							      "shell",
749 							      "the RBShell",
750 							      RB_TYPE_SHELL,
751 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
752 	/**
753 	 * RBTrackTransferQueue:batch:
754 	 *
755 	 * The current #RBTrackTransferBatch being processed
756 	 */
757 	g_object_class_install_property (object_class,
758 					 PROP_BATCH,
759 					 g_param_spec_object ("batch",
760 							      "batch",
761 							      "current RBTrackTransferBatch",
762 							      RB_TYPE_TRACK_TRANSFER_BATCH,
763 							      G_PARAM_READABLE));
764 	/**
765 	 * RBTrackTransferQueue::transfer-progress:
766 	 * @queue: the #RBTrackTransferQueue
767 	 * @done: the number of entries transferred
768 	 * @total: the total number of entries in the batch
769 	 * @fraction: the fraction of the batch that has been transferred
770 	 * @time_left: the estimated remaining time (in seconds)
771 	 *
772 	 * Emitted regularly to convey progress information.  At the end of any given
773 	 * transfer batch, there will be one signal emission with @done == @total and
774 	 * @fraction == 1.0.
775 	 */
776 	signals[TRANSFER_PROGRESS] =
777 		g_signal_new ("transfer-progress",
778 			      RB_TYPE_TRACK_TRANSFER_QUEUE,
779 			      G_SIGNAL_RUN_LAST,
780 			      G_STRUCT_OFFSET (RBTrackTransferQueueClass, transfer_progress),
781 			      NULL, NULL,
782 			      rb_marshal_VOID__INT_INT_DOUBLE_INT,
783 			      G_TYPE_NONE,
784 			      4, G_TYPE_INT, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_INT);
785 	/**
786 	 * RBTrackTransferQueue::missing-plugins:
787 	 * @queue: the #RBTrackTransferQueue
788 	 * @details: the list of plugin detail strings describing the missing plugins
789 	 * @descriptions: the list of descriptions for the missing plugins
790 	 * @closure: a #GClosure to be called when the plugin installation is complete
791 	 *
792 	 * Emitted to request installation of one or more encoder plugins for a
793 	 * destination media format.  When the closure included in the signal args
794 	 * is called, the transfer batch will be started.
795 	 */
796 	signals[MISSING_PLUGINS] =
797 		g_signal_new ("missing-plugins",
798 			      G_OBJECT_CLASS_TYPE (object_class),
799 			      G_SIGNAL_RUN_LAST,
800 			      0,
801 			      NULL, NULL,
802 			      rb_marshal_BOOLEAN__POINTER_POINTER_POINTER,
803 			      G_TYPE_BOOLEAN,
804 			      3,
805 			      G_TYPE_STRV, G_TYPE_STRV, G_TYPE_CLOSURE);
806 
807 	g_type_class_add_private (klass, sizeof (RBTrackTransferQueuePrivate));
808 }