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