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 }