diff -u -p -r1.255 configure.ac
--- configure.ac	11 Aug 2006 05:32:53 -0000	1.255
+++ configure.ac	15 Aug 2006 09:30:01 -0000
@@ -513,6 +513,19 @@ if test x"$enable_musicbrainz" = xyes; t
 fi
 AM_CONDITIONAL(HAVE_MUSICBRAINZ, test "x$enable_musicbrainz" = "xyes")
 
+dnl check for Mathusalem
+AC_ARG_WITH(mathusalem, AC_HELP_STRING([--without-mathusalem],
+				[don't build with Mathusalem support]))
+if test x"$with_mathusalem" != "xno"; then
+	PKG_CHECK_MODULES(MATHUSALEM, mathusalem, with_mathusalem=yes,
+		          with_mathusalem=no)
+fi
+if test x"$with_mathusalem" = xyes; then
+	AC_SUBST(MATHUSALEM_CFLAGS)
+	AC_SUBST(MATHUSALEM_LIBS)
+	AC_DEFINE(HAVE_MATHUSALEM, 1, [define if you have Mathusalem])
+fi
+AM_CONDITIONAL(USE_MATHUSALEM, test "x$with_mathusalem" = "xyes")
 
 
 AC_PATH_X
@@ -975,7 +988,9 @@ tests/Makefile
 doc/Makefile
 doc/reference/Makefile
 backends/Makefile
+backends/core/Makefile
 backends/gstreamer/Makefile
+backends/mathusalem/Makefile
 remote/Makefile
 remote/dbus/Makefile
 ])
@@ -1079,6 +1094,11 @@ if test x"$with_internal_libsexy" = xyes
 	AC_MSG_NOTICE([   using internal libsexy])
 else
 	AC_MSG_NOTICE([** using system-wide libsexy])
+fi
+if test x"$with_mathusalem" != "xyes"; then
+	AC_MSG_NOTICE([   Mathusalem support is disabled])
+else
+	AC_MSG_NOTICE([** Mathusalem support is enabled])
 fi
 
 AC_MSG_NOTICE([End options])
diff -u -p -r1.6 Makefile.am
--- backends/Makefile.am	12 Jul 2006 02:52:34 -0000	1.6
+++ backends/Makefile.am	15 Aug 2006 09:30:02 -0000
@@ -2,15 +2,22 @@
 
 noinst_LTLIBRARIES = librbbackends.la
 
-SUBDIRS = gstreamer 
+SUBDIRS = core gstreamer 
+
+if USE_MATHUSALEM
+SUBDIRS += mathusalem
+endif
 
 librbbackends_la_SOURCES =				\
 	rb-player.h					\
 	rb-player.c					\
+	rb-task.h					\
+	rb-task.c					\
 	$(NULL)
 
 librbbackends_la_LIBADD =				\
 	gstreamer/librbbackendsgstreamer.la		\
+	core/librbbackendscore.la			\
 	$(RHYTHMBOX_LIBS)
 
 librbbackends_la_LDFLAGS = -export-dynamic
@@ -31,5 +38,9 @@ if ENABLE_TRACK_TRANSFER
 librbbackends_la_SOURCES +=	\
 	rb-encoder.h 		\
 	rb-encoder.c
+endif
+
+if USE_MATHUSALEM
+librbbackends_la_LIBADD += mathusalem/librbbackendsmathusalem.la
 endif
 
diff -u -p -r1.20 rb-marshal.list
--- lib/rb-marshal.list	7 Jul 2006 22:41:42 -0000	1.20
+++ lib/rb-marshal.list	15 Aug 2006 09:30:05 -0000
@@ -1,5 +1,6 @@
 BOOLEAN:BOOLEAN,BOOLEAN,BOOLEAN
 BOOLEAN:POINTER
+BOOLEAN:VOID
 INT:VOID
 OBJECT:OBJECT
 STRING:STRING
diff -u -p -r1.31 rb-util.c
--- lib/rb-util.c	30 Jul 2006 13:54:55 -0000	1.31
+++ lib/rb-util.c	15 Aug 2006 09:30:05 -0000
@@ -691,24 +691,38 @@ rb_string_list_equal (GList *a, GList *b
 	return ret;
 }
 
+typedef struct {
+	GList **list;
+	GBoxedCopyFunc func;
+} RBListCopyData;
+
 static void
-list_copy_cb (const char *s, GList **list)
+list_copy_cb (gpointer d, RBListCopyData *data)
 {
-	*list = g_list_prepend (*list, g_strdup (s));
+	*data->list = g_list_prepend (*data->list, data->func (d));
 }
 
 GList *
-rb_string_list_copy (GList *list)
+rb_list_copy (GList *list, GBoxedCopyFunc copy_func)
 {
 	GList *copy = NULL;
+	RBListCopyData data;
 	
 	if (list == NULL)
 		return NULL;
 
-	g_list_foreach (list, (GFunc)list_copy_cb, &copy);
+	data.func = copy_func;
+	data.list = &copy;
+	g_list_foreach (list, (GFunc)list_copy_cb, &data);
 	copy = g_list_reverse (copy);
 
 	return copy;
+}
+
+GList *
+rb_string_list_copy (GList *list)
+{
+	return rb_list_copy (list, (GBoxedCopyFunc)g_strdup);
 }
 
 void
diff -u -p -r1.24 rb-util.h
--- lib/rb-util.h	30 Jul 2006 07:37:52 -0000	1.24
+++ lib/rb-util.h	15 Aug 2006 09:30:05 -0000
@@ -57,6 +57,7 @@ gchar** rb_string_split_words (const gch
 gboolean rb_string_list_equal (GList *a, GList *b);
 void rb_list_deep_free (GList *list);
 GList* rb_string_list_copy (GList *list);
+GList* rb_list_copy (GList *list, GBoxedCopyFunc copy_func);
 
 GList* rb_collate_hash_table_keys (GHashTable *table);
 GList* rb_collate_hash_table_values (GHashTable *table);
diff -u -p -r1.31 rb-removable-media-manager.c
--- shell/rb-removable-media-manager.c	10 Aug 2006 05:35:08 -0000	1.31
+++ shell/rb-removable-media-manager.c	15 Aug 2006 09:30:10 -0000
@@ -54,6 +54,7 @@
 
 #ifdef ENABLE_TRACK_TRANSFER
 #include "rb-encoder.h"
+#include "rb-task.h"
 #endif
 
 #ifndef HAVE_BURN_DRIVE_UNREF
@@ -95,9 +96,6 @@ static void  rb_removable_media_manager_
 				gpointer data);
 static gboolean rb_removable_media_manager_load_media (RBRemovableMediaManager *manager);
 
-#ifdef ENABLE_TRACK_TRANSFER
-static void do_transfer (RBRemovableMediaManager *manager);
-#endif
 static void rb_removable_media_manager_cmd_copy_tracks (GtkAction *action,
 							RBRemovableMediaManager *mgr);
 
@@ -121,12 +119,6 @@ typedef struct
 	gboolean scanned;
 
 	char *playing_uri;
-
-	GAsyncQueue *transfer_queue;
-	gboolean transfer_running;
-	gint transfer_total;
-	gint transfer_done;
-	double transfer_fraction;
 } RBRemovableMediaManagerPrivate;
 
 G_DEFINE_TYPE (RBRemovableMediaManager, rb_removable_media_manager, G_TYPE_OBJECT)
@@ -144,7 +136,6 @@ enum
 enum
 {
 	MEDIUM_ADDED,
-	TRANSFER_PROGRESS,
 	CREATE_SOURCE,
 	LAST_SIGNAL
 };
@@ -215,16 +206,6 @@ rb_removable_media_manager_class_init (R
 			      G_TYPE_NONE,
 			      1, G_TYPE_OBJECT);
 
-	rb_removable_media_manager_signals[TRANSFER_PROGRESS] =
-		g_signal_new ("transfer-progress",
-			      RB_TYPE_REMOVABLE_MEDIA_MANAGER,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (RBRemovableMediaManagerClass, transfer_progress),
-			      NULL, NULL,
-			      rb_marshal_VOID__INT_INT_DOUBLE,
-			      G_TYPE_NONE,
-			      3, G_TYPE_INT, G_TYPE_INT, G_TYPE_DOUBLE);
-
 	rb_removable_media_manager_signals[CREATE_SOURCE] =
 		g_signal_new ("create-source",
 			      RB_TYPE_REMOVABLE_MEDIA_MANAGER,
@@ -244,7 +225,6 @@ rb_removable_media_manager_init (RBRemov
 	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
 
 	priv->volume_mapping = g_hash_table_new (NULL, NULL);
-	priv->transfer_queue = g_async_queue_new ();
 
 	g_idle_add ((GSourceFunc)rb_removable_media_manager_load_media, mgr);
 }
@@ -286,7 +266,6 @@ rb_removable_media_manager_finalize (GOb
 #if !NAUTILUS_BURN_CHECK_VERSION(2,15,3)
 	g_hash_table_destroy (priv->cd_drive_mapping);
 #endif
-	g_async_queue_unref (priv->transfer_queue);
 
 	G_OBJECT_CLASS (rb_removable_media_manager_parent_class)->finalize (object);
 }
@@ -887,30 +866,55 @@ rb_removable_media_manager_scan (RBRemov
 /* Track transfer */
 
 typedef struct {
-	RBRemovableMediaManager *manager;
-	RhythmDBEntry *entry;
-	char *dest;
-	char *mime_type;
-	gboolean failed;
+	GList *entries;
+	GList *dests;
+	GList *mime_types;
+
+	RBTask *task;
+
 	RBTranferCompleteCallback callback;
 	gpointer userdata;
+	GDestroyNotify destroy;
+
+	guint total_tracks;
+	guint current_track;
+	double fraction;
+
+	RBEncoder *encoder;
+	gboolean failed;
 } TransferData;
 
+static gboolean start_transfer (TransferData *data);
+
 static void
-emit_progress (RBRemovableMediaManager *mgr)
+transfer_data_destroy (TransferData *data)
 {
-	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (mgr);
+	data->destroy (data->userdata);
+
+	g_list_foreach (data->entries, (GFunc)rhythmdb_entry_unref, NULL);
+	g_list_free (data->entries);
+	data->entries = NULL;
+	rb_list_deep_free (data->dests);
+	data->dests = NULL;
 
-	g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[TRANSFER_PROGRESS], 0,
-		       priv->transfer_done,
-		       priv->transfer_total,
-		       priv->transfer_fraction);
+	g_object_unref (data->task);
+
+	g_free (data);
+}
+
+static void
+emit_progress (TransferData *data)
+{
+	double f;
+
+	f = (data->current_track + data->fraction) / data->total_tracks;
+	g_object_set (data->task, "fraction", (f <= 1.0) ? f : 1.0, NULL);
 }
 
 static void
 error_cb (RBEncoder *encoder, GError *error, TransferData *data)
 {
-	rb_debug ("Error transferring track to %s: %s", data->dest, error->message);
+	rb_debug ("Error transferring track %s: %s", (char*)data->dests->data, (char*)error->message);
 	rb_error_dialog (NULL, _("Error transferring track"), "%s", error->message);
 
 	data->failed = TRUE;
@@ -920,96 +924,141 @@ error_cb (RBEncoder *encoder, GError *er
 static void
 progress_cb (RBEncoder *encoder, double fraction, TransferData *data)
 {
-	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (data->manager);
-
-	rb_debug ("transfer progress %f", (float)fraction);
-	priv->transfer_fraction = fraction;
-	emit_progress (data->manager);
+	data->fraction = fraction;
+	emit_progress (data);
 }
 
 static void
 completed_cb (RBEncoder *encoder, TransferData *data)
 {
-	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (data->manager);
+	rb_debug ("completed transferring track to %s", (char*)data->dests->data);
+
+	if (!data->failed) {
+		data->callback ((RhythmDBEntry*)data->entries->data,
+				  (char*)data->dests->data,
+				  data->userdata);
+	}
 
-	rb_debug ("completed transferring track to %s", data->dest);
-	if (!data->failed)
-		(data->callback) (data->entry, data->dest, data->userdata);
-
-	priv->transfer_running = FALSE;
-	priv->transfer_done++;
-	priv->transfer_fraction = 0.0;
-	do_transfer (data->manager);
+	rhythmdb_entry_unref ((RhythmDBEntry*)data->entries->data);
+	data->entries = g_list_delete_link (data->entries, data->entries);
+	g_free (data->dests->data);
+	data->dests = g_list_delete_link (data->dests, data->dests);
 
 	g_object_unref (G_OBJECT (encoder));
-	g_free (data->dest);
-	g_free (data->mime_type);
-	g_free (data);
+
+	data->current_track++;
+	data->fraction = 0.0;
+	emit_progress (data);
+
+	if (data->entries) {
+		g_idle_add ((GSourceFunc)start_transfer, data);
+	} else {
+		rb_task_completed (data->task);
+		transfer_data_destroy (data);
+	}
 }
 
-static void
-do_transfer (RBRemovableMediaManager *manager)
+static gboolean
+start_transfer (TransferData *data)
 {
-	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (manager);
-	TransferData *data;
-	RBEncoder *encoder;
+	RhythmDBEntry *entry;
+	char *dest;
+	char *title;
 
 	g_assert (rb_is_main_thread ());
 
-	emit_progress (manager);
+	data->fraction = 0.0;
+	data->failed = FALSE;
+	emit_progress (data);
+
+	entry = (RhythmDBEntry*)data->entries->data;
+	dest = (char*)data->dests->data;
+
+	title = g_strdup_printf (_("%s - %s (%d of %d)"),
+				 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
+				 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
+				 data->current_track + 1, data->total_tracks);
+	g_object_get (data->task, "description", title, NULL);
+	g_free (title);
 
-	if (priv->transfer_running)
-		return;
-
-	data = g_async_queue_try_pop (priv->transfer_queue);
-	if (data == NULL) {
-		priv->transfer_total = 0;
-		priv->transfer_done = 0;
-		emit_progress (manager);
-		return;
-	}
-
-	priv->transfer_running = TRUE;
-	priv->transfer_fraction = 0.0;
-
-	encoder = rb_encoder_new ();
-	g_signal_connect (G_OBJECT (encoder),
+	data->encoder = rb_encoder_new ();
+	g_signal_connect (G_OBJECT (data->encoder),
 			  "error", G_CALLBACK (error_cb),
 			  data);
-	g_signal_connect (G_OBJECT (encoder),
+	g_signal_connect (G_OBJECT (data->encoder),
 			  "progress", G_CALLBACK (progress_cb),
 			  data);
-	g_signal_connect (G_OBJECT (encoder),
+	g_signal_connect (G_OBJECT (data->encoder),
 			  "completed", G_CALLBACK (completed_cb),
 			  data);
-	rb_encoder_encode (encoder, data->entry, data->dest, NULL);
+
+	if (data->mime_types) {
+		/* FIXME: pass the list, instead of first mime-type */
+		rb_encoder_encode (data->encoder, entry, dest, (const char *)data->mime_types->data);
+	} else {
+		rb_encoder_encode (data->encoder, entry, dest, NULL);
+	}
+
+	return FALSE;
+}
+
+static void
+abort_cb (RBTask *task, TransferData *data)
+{
+	g_signal_handlers_disconnect_by_func (data->encoder, completed_cb, data);
+	if (data->encoder) {
+		rb_encoder_cancel (data->encoder);
+		g_object_unref (data->encoder);
+	}
+
+	rb_task_completed (data->task); /* we seem to need this */
+	transfer_data_destroy (data);
 }
 
 void
 rb_removable_media_manager_queue_transfer (RBRemovableMediaManager *manager,
-					  RhythmDBEntry *entry,
-					  const char *dest,
-					  const char *mime_type,
-					  RBTranferCompleteCallback callback,
-					  gpointer userdata)
+					   GList *entries,
+					   GList *dests,
+					   GList *mime_types,
+					   RBTranferCompleteCallback callback,
+					   gpointer userdata,
+					   GDestroyNotify destroy)
 {
-	RBRemovableMediaManagerPrivate *priv = REMOVABLE_MEDIA_MANAGER_GET_PRIVATE (manager);
 	TransferData *data;
+	RBShell *shell;
 
-	g_assert (rb_is_main_thread ());
+	g_return_if_fail (rb_is_main_thread ());
+	g_return_if_fail (entries != NULL);
+	g_return_if_fail (rb_is_main_thread ());
 
 	data = g_new0 (TransferData, 1);
-	data->manager = manager;
-	data->entry = entry;
-	data->dest = g_strdup (dest);
-	data->mime_type = g_strdup (mime_type);
+	data->total_tracks = g_list_length (entries);
+
+	g_return_if_fail (g_list_length (dests) == data->total_tracks);
+
+	data->entries = rb_list_copy (entries, (GBoxedCopyFunc)rhythmdb_entry_ref);
+	data->dests = rb_string_list_copy (dests);
+	data->mime_types = rb_string_list_copy (mime_types);
+
 	data->callback = callback;
 	data->userdata = userdata;
+	data->destroy = destroy;
+
+	g_object_get (manager, "shell", &shell, NULL);
+	data->task = rb_task_new (shell);
+	g_object_unref (shell);
+
+	g_object_set (data->task,
+		      "title", _("Transferring tracks"),
+		      "abortable", TRUE,
+		      NULL);
+	g_signal_connect (data->task,
+			  "abort", (GCallback)abort_cb,
+			  data);
 
-	g_async_queue_push (priv->transfer_queue, data);
-	priv->transfer_total++;
-	do_transfer (manager);
+	g_idle_add ((GSourceFunc)start_transfer, data);
 }
+
 
 static gboolean
 copy_entry (RhythmDBQueryModel *model,
diff -u -p -r1.6 rb-removable-media-manager.h
--- shell/rb-removable-media-manager.h	14 Jul 2006 12:30:51 -0000	1.6
+++ shell/rb-removable-media-manager.h	15 Aug 2006 09:30:10 -0000
@@ -54,10 +54,6 @@ typedef struct
 	/* signals */
 	void	(*medium_added)		(RBRemovableMediaManager *mgr,
 					 RBSource *source);
-	void	(*transfer_progress)	(RBRemovableMediaManager *mgr,
-					 gint done,
-					 gint total,
-					 double fraction);
 	RBSource * (*create_source)	(RBRemovableMediaManager *mgr,
 					 GnomeVFSVolume *volume);
 } RBRemovableMediaManagerClass;
@@ -69,11 +65,12 @@ void			rb_removable_media_manager_scan (
 
 #ifdef ENABLE_TRACK_TRANSFER
 void	rb_removable_media_manager_queue_transfer (RBRemovableMediaManager *mgr,
-						   RhythmDBEntry *entry,
-						   const char *dest,
-						   const char *mime_type,
-						   RBTranferCompleteCallback callback,
-						   gpointer userdata);
+						   GList *entries, /* RhythmDBEntry* */
+						   GList *dest, /* char*, matching above */
+						   GList *mime_types, /* char*, prefferred order, NULL=any */
+						   RBTranferCompleteCallback callback, /* called per file */
+						   gpointer userdata,
+						   GDestroyNotify destroy); /* called at end */
 #endif
 
 G_END_DECLS
diff -u -p -r1.470 rb-shell.c
--- shell/rb-shell.c	10 Aug 2006 05:35:08 -0000	1.470
+++ shell/rb-shell.c	15 Aug 2006 09:30:10 -0000
@@ -149,11 +149,6 @@ static void rb_shell_druid_response_cb (
 static void rb_shell_playlist_added_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
 static void rb_shell_playlist_created_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
 static void rb_shell_medium_added_cb (RBRemovableMediaManager *mgr, RBSource *source, RBShell *shell);
-static void rb_shell_transfer_progress_cb (RBRemovableMediaManager *mgr,
-					   gint done,
-					   gint total,
-					   double fraction,
-					   RBShell *shell);
 static void rb_shell_source_deleted_cb (RBSource *source, RBShell *shell);
 static void rb_shell_set_window_title (RBShell *shell, const char *window_title);
 static void rb_shell_set_elapsed (RBShell *shell, guint elapsed);
@@ -1212,8 +1207,6 @@ construct_sources (RBShell *shell)
 
 	g_signal_connect_object (G_OBJECT (shell->priv->removable_media_manager), "medium_added",
 				 G_CALLBACK (rb_shell_medium_added_cb), shell, 0);
-	g_signal_connect_object (G_OBJECT (shell->priv->removable_media_manager), "transfer-progress",
-				 G_CALLBACK (rb_shell_transfer_progress_cb), shell, 0);
 
 	rb_profile_end ("constructing sources");
 }
@@ -1788,33 +1781,6 @@ rb_shell_medium_added_cb (RBRemovableMed
 			  RBShell *shell)
 {
 	rb_shell_append_source (shell, source, NULL);
-}
-
-static void
-rb_shell_transfer_progress_cb (RBRemovableMediaManager *mgr,
-			       gint done,
-			       gint total,
-			       double fraction,
-			       RBShell *shell)
-{
-	rb_debug ("transferred %d tracks out of %d", done, total);
-
-	if (total > 0) {
-		char *s;
-
-		if (fraction >= 0)
-			s = g_strdup_printf (_("Transferring track %d out of %d (%.0f%%)"),
-						   done + 1, total, fraction * 100);
-		else
-			s = g_strdup_printf (_("Transferring track %d out of %d"),
-						   done + 1, total);
-
-		rb_statusbar_set_progress (shell->priv->statusbar,
-					   (((double)(done) + fraction)/total),
-					   s);
-	} else {
-		rb_statusbar_set_progress (shell->priv->statusbar, -1, NULL);
-	}
 }
 
 static void
diff -u -p -r1.184 rb-library-source.c
--- sources/rb-library-source.c	14 Aug 2006 09:03:24 -0000	1.184
+++ sources/rb-library-source.c	15 Aug 2006 09:30:10 -0000
@@ -1157,7 +1157,7 @@ impl_can_paste (RBSource *asource)
 
 #ifdef ENABLE_TRACK_TRANSFER
 static void
-completed_cb (RhythmDBEntry *entry, const char *dest, RBLibrarySource *source)
+xfer_entry_cb (RhythmDBEntry *entry, const char *dest, RBLibrarySource *source)
 {
 	rhythmdb_add_uri (source->priv->db, dest);
 }
@@ -1171,6 +1171,8 @@ impl_paste (RBSource *asource, GList *en
 	GSList *sl;
 	RBShell *shell;
 	RhythmDBEntryType source_entry_type;
+	GList *xfer_entries = NULL;
+	GList *xfer_dests = NULL;
 
 	sl = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
 	if ((sl == NULL) ||
@@ -1211,12 +1213,20 @@ impl_paste (RBSource *asource, GList *en
 			continue;
 		}
 
-		rb_removable_media_manager_queue_transfer (rm_mgr, entry,
-							  dest, NULL,
-							  (RBTranferCompleteCallback)completed_cb, source);
+		xfer_entries = g_list_prepend (xfer_entries, entry);
+		xfer_dests = g_list_prepend (xfer_dests, dest);
 	}
 	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, source_entry_type);
 
+	xfer_entries = g_list_reverse (xfer_entries);
+	xfer_dests = g_list_reverse (xfer_dests);
+
+	rb_removable_media_manager_queue_transfer (rm_mgr,
+						   xfer_entries, xfer_dests, NULL,
+						   (RBTranferCompleteCallback)xfer_entry_cb, g_object_ref (source),
+						   g_object_unref);
+	g_list_free (xfer_entries);
+	rb_list_deep_free (xfer_dests);
 	g_object_unref (rm_mgr);
 }
 #endif
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/rb-task.h	2006-08-15 17:38:40.000000000 +1000
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * arch-tag: Interface to audio task backend
+ *
+ * Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#ifndef __RB_TASK_H__
+#define __RB_TASK_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_TASK            (rb_task_get_type ())
+#define RB_TASK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_TASK, RBTask))
+#define RB_IS_TASK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_TASK))
+#define RB_TASK_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), RB_TYPE_TASK, RBTaskIface))
+
+
+/*
+ * Properties:
+ *
+ * char*	title (short)
+ * char*	description (long)
+ * char*	stock-icon
+ * double	fration (less than 0 is "unknown")
+ * gboolean	pausable
+ * gboolean	abortable
+ */
+
+typedef struct _RBTask RBTask;
+
+typedef struct
+{
+	GTypeInterface g_iface;
+
+	/* vtable */
+	void	(*completed)		(RBTask *task);
+
+	/* signals */
+	void		(*pause_resume)		(RBTask *task);
+	void		(*abort)		(RBTask *task);
+} RBTaskIface;
+
+RBTask*	rb_task_new		(gpointer shell);
+GType rb_task_get_type (void);
+
+void		rb_task_completed	(RBTask *task);
+
+/* only to be used by subclasses */
+void		_rb_task_emit_pause_resume (RBTask *task);
+void		_rb_task_emit_abort (RBTask *task);
+
+G_END_DECLS
+
+#endif /* __RB_TASK_H__ */
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/rb-task.c	2006-08-15 17:39:44.000000000 +1000
@@ -0,0 +1,159 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2006  James Livingston  <jrl@ids.org.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include <config.h>
+
+#include "rb-marshal.h"
+#include "rb-task.h"
+
+#ifdef HAVE_MATHUSALEM
+#include "mathusalem/rb-task-mathusalem.h"
+#endif
+#include "core/rb-task-core.h"
+
+/* Signals */
+enum {
+	PAUSE_RESUME,
+	ABORT,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+rb_task_interface_init (RBTaskIface *iface)
+{
+	signals[PAUSE_RESUME] =
+		g_signal_new ("pause-resume",
+			      G_TYPE_FROM_INTERFACE (iface),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBTaskIface, pause_resume),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	signals[ABORT] =
+		g_signal_new ("abort",
+			      G_TYPE_FROM_INTERFACE (iface),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBTaskIface, abort),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+
+	g_object_interface_install_property (iface,
+					     g_param_spec_boolean ("pausable",
+								   "pausable",
+								   "Whether the task is pausable",
+								   FALSE,
+								   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_interface_install_property (iface,
+					     g_param_spec_boolean ("abortable",
+								   "abortable",
+								   "Whether the task is abortable",
+								   FALSE,
+								   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_interface_install_property (iface,
+					     g_param_spec_string ("title",
+								  "title",
+								  "title",
+								  NULL,
+								  G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_interface_install_property (iface,
+					     g_param_spec_string ("description",
+								  "description",
+								  "description",
+								  NULL,
+								  G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_interface_install_property (iface,
+					     g_param_spec_string ("stock-icon",
+								  "stock-icon",
+								  "stock-icon",
+								  NULL,
+								  G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_interface_install_property (iface,
+					     g_param_spec_double ("fraction",
+								  "fraction",
+								  "fraction",
+								  -1.0, 1.0, 0.0,
+								  G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+GType
+rb_task_get_type (void)
+{
+	static GType our_type = 0;
+
+	if (!our_type) {
+		static const GTypeInfo our_info = {
+			sizeof (RBTaskIface),
+			NULL,	/* base_init */
+			NULL,	/* base_finalize */
+			(GClassInitFunc)rb_task_interface_init,
+			NULL,	/* class_finalize */
+			NULL,	/* class_data */
+			0,
+			0,
+			NULL
+		};
+
+		our_type = g_type_register_static (G_TYPE_INTERFACE, "RBTask", &our_info, 0);
+		g_type_interface_add_prerequisite (our_type, G_TYPE_OBJECT);
+	}
+
+	return our_type;
+}
+
+void
+rb_task_completed (RBTask *encoder)
+{
+	RBTaskIface *iface = RB_TASK_GET_IFACE (encoder);
+
+	iface->completed (encoder);
+}
+
+RBTask*
+rb_task_new (gpointer shell)
+{
+	RBTask *task = NULL;
+
+#ifdef HAVE_MATHUSALEM
+	if (task == NULL)
+		task = RB_TASK (g_object_new (RB_TYPE_TASK_MATHUSALEM, NULL));
+#endif
+	if (task == NULL)
+		task = RB_TASK (g_object_new (RB_TYPE_TASK_CORE, "shell", shell, NULL));
+
+	return task;
+}
+
+void
+_rb_task_emit_pause_resume (RBTask *task)
+{
+	g_signal_emit (task, signals[PAUSE_RESUME], 0);
+}
+
+void
+_rb_task_emit_abort (RBTask *task)
+{
+	g_signal_emit (task, signals[ABORT], 0);
+}
+
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/core/Makefile.am	2006-08-14 16:18:44.000000000 +1000
@@ -0,0 +1,27 @@
+## arch-tag: Automake rules for core backend library
+
+noinst_LTLIBRARIES = librbbackendscore.la
+
+librbbackendscore_la_SOURCES =				\
+	rb-task-core.h					\
+	rb-task-core.c					\
+	$(NULL)
+
+librbbackendscore_la_LIBADD =			\
+	$(RHYTHMBOX_LIBS)
+
+librbbackendscore_la_LDFLAGS = -export-dynamic
+
+INCLUDES = 						\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"			\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib 				\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/sources				\
+	-I$(top_srcdir)/widgets				\
+	-I$(top_builddir)/lib 				\
+	$(RHYTHMBOX_CFLAGS)
+
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/core/rb-task-core.h	2006-08-15 13:23:38.000000000 +1000
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * arch-tag: Interface to audio task backend
+ *
+ * Copyright (C) 2006 James Livincoreon <jrl@ids.org.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#ifndef __RB_TASK_CORE_H__
+#define __RB_TASK_CORE_H__
+
+#include <glib-object.h>
+
+#include "rb-task.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_TASK_CORE            (rb_task_core_get_type ())
+#define RB_TASK_CORE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_TASK, RBTaskCore))
+#define RB_IS_TASK_CORE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_TASK))
+
+typedef struct _RBTaskCorePrivate RBTaskCorePrivate;
+
+typedef struct
+{
+	GObjectClass parent;
+} RBTaskCoreClass;
+
+typedef struct
+{
+	GObject parent;
+	RBTaskCorePrivate *priv;
+} RBTaskCore;
+
+
+GType rb_task_core_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RB_TASK_CORE_H__ */
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/core/rb-task-core.c	2006-08-15 17:33:17.000000000 +1000
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * Copyright (C) 2006 James Livincoreon <jrl@ids.org.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#include <config.h>
+
+#include "rb-task.h"
+#include "rb-task-core.h"
+#include "rb-shell.h"
+#include "rb-debug.h"
+
+
+static void rb_task_core_class_init (RBTaskCoreClass *klass);
+static void rb_task_core_init       (RBTaskCore *task);
+static void rb_task_core_finalize   (GObject *object);
+static void rb_task_init (RBTaskIface *iface);
+static GObject* rb_task_core_constructor (GType type,
+					  guint n_construct_properties,
+					  GObjectConstructParam *construct_properties);
+static void rb_task_core_set_property (GObject *object,
+				       guint prop_id,
+				       const GValue *value,
+				       GParamSpec *pspec);
+static void rb_task_core_get_property (GObject *object,
+				       guint prop_id,
+				       GValue *value,
+				       GParamSpec *pspec);
+
+
+struct _RBTaskCorePrivate {
+	gboolean pausable;
+	gboolean abortable;
+
+	char *title;
+	char *description;
+	char *stock_icon;
+	double fraction;
+
+	RBShell *shell;
+
+	GtkWidget *widget;
+	GtkWidget *title_widget;
+	GtkWidget *desc_widget;
+	GtkWidget *fraction_widget;
+	GtkWidget *pause_button;
+	GtkWidget *abort_button;
+};
+
+G_DEFINE_TYPE_WITH_CODE(RBTaskCore, rb_task_core, G_TYPE_OBJECT,
+			G_IMPLEMENT_INTERFACE(RB_TYPE_TASK,
+					      rb_task_init))
+#define RB_TASK_CORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_TASK_CORE, RBTaskCorePrivate))
+
+enum {
+	PROP_0,
+
+	/* RBTask properties */
+	PROP_PAUSABLE,
+	PROP_ABORTABLE,
+	PROP_TITLE,
+	PROP_DESCRIPTION,
+	PROP_STOCK_ICON,
+	PROP_FRACTION,
+
+	/* our properties */
+	PROP_SHELL,
+};
+
+static void rb_task_core_completed (RBTask *task);
+
+static void
+rb_task_core_class_init (RBTaskCoreClass *klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+
+	object_class->constructor = rb_task_core_constructor;
+	object_class->finalize = rb_task_core_finalize;
+	object_class->set_property = rb_task_core_set_property;
+	object_class->get_property = rb_task_core_get_property;
+
+	g_object_class_override_property (object_class, PROP_PAUSABLE, "pausable");
+	g_object_class_override_property (object_class, PROP_ABORTABLE, "abortable");
+	g_object_class_override_property (object_class, PROP_TITLE, "title");
+	g_object_class_override_property (object_class, PROP_STOCK_ICON, "stock-icon");
+	g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+	g_object_class_override_property (object_class, PROP_FRACTION, "fraction");
+
+	g_object_class_install_property (object_class,
+					 PROP_SHELL,
+					 g_param_spec_object ("shell",
+					 		      "shell",
+					 		      "shell",
+					 		      RB_TYPE_SHELL,
+					 		      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (klass, sizeof (RBTaskCorePrivate));
+}
+
+static void
+rb_task_core_init (RBTaskCore *task)
+{
+	task->priv = RB_TASK_CORE_GET_PRIVATE (task);
+}
+
+static GObject*
+rb_task_core_constructor (GType type,
+			  guint n_construct_properties,
+			  GObjectConstructParam *construct_properties)
+{
+	RBTaskCore *task;
+	GtkWidget *hbox;
+
+	task = RB_TASK_CORE (((GObjectClass*)rb_task_core_parent_class)
+		->constructor (type, n_construct_properties, construct_properties));
+
+	task->priv->widget = gtk_vbox_new (FALSE, 3);
+
+	task->priv->title_widget = gtk_label_new (NULL);
+	gtk_label_set_ellipsize (GTK_LABEL (task->priv->title_widget), PANGO_ELLIPSIZE_END);
+	gtk_container_add (GTK_CONTAINER (task->priv->widget), task->priv->title_widget);
+
+	task->priv->desc_widget = gtk_label_new (NULL);
+	gtk_label_set_ellipsize (GTK_LABEL (task->priv->desc_widget), PANGO_ELLIPSIZE_END);
+	gtk_container_add (GTK_CONTAINER (task->priv->widget), task->priv->desc_widget);
+
+	task->priv->fraction_widget = gtk_progress_bar_new ();
+	gtk_container_add (GTK_CONTAINER (task->priv->widget), task->priv->fraction_widget);
+
+	hbox = gtk_hbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (task->priv->widget), hbox);
+
+	task->priv->pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
+	gtk_container_add (GTK_CONTAINER (hbox), task->priv->pause_button);
+	gtk_widget_set_no_show_all (task->priv->pause_button, TRUE);
+
+	task->priv->abort_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+	gtk_container_add (GTK_CONTAINER (hbox), task->priv->abort_button);
+	gtk_widget_set_no_show_all (task->priv->abort_button, TRUE);
+
+	gtk_container_set_border_width (GTK_CONTAINER (task->priv->widget), 3);
+	rb_shell_add_widget (task->priv->shell, task->priv->widget, RB_SHELL_UI_LOCATION_SIDEBAR);
+	gtk_widget_show_all (task->priv->widget);
+
+	return G_OBJECT (task);
+}
+
+static void
+rb_task_init (RBTaskIface *iface)
+{
+	iface->completed = rb_task_core_completed;
+}
+
+static void
+rb_task_core_finalize (GObject *object)
+{
+	RBTaskCore *task = RB_TASK_CORE (object);
+
+	if (task->priv->shell)
+		g_object_unref (task->priv->shell);
+
+	g_free (task->priv->title);
+	g_free (task->priv->description);
+	g_free (task->priv->stock_icon);
+
+	G_OBJECT_CLASS (rb_task_core_parent_class)->finalize (object);
+}
+
+
+
+static void
+rb_task_core_set_property (GObject *object,
+			   guint prop_id,
+			   const GValue *value,
+			   GParamSpec *pspec)
+{
+	RBTaskCore *task = RB_TASK_CORE (object);
+
+	switch (prop_id)
+	{
+	case PROP_PAUSABLE:
+		task->priv->pausable = g_value_get_boolean (value);
+
+		g_object_set (task->priv->pause_button, "visible", task->priv->pausable, NULL);
+		break;
+	case PROP_ABORTABLE:
+		task->priv->abortable = g_value_get_boolean (value);
+
+		g_object_set (task->priv->abort_button, "visible", task->priv->abortable, NULL);
+		break;
+	case PROP_TITLE:
+		g_free (task->priv->title);
+		task->priv->title = g_value_dup_string (value);
+
+		if (task->priv->title_widget)
+			gtk_label_set_text (GTK_LABEL (task->priv->title_widget), task->priv->title);
+		else
+			gtk_label_set_text (GTK_LABEL (task->priv->title_widget), "");
+		break;
+	case PROP_DESCRIPTION:
+		g_free (task->priv->description);
+		task->priv->description = g_value_dup_string (value);
+
+		if (task->priv->desc_widget)
+			gtk_label_set_text (GTK_LABEL (task->priv->desc_widget), task->priv->description);
+		else
+			gtk_label_set_text (GTK_LABEL (task->priv->desc_widget), "");
+		break;
+	case PROP_STOCK_ICON:
+		g_free (task->priv->stock_icon);
+		task->priv->stock_icon = g_value_dup_string (value);
+
+		/* TODO */
+		break;
+	case PROP_FRACTION:
+		task->priv->fraction = g_value_get_double (value);
+
+		rb_debug ("task '%s': %f", task->priv->title, task->priv->fraction);
+		if (task->priv->fraction_widget) {
+			if (task->priv->fraction >= 0.0) {
+				char *s;
+
+				gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (task->priv->fraction_widget), task->priv->fraction);
+
+				s = g_strdup_printf ("%d%%", (int)(task->priv->fraction * 100));
+				gtk_progress_bar_set_text (GTK_PROGRESS_BAR (task->priv->fraction_widget), s);
+				g_free (s);
+			} else {
+				gtk_progress_bar_pulse (GTK_PROGRESS_BAR (task->priv->fraction_widget));
+				gtk_progress_bar_set_text (GTK_PROGRESS_BAR (task->priv->fraction_widget), NULL);
+			}
+		}
+		break;
+	case PROP_SHELL:
+		g_assert (task->priv->shell == NULL);
+		task->priv->shell = RB_SHELL (g_value_dup_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_task_core_get_property (GObject *object,
+			   guint prop_id,
+			   GValue *value,
+			   GParamSpec *pspec)
+{
+	RBTaskCore *task = RB_TASK_CORE (object);
+
+	switch (prop_id)
+	{
+	case PROP_PAUSABLE:
+		g_value_set_boolean (value, task->priv->pausable);
+		break;
+	case PROP_ABORTABLE:
+		g_value_set_boolean (value, task->priv->abortable);
+		break;
+	case PROP_TITLE:
+		g_value_set_string (value, task->priv->title);
+		break;
+	case PROP_DESCRIPTION:
+		g_value_set_string (value, task->priv->description);
+		break;
+	case PROP_STOCK_ICON:
+		g_value_set_string (value, task->priv->stock_icon);
+		break;
+	case PROP_FRACTION:
+		g_value_set_double (value, task->priv->fraction);
+		break;
+	case PROP_SHELL:
+		g_value_set_object (value, task->priv->shell);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_task_core_completed (RBTask *rbtask)
+{
+	RBTaskCore *task = RB_TASK_CORE (rbtask);
+
+	rb_debug ("task '%s' completed", task->priv->title);
+
+	if (task->priv->widget != NULL) {
+		rb_shell_remove_widget (task->priv->shell, task->priv->widget, RB_SHELL_UI_LOCATION_SIDEBAR);
+		task->priv->widget = NULL;
+	}
+}
+
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/mathusalem/Makefile.am	2006-08-15 17:18:11.000000000 +1000
@@ -0,0 +1,29 @@
+## arch-tag: Automake rules for mathusalem backend library
+
+noinst_LTLIBRARIES = librbbackendsmathusalem.la
+
+librbbackendsmathusalem_la_SOURCES =				\
+	rb-task-mathusalem.h					\
+	rb-task-mathusalem.c					\
+	$(NULL)
+
+librbbackendsmathusalem_la_LIBADD =			\
+	$(MATHUSALEM_LIBS)			\
+	$(RHYTHMBOX_LIBS)
+
+librbbackendsmathusalem_la_LDFLAGS = -export-dynamic
+
+INCLUDES = 						\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"			\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib 				\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/sources				\
+	-I$(top_srcdir)/widgets				\
+	-I$(top_builddir)/lib 				\
+	$(MATHUSALEM_CFLAGS)				\
+	$(RHYTHMBOX_CFLAGS)
+
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/mathusalem/rb-task-mathusalem.h	2006-08-15 17:21:32.000000000 +1000
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * arch-tag: Interface to audio task backend
+ *
+ * Copyright (C) 2006 James Livinmathusalemon <jrl@ids.org.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#ifndef __RB_TASK_MATHUSALEM_H__
+#define __RB_TASK_MATHUSALEM_H__
+
+#include <glib-object.h>
+
+#include "rb-task.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_TASK_MATHUSALEM            (rb_task_mathusalem_get_type ())
+#define RB_TASK_MATHUSALEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_TASK, RBTaskMathusalem))
+#define RB_IS_TASK_MATHUSALEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_TASK))
+
+typedef struct _RBTaskMathusalemPrivate RBTaskMathusalemPrivate;
+
+typedef struct
+{
+	GObjectClass parent;
+} RBTaskMathusalemClass;
+
+typedef struct
+{
+	GObject parent;
+	RBTaskMathusalemPrivate *priv;
+} RBTaskMathusalem;
+
+
+GType rb_task_mathusalem_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RB_TASK_MATHUSALEM_H__ */
--- /dev/null	2006-08-11 05:38:51.000000000 +1000
+++ backends/mathusalem/rb-task-mathusalem.c	2006-08-15 19:27:55.000000000 +1000
@@ -0,0 +1,257 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * Copyright (C) 2006 James Livinmathusalemon <jrl@ids.org.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <mathusalem/mathusalem.h>
+
+#include "rb-task.h"
+#include "rb-task-mathusalem.h"
+#include "rb-debug.h"
+
+
+static void rb_task_mathusalem_class_init (RBTaskMathusalemClass *klass);
+static void rb_task_mathusalem_init       (RBTaskMathusalem *task);
+static void rb_task_mathusalem_finalize   (GObject *object);
+static void rb_task_init (RBTaskIface *iface);
+static void rb_task_mathusalem_set_property (GObject *object,
+				       guint prop_id,
+				       const GValue *value,
+				       GParamSpec *pspec);
+static void rb_task_mathusalem_get_property (GObject *object,
+				       guint prop_id,
+				       GValue *value,
+				       GParamSpec *pspec);
+
+static void task_action_triggered_cb (MalemTask *mtask,
+				      const gchar *action_name,
+				      RBTaskMathusalem *task);
+
+struct _RBTaskMathusalemPrivate {
+	gboolean pausable;
+	gboolean abortable;
+
+	char *title;
+	char *description;
+	char *stock_icon;
+	double fraction;
+
+	MalemTask *task;
+};
+
+G_DEFINE_TYPE_WITH_CODE(RBTaskMathusalem, rb_task_mathusalem, G_TYPE_OBJECT,
+			G_IMPLEMENT_INTERFACE(RB_TYPE_TASK,
+					      rb_task_init))
+#define RB_TASK_MATHUSALEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_TASK_MATHUSALEM, RBTaskMathusalemPrivate))
+
+enum {
+	PROP_0,
+
+	/* RBTask properties */
+	PROP_PAUSABLE,
+	PROP_ABORTABLE,
+	PROP_TITLE,
+	PROP_DESCRIPTION,
+	PROP_STOCK_ICON,
+	PROP_FRACTION,
+};
+
+static void rb_task_mathusalem_completed (RBTask *task);
+
+static void
+rb_task_mathusalem_class_init (RBTaskMathusalemClass *klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+
+	object_class->finalize = rb_task_mathusalem_finalize;
+	object_class->set_property = rb_task_mathusalem_set_property;
+	object_class->get_property = rb_task_mathusalem_get_property;
+
+	g_object_class_override_property (object_class, PROP_PAUSABLE, "pausable");
+	g_object_class_override_property (object_class, PROP_ABORTABLE, "abortable");
+	g_object_class_override_property (object_class, PROP_TITLE, "title");
+	g_object_class_override_property (object_class, PROP_STOCK_ICON, "stock-icon");
+	g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+	g_object_class_override_property (object_class, PROP_FRACTION, "fraction");
+
+	g_type_class_add_private (klass, sizeof (RBTaskMathusalemPrivate));
+}
+
+static void
+rb_task_mathusalem_init (RBTaskMathusalem *task)
+{
+	static const gchar const *ifaces[] = {
+		/* "org.gnome.Mathusalem.FileTransfer", */
+		NULL
+	};
+
+	task->priv = RB_TASK_MATHUSALEM_GET_PRIVATE (task);
+
+	task->priv->task = malem_factory_register_task (MALEM_FACTORY (malem_factory_client_get_default ()),
+							ifaces,
+							NULL,
+							_("Rhythmbox"));
+
+	g_signal_connect_object (task->priv->task,
+				 "action-triggered", G_CALLBACK (task_action_triggered_cb),
+				 task, 0);
+	malem_task_start (task->priv->task);
+}
+
+static void
+rb_task_init (RBTaskIface *iface)
+{
+	iface->completed = rb_task_mathusalem_completed;
+}
+
+static void
+rb_task_mathusalem_finalize (GObject *object)
+{
+	RBTaskMathusalem *task = RB_TASK_MATHUSALEM (object);
+
+	g_free (task->priv->title);
+	g_free (task->priv->description);
+	g_free (task->priv->stock_icon);
+
+	g_object_unref (task->priv->task);
+
+	G_OBJECT_CLASS (rb_task_mathusalem_parent_class)->finalize (object);
+}
+
+
+
+static void
+rb_task_mathusalem_set_property (GObject *object,
+			   guint prop_id,
+			   const GValue *value,
+			   GParamSpec *pspec)
+{
+	RBTaskMathusalem *task = RB_TASK_MATHUSALEM (object);
+
+	switch (prop_id)
+	{
+	case PROP_PAUSABLE:
+		task->priv->pausable = g_value_get_boolean (value);
+
+		if (task->priv->pausable) {
+			malem_task_add_action (task->priv->task, "pause", NULL, NULL);
+		} else {
+			/*alem_task_remove_action (task->priv->task, "pause", NULL, NULL);*/
+		}
+		break;
+	case PROP_ABORTABLE:
+		task->priv->abortable = g_value_get_boolean (value);
+
+		if (task->priv->abortable) {
+			malem_task_add_action (task->priv->task, "abort", NULL, NULL);
+		} else {
+			/*malem_task_remove_action (task->priv->task, "abort", NULL, NULL);*/
+		}
+		break;
+	case PROP_TITLE:
+		g_free (task->priv->title);
+		task->priv->title = g_value_dup_string (value);
+
+		/* FIXME doesn't supprt this yet:
+		malem_task_set_title (task->priv->task, task->priv->title);*/
+		break;
+	case PROP_DESCRIPTION:
+		g_free (task->priv->description);
+		task->priv->description = g_value_dup_string (value);
+
+		/* TODO */
+		break;
+	case PROP_STOCK_ICON:
+		g_free (task->priv->stock_icon);
+		task->priv->stock_icon = g_value_dup_string (value);
+
+		/* TODO */
+		break;
+	case PROP_FRACTION:
+		task->priv->fraction = g_value_get_double (value);
+
+		rb_debug ("task '%s': %f", task->priv->title, task->priv->fraction);
+		malem_task_set_progress (task->priv->task,
+					 task->priv->fraction < 0.0 ? -1 : (int)(task->priv->fraction * 100));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+rb_task_mathusalem_get_property (GObject *object,
+			   guint prop_id,
+			   GValue *value,
+			   GParamSpec *pspec)
+{
+	RBTaskMathusalem *task = RB_TASK_MATHUSALEM (object);
+
+	switch (prop_id)
+	{
+	case PROP_PAUSABLE:
+		g_value_set_boolean (value, task->priv->pausable);
+		break;
+	case PROP_ABORTABLE:
+		g_value_set_boolean (value, task->priv->abortable);
+		break;
+	case PROP_TITLE:
+		g_value_set_string (value, task->priv->title);
+		break;
+	case PROP_DESCRIPTION:
+		g_value_set_string (value, task->priv->description);
+		break;
+	case PROP_STOCK_ICON:
+		g_value_set_string (value, task->priv->stock_icon);
+		break;
+	case PROP_FRACTION:
+		g_value_set_double (value, task->priv->fraction);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+task_action_triggered_cb (MalemTask *mtask,
+			  const gchar *action_name,
+			  RBTaskMathusalem *task)
+{
+	if (strcmp (action_name, "abort") == 0) {
+		_rb_task_emit_abort (RB_TASK (task));
+	} else if (strcmp (action_name, "pause") == 0) {
+		_rb_task_emit_pause_resume (RB_TASK (task));
+	}
+}
+
+static void
+rb_task_mathusalem_completed (RBTask *rbtask)
+{
+	RBTaskMathusalem *task = RB_TASK_MATHUSALEM (rbtask);
+
+	rb_debug ("task '%s' completed", task->priv->title);
+	malem_task_status_changed (task->priv->task, MALEM_TASK_STATUS_COMPLETED);
+}
+
