2006-12-03 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / shell / rb-removable-media-manager.c
blobf9e8a44af73d94fb5e7d4e5bc99e446ed7017eaa
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * arch-tag: Implementation of Rhythmbox removable media manager
5 * Copyright (C) 2005 James Livingston <jrl@ids.org.au>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
25 #include <string.h>
26 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
28 #include <libgnomevfs/gnome-vfs.h>
30 #include "rb-removable-media-manager.h"
31 #include "rb-library-source.h"
32 #include "rb-sourcelist.h"
33 #include "rb-removable-media-source.h"
35 #include "rb-shell.h"
36 #include "rb-shell-player.h"
37 #include "rb-debug.h"
38 #include "rb-dialog.h"
39 #include "rb-stock-icons.h"
40 #include "rhythmdb.h"
41 #include "rb-marshal.h"
42 #include "rb-util.h"
44 #ifdef ENABLE_TRACK_TRANSFER
45 #include "rb-encoder.h"
46 #endif
48 static void rb_removable_media_manager_class_init (RBRemovableMediaManagerClass *klass);
49 static void rb_removable_media_manager_init (RBRemovableMediaManager *mgr);
50 static void rb_removable_media_manager_dispose (GObject *object);
51 static void rb_removable_media_manager_finalize (GObject *object);
52 static void rb_removable_media_manager_set_property (GObject *object,
53 guint prop_id,
54 const GValue *value,
55 GParamSpec *pspec);
56 static void rb_removable_media_manager_get_property (GObject *object,
57 guint prop_id,
58 GValue *value,
59 GParamSpec *pspec);
61 static void rb_removable_media_manager_cmd_scan_media (GtkAction *action,
62 RBRemovableMediaManager *manager);
63 static void rb_removable_media_manager_cmd_eject_medium (GtkAction *action,
64 RBRemovableMediaManager *mgr);
65 static void rb_removable_media_manager_set_uimanager (RBRemovableMediaManager *mgr,
66 GtkUIManager *uimanager);
68 static void rb_removable_media_manager_append_media_source (RBRemovableMediaManager *mgr, RBRemovableMediaSource *source);
70 static void rb_removable_media_manager_mount_volume (RBRemovableMediaManager *mgr,
71 GnomeVFSVolume *volume);
72 static void rb_removable_media_manager_unmount_volume (RBRemovableMediaManager *mgr,
73 GnomeVFSVolume *volume);
75 static void rb_removable_media_manager_volume_mounted_cb (GnomeVFSVolumeMonitor *monitor,
76 GnomeVFSVolume *volume,
77 gpointer data);
78 static void rb_removable_media_manager_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
79 GnomeVFSVolume *volume,
80 gpointer data);
81 static gboolean rb_removable_media_manager_load_media (RBRemovableMediaManager *manager);
83 #ifdef ENABLE_TRACK_TRANSFER
84 static void do_transfer (RBRemovableMediaManager *manager);
85 #endif
86 static void rb_removable_media_manager_cmd_copy_tracks (GtkAction *action,
87 RBRemovableMediaManager *mgr);
89 typedef struct
91 RBShell *shell;
92 gboolean disposed;
94 RBSourceList *sourcelist;
95 RBSource *selected_source;
97 GtkActionGroup *actiongroup;
98 GtkUIManager *uimanager;
100 GList *sources;
101 GHashTable *volume_mapping;
102 GList *cur_volume_list;
103 gboolean scanned;
105 GAsyncQueue *transfer_queue;
106 gboolean transfer_running;
107 gint transfer_total;
108 gint transfer_done;
109 double transfer_fraction;
110 } RBRemovableMediaManagerPrivate;
112 G_DEFINE_TYPE (RBRemovableMediaManager, rb_removable_media_manager, G_TYPE_OBJECT)
113 #define REMOVABLE_MEDIA_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_REMOVABLE_MEDIA_MANAGER, RBRemovableMediaManagerPrivate))
115 enum
117 PROP_0,
118 PROP_SHELL,
119 PROP_SOURCELIST,
120 PROP_SOURCE,
121 PROP_SCANNED
124 enum
126 MEDIUM_ADDED,
127 TRANSFER_PROGRESS,
128 CREATE_SOURCE,
129 LAST_SIGNAL
132 static guint rb_removable_media_manager_signals[LAST_SIGNAL] = { 0 };
134 static GtkActionEntry rb_removable_media_manager_actions [] =
136 { "RemovableSourceEject", GNOME_MEDIA_EJECT, N_("_Eject"), NULL,
137 N_("Eject this medium"),
138 G_CALLBACK (rb_removable_media_manager_cmd_eject_medium) },
139 { "RemovableSourceCopyAllTracks", GTK_STOCK_CDROM, N_("_Copy to library"), NULL,
140 N_("Copy all tracks to the library"),
141 G_CALLBACK (rb_removable_media_manager_cmd_copy_tracks) },
142 { "MusicScanMedia", NULL, N_("_Scan Removable Media"), NULL,
143 N_("Scan for new Removable Media"),
144 G_CALLBACK (rb_removable_media_manager_cmd_scan_media) },
146 static guint rb_removable_media_manager_n_actions = G_N_ELEMENTS (rb_removable_media_manager_actions);
148 static void
149 rb_removable_media_manager_class_init (RBRemovableMediaManagerClass *klass)
151 GObjectClass *object_class = G_OBJECT_CLASS (klass);
153 object_class->dispose = rb_removable_media_manager_dispose;
154 object_class->finalize = rb_removable_media_manager_finalize;
155 object_class->set_property = rb_removable_media_manager_set_property;
156 object_class->get_property = rb_removable_media_manager_get_property;
158 g_object_class_install_property (object_class,
159 PROP_SOURCE,
160 g_param_spec_object ("source",
161 "RBSource",
162 "RBSource object",
163 RB_TYPE_SOURCE,
164 G_PARAM_READWRITE));
165 g_object_class_install_property (object_class,
166 PROP_SHELL,
167 g_param_spec_object ("shell",
168 "RBShell",
169 "RBShell object",
170 RB_TYPE_SHELL,
171 G_PARAM_READWRITE));
173 g_object_class_install_property (object_class,
174 PROP_SOURCELIST,
175 g_param_spec_object ("sourcelist",
176 "RBSourceList",
177 "RBSourceList",
178 RB_TYPE_SOURCELIST,
179 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
180 g_object_class_install_property (object_class,
181 PROP_SCANNED,
182 g_param_spec_boolean ("scanned",
183 "scanned",
184 "Whether a scan has been performed",
185 FALSE,
186 G_PARAM_READABLE));
188 rb_removable_media_manager_signals[MEDIUM_ADDED] =
189 g_signal_new ("medium_added",
190 RB_TYPE_REMOVABLE_MEDIA_MANAGER,
191 G_SIGNAL_RUN_LAST,
192 G_STRUCT_OFFSET (RBRemovableMediaManagerClass, medium_added),
193 NULL, NULL,
194 g_cclosure_marshal_VOID__OBJECT,
195 G_TYPE_NONE,
196 1, G_TYPE_OBJECT);
198 rb_removable_media_manager_signals[TRANSFER_PROGRESS] =
199 g_signal_new ("transfer-progress",
200 RB_TYPE_REMOVABLE_MEDIA_MANAGER,
201 G_SIGNAL_RUN_LAST,
202 G_STRUCT_OFFSET (RBRemovableMediaManagerClass, transfer_progress),
203 NULL, NULL,
204 rb_marshal_VOID__INT_INT_DOUBLE,
205 G_TYPE_NONE,
206 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_DOUBLE);
208 rb_removable_media_manager_signals[CREATE_SOURCE] =
209 g_signal_new ("create-source",
210 RB_TYPE_REMOVABLE_MEDIA_MANAGER,
211 G_SIGNAL_RUN_LAST,
212 G_STRUCT_OFFSET (RBRemovableMediaManagerClass, create_source),
213 rb_signal_accumulator_object_handled, NULL,
214 rb_marshal_OBJECT__OBJECT,
215 RB_TYPE_SOURCE,
216 1, GNOME_VFS_TYPE_VOLUME);
218 g_type_class_add_private (klass, sizeof (RBRemovableMediaManagerPrivate));
221 static void
222 rb_removable_media_manager_init (RBRemovableMediaManager *mgr)
224 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
226 priv->volume_mapping = g_hash_table_new (NULL, NULL);
227 priv->transfer_queue = g_async_queue_new ();
229 g_idle_add ((GSourceFunc)rb_removable_media_manager_load_media, mgr);
232 static void
233 rb_removable_media_manager_dispose (GObject *object)
235 RBRemovableMediaManager *mgr = RB_REMOVABLE_MEDIA_MANAGER (object);
236 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
238 if (!priv->disposed)
240 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor ();
242 g_signal_handlers_disconnect_by_func (G_OBJECT (monitor),
243 G_CALLBACK (rb_removable_media_manager_volume_mounted_cb),
244 mgr);
245 g_signal_handlers_disconnect_by_func (G_OBJECT (monitor),
246 G_CALLBACK (rb_removable_media_manager_volume_unmounted_cb),
247 mgr);
250 if (priv->sources) {
251 g_list_free (priv->sources);
252 priv->sources = NULL;
255 priv->disposed = TRUE;
257 G_OBJECT_CLASS (rb_removable_media_manager_parent_class)->dispose (object);
260 static void
261 rb_removable_media_manager_finalize (GObject *object)
263 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (object);
265 g_hash_table_destroy (priv->volume_mapping);
266 g_async_queue_unref (priv->transfer_queue);
268 G_OBJECT_CLASS (rb_removable_media_manager_parent_class)->finalize (object);
271 static void
272 rb_removable_media_manager_set_property (GObject *object,
273 guint prop_id,
274 const GValue *value,
275 GParamSpec *pspec)
277 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (object);
279 switch (prop_id)
281 case PROP_SOURCE:
283 priv->selected_source = g_value_get_object (value);
284 break;
286 case PROP_SHELL:
288 GtkUIManager *uimanager;
290 priv->shell = g_value_get_object (value);
291 g_object_get (priv->shell,
292 "ui-manager", &uimanager,
293 NULL);
294 rb_removable_media_manager_set_uimanager (RB_REMOVABLE_MEDIA_MANAGER (object), uimanager);
295 g_object_unref (uimanager);
296 break;
298 case PROP_SOURCELIST:
299 priv->sourcelist = g_value_get_object (value);
300 break;
301 default:
302 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
303 break;
307 static void
308 rb_removable_media_manager_get_property (GObject *object,
309 guint prop_id,
310 GValue *value,
311 GParamSpec *pspec)
313 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (object);
315 switch (prop_id)
317 case PROP_SOURCE:
318 g_value_set_object (value, priv->selected_source);
319 break;
320 case PROP_SHELL:
321 g_value_set_object (value, priv->shell);
322 break;
323 case PROP_SOURCELIST:
324 g_value_set_object (value, priv->sourcelist);
325 break;
326 case PROP_SCANNED:
327 g_value_set_boolean (value, priv->scanned);
328 break;
329 default:
330 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331 break;
335 RBRemovableMediaManager *
336 rb_removable_media_manager_new (RBShell *shell,
337 RBSourceList *sourcelist)
339 return g_object_new (RB_TYPE_REMOVABLE_MEDIA_MANAGER,
340 "shell", shell,
341 "sourcelist", sourcelist,
342 NULL);
345 static gboolean
346 rb_removable_media_manager_load_media (RBRemovableMediaManager *manager)
348 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor ();
350 GDK_THREADS_ENTER ();
353 * Monitor new (un)mounted file systems to look for new media
355 * both pre-unmount and unmounted callbacks are registered because it is
356 * better to do it before the unmount, but sometimes we don't get those
357 * (e.g. someone pressing the eject button on a cd drive). If we get the
358 * pre-unmount signal, the corrosponding unmounted signal is ignored
360 g_signal_connect (G_OBJECT (monitor), "volume-mounted",
361 G_CALLBACK (rb_removable_media_manager_volume_mounted_cb),
362 manager);
363 g_signal_connect (G_OBJECT (monitor), "volume-pre-unmount",
364 G_CALLBACK (rb_removable_media_manager_volume_unmounted_cb),
365 manager);
366 g_signal_connect (G_OBJECT (monitor), "volume-unmounted",
367 G_CALLBACK (rb_removable_media_manager_volume_unmounted_cb),
368 manager);
370 GDK_THREADS_LEAVE ();
371 return FALSE;
374 static void
375 rb_removable_media_manager_volume_mounted_cb (GnomeVFSVolumeMonitor *monitor,
376 GnomeVFSVolume *volume,
377 gpointer data)
379 RBRemovableMediaManager *mgr = RB_REMOVABLE_MEDIA_MANAGER (data);
381 rb_removable_media_manager_mount_volume (mgr, volume);
384 static gboolean
385 remove_volume_by_source (GnomeVFSVolume *volume, RBSource *source,
386 RBSource *ref_source)
388 return (ref_source == source);
391 static void
392 rb_removable_media_manager_source_deleted_cb (RBSource *source, RBRemovableMediaManager *mgr)
394 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
396 rb_debug ("removing source %p", source);
397 g_hash_table_foreach_remove (priv->volume_mapping,
398 (GHRFunc)remove_volume_by_source,
399 source);
400 priv->sources = g_list_remove (priv->sources, source);
403 static void
404 rb_removable_media_manager_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
405 GnomeVFSVolume *volume,
406 gpointer data)
408 RBRemovableMediaManager *mgr = RB_REMOVABLE_MEDIA_MANAGER (data);
410 g_assert (volume != NULL);
411 rb_removable_media_manager_unmount_volume (mgr, volume);
414 static void
415 rb_removable_media_manager_mount_volume (RBRemovableMediaManager *mgr, GnomeVFSVolume *volume)
417 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
418 RBRemovableMediaSource *source = NULL;
419 char *fs_type, *device_path, *display_name, *hal_udi, *icon_name;
420 GnomeVFSDeviceType device_type;
422 g_assert (volume != NULL);
424 if (g_hash_table_lookup (priv->volume_mapping, volume) != NULL)
425 return;
427 if (!gnome_vfs_volume_is_mounted (volume))
428 return;
430 /* ignore network volumes */
431 device_type = gnome_vfs_volume_get_device_type (volume);
432 if (device_type == GNOME_VFS_DEVICE_TYPE_NFS ||
433 device_type == GNOME_VFS_DEVICE_TYPE_AUTOFS ||
434 device_type == GNOME_VFS_DEVICE_TYPE_SMB ||
435 device_type == GNOME_VFS_DEVICE_TYPE_NETWORK)
436 return;
438 fs_type = gnome_vfs_volume_get_filesystem_type (volume);
439 device_path = gnome_vfs_volume_get_device_path (volume);
440 display_name = gnome_vfs_volume_get_display_name (volume);
441 hal_udi = gnome_vfs_volume_get_hal_udi (volume);
442 icon_name = gnome_vfs_volume_get_icon (volume);
443 rb_debug ("detecting new media - device type=%d", device_type);
444 rb_debug ("detecting new media - volume type=%d", gnome_vfs_volume_get_volume_type (volume));
445 rb_debug ("detecting new media - fs type=%s", fs_type);
446 rb_debug ("detecting new media - device path=%s", device_path);
447 rb_debug ("detecting new media - display name=%s", display_name);
448 rb_debug ("detecting new media - hal udi=%s", hal_udi);
449 rb_debug ("detecting new media - icon=%s", icon_name);
451 /* rb_xxx_source_new first checks if the 'volume' parameter corresponds
452 * to a medium of type 'xxx', and returns NULL if it doesn't.
453 * When volume is of the appropriate type, it creates a new source
454 * to handle this volume
457 g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[CREATE_SOURCE], 0,
458 volume, &source);
460 if (source) {
461 g_hash_table_insert (priv->volume_mapping, volume, source);
462 rb_removable_media_manager_append_media_source (mgr, source);
463 } else {
464 rb_debug ("Unhandled media");
467 g_free (fs_type);
468 g_free (device_path);
469 g_free (display_name);
470 g_free (hal_udi);
471 g_free (icon_name);
474 static void
475 rb_removable_media_manager_unmount_volume (RBRemovableMediaManager *mgr, GnomeVFSVolume *volume)
477 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
478 RBRemovableMediaSource *source;
480 g_assert (volume != NULL);
482 rb_debug ("media removed");
483 source = g_hash_table_lookup (priv->volume_mapping, volume);
484 if (source) {
485 rb_source_delete_thyself (RB_SOURCE (source));
489 static void
490 rb_removable_media_manager_append_media_source (RBRemovableMediaManager *mgr, RBRemovableMediaSource *source)
492 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
494 priv->sources = g_list_prepend (priv->sources, source);
495 g_signal_connect_object (G_OBJECT (source), "deleted",
496 G_CALLBACK (rb_removable_media_manager_source_deleted_cb), mgr, 0);
498 g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[MEDIUM_ADDED], 0,
499 source);
502 static void
503 rb_removable_media_manager_set_uimanager (RBRemovableMediaManager *mgr,
504 GtkUIManager *uimanager)
506 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
508 if (priv->uimanager != NULL) {
509 if (priv->actiongroup != NULL) {
510 gtk_ui_manager_remove_action_group (priv->uimanager,
511 priv->actiongroup);
513 g_object_unref (G_OBJECT (priv->uimanager));
514 priv->uimanager = NULL;
517 priv->uimanager = uimanager;
519 if (priv->uimanager != NULL) {
520 g_object_ref (priv->uimanager);
523 if (priv->actiongroup == NULL) {
524 priv->actiongroup = gtk_action_group_new ("RemovableMediaActions");
525 gtk_action_group_set_translation_domain (priv->actiongroup,
526 GETTEXT_PACKAGE);
527 gtk_action_group_add_actions (priv->actiongroup,
528 rb_removable_media_manager_actions,
529 rb_removable_media_manager_n_actions,
530 mgr);
533 #ifndef ENABLE_TRACK_TRANSFER
535 GtkAction *action;
537 action = gtk_action_group_get_action (priv->actiongroup, "RemovableSourceCopyAllTracks");
538 gtk_action_set_visible (action, FALSE);
540 #endif
542 gtk_ui_manager_insert_action_group (priv->uimanager,
543 priv->actiongroup,
547 static void
548 rb_removable_media_manager_eject_medium_cb (gboolean succeeded,
549 const char *error,
550 const char *detailed_error,
551 gpointer *data)
553 if (succeeded)
554 return;
556 rb_error_dialog (NULL, error, "%s", detailed_error);
559 static void
560 rb_removable_media_manager_cmd_eject_medium (GtkAction *action, RBRemovableMediaManager *mgr)
562 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
563 RBRemovableMediaSource *source = RB_REMOVABLE_MEDIA_SOURCE (priv->selected_source);
564 GnomeVFSVolume *volume;
566 g_object_get (source, "volume", &volume, NULL);
567 rb_removable_media_manager_unmount_volume (mgr, volume);
568 gnome_vfs_volume_eject (volume, (GnomeVFSVolumeOpCallback)rb_removable_media_manager_eject_medium_cb, mgr);
569 gnome_vfs_volume_unref (volume);
572 static void
573 rb_removable_media_manager_cmd_scan_media (GtkAction *action, RBRemovableMediaManager *manager)
575 rb_removable_media_manager_scan (manager);
578 struct VolumeCheckData
580 RBRemovableMediaManager *manager;
581 GList *volume_list;
582 GList *volumes_to_remove;
585 static void
586 rb_removable_media_manager_check_volume (GnomeVFSVolume *volume,
587 RBRemovableMediaSource *source,
588 struct VolumeCheckData *check_data)
590 /* if the volume is no longer present, queue it for removal */
591 if (g_list_find (check_data->volume_list, volume) == NULL)
592 check_data->volumes_to_remove = g_list_prepend (check_data->volumes_to_remove, volume);
595 static void
596 rb_removable_media_manager_unmount_volume_swap (GnomeVFSVolume *volume, RBRemovableMediaManager *manager)
598 rb_removable_media_manager_unmount_volume (manager, volume);
601 void
602 rb_removable_media_manager_scan (RBRemovableMediaManager *manager)
604 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (manager);
605 GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor ();
606 GList *list, *it;
607 GnomeVFSVolume *volume;
608 struct VolumeCheckData check_data;
610 priv->scanned = TRUE;
612 list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
614 /* see if any removable media has gone */
615 check_data.volume_list = list;
616 check_data.manager = manager;
617 check_data.volumes_to_remove = NULL;
618 g_hash_table_foreach (priv->volume_mapping,
619 (GHFunc) rb_removable_media_manager_check_volume,
620 &check_data);
621 g_list_foreach (check_data.volumes_to_remove,
622 (GFunc) rb_removable_media_manager_unmount_volume_swap,
623 manager);
624 g_list_free (check_data.volumes_to_remove);
626 /* look for new volume media */
627 for (it = list; it != NULL; it = g_list_next (it)) {
628 volume = GNOME_VFS_VOLUME (it->data);
629 rb_removable_media_manager_mount_volume (manager, volume);
630 gnome_vfs_volume_unref (volume);
632 g_list_free (list);
635 #ifdef ENABLE_TRACK_TRANSFER
636 /* Track transfer */
638 typedef struct {
639 RBRemovableMediaManager *manager;
640 RhythmDBEntry *entry;
641 char *dest;
642 char *mime_type;
643 gboolean failed;
644 RBTranferCompleteCallback callback;
645 gpointer userdata;
646 } TransferData;
648 static void
649 emit_progress (RBRemovableMediaManager *mgr)
651 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
653 g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[TRANSFER_PROGRESS], 0,
654 priv->transfer_done,
655 priv->transfer_total,
656 priv->transfer_fraction);
659 static void
660 error_cb (RBEncoder *encoder, GError *error, TransferData *data)
662 rb_debug ("Error transferring track to %s: %s", data->dest, error->message);
663 rb_error_dialog (NULL, _("Error transferring track"), "%s", error->message);
665 data->failed = TRUE;
666 rb_encoder_cancel (encoder);
669 static void
670 progress_cb (RBEncoder *encoder, double fraction, TransferData *data)
672 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (data->manager);
674 rb_debug ("transfer progress %f", (float)fraction);
675 priv->transfer_fraction = fraction;
676 emit_progress (data->manager);
679 static void
680 completed_cb (RBEncoder *encoder, TransferData *data)
682 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (data->manager);
684 rb_debug ("completed transferring track to %s", data->dest);
685 if (!data->failed)
686 (data->callback) (data->entry, data->dest, data->userdata);
688 priv->transfer_running = FALSE;
689 priv->transfer_done++;
690 priv->transfer_fraction = 0.0;
691 do_transfer (data->manager);
693 g_object_unref (G_OBJECT (encoder));
694 g_free (data->dest);
695 g_free (data->mime_type);
696 g_free (data);
699 static void
700 do_transfer (RBRemovableMediaManager *manager)
702 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (manager);
703 TransferData *data;
704 RBEncoder *encoder;
706 g_assert (rb_is_main_thread ());
708 emit_progress (manager);
710 if (priv->transfer_running)
711 return;
713 data = g_async_queue_try_pop (priv->transfer_queue);
714 if (data == NULL) {
715 priv->transfer_total = 0;
716 priv->transfer_done = 0;
717 emit_progress (manager);
718 return;
721 priv->transfer_running = TRUE;
722 priv->transfer_fraction = 0.0;
724 encoder = rb_encoder_new ();
725 g_signal_connect (G_OBJECT (encoder),
726 "error", G_CALLBACK (error_cb),
727 data);
728 g_signal_connect (G_OBJECT (encoder),
729 "progress", G_CALLBACK (progress_cb),
730 data);
731 g_signal_connect (G_OBJECT (encoder),
732 "completed", G_CALLBACK (completed_cb),
733 data);
734 rb_encoder_encode (encoder, data->entry, data->dest, NULL);
737 void
738 rb_removable_media_manager_queue_transfer (RBRemovableMediaManager *manager,
739 RhythmDBEntry *entry,
740 const char *dest,
741 const char *mime_type,
742 RBTranferCompleteCallback callback,
743 gpointer userdata)
745 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (manager);
746 TransferData *data;
748 g_assert (rb_is_main_thread ());
750 data = g_new0 (TransferData, 1);
751 data->manager = manager;
752 data->entry = entry;
753 data->dest = g_strdup (dest);
754 data->mime_type = g_strdup (mime_type);
755 data->callback = callback;
756 data->userdata = userdata;
758 g_async_queue_push (priv->transfer_queue, data);
759 priv->transfer_total++;
760 do_transfer (manager);
763 static gboolean
764 copy_entry (RhythmDBQueryModel *model,
765 GtkTreePath *path,
766 GtkTreeIter *iter,
767 GList **list)
769 GList *l;
770 l = g_list_append (*list, rhythmdb_query_model_iter_to_entry (model, iter));
771 *list = l;
772 return FALSE;
774 #endif
776 static void
777 rb_removable_media_manager_cmd_copy_tracks (GtkAction *action, RBRemovableMediaManager *mgr)
779 #ifdef ENABLE_TRACK_TRANSFER
780 RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
781 RBRemovableMediaSource *source;
782 RBLibrarySource *library;
783 RhythmDBQueryModel *model;
784 GList *list = NULL;
786 source = RB_REMOVABLE_MEDIA_SOURCE (priv->selected_source);
787 g_object_get (source, "query-model", &model, NULL);
788 g_object_get (priv->shell, "library-source", &library, NULL);
790 gtk_tree_model_foreach (GTK_TREE_MODEL (model), (GtkTreeModelForeachFunc)copy_entry, &list);
791 rb_source_paste (RB_SOURCE (library), list);
792 g_list_free (list);
794 g_object_unref (model);
795 g_object_unref (library);
796 #endif