[rhythmbox] ipod: update track order in the ipod db playlist before saving (bug #604170)



commit fcb6157e0988188cc99d0a238cfe1bf6f21f3494
Author: Ben Walsh <b wumpster com>
Date:   Thu Oct 28 13:25:37 2010 +0100

    ipod: update track order in the ipod db playlist before saving (bug #604170)
    
    When tracks in an ipod playlist have been reordered, update the ipod
    db playlist to match the new order before saving.
    
    To do this, add a new signal to RbIpodDb to allow other parts of the
    plugin to update bits of the database immediately before it becomes
    read-only.

 plugins/ipod/rb-ipod-db.c                     |   29 ++++++
 plugins/ipod/rb-ipod-source.c                 |  121 ++++++++++++++++++++++---
 plugins/ipod/rb-ipod-static-playlist-source.c |   33 +++++++-
 plugins/ipod/rb-ipod-static-playlist-source.h |    2 +
 4 files changed, 170 insertions(+), 15 deletions(-)
---
diff --git a/plugins/ipod/rb-ipod-db.c b/plugins/ipod/rb-ipod-db.c
index 9121d44..0c59d58 100644
--- a/plugins/ipod/rb-ipod-db.c
+++ b/plugins/ipod/rb-ipod-db.c
@@ -118,6 +118,13 @@ G_DEFINE_TYPE (RbIpodDb, rb_ipod_db, G_TYPE_OBJECT)
 
 #define IPOD_DB_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IPOD_DB, RbIpodDbPrivate))
 
+enum {
+	BEFORE_SAVE,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 static void
 rb_itdb_save (RbIpodDb *ipod_db, GError **error)
 {
@@ -209,6 +216,22 @@ rb_ipod_db_class_init (RbIpodDbClass *klass)
 
 	object_class->dispose = rb_ipod_db_dispose;
 
+	/**
+	 * RbIpodDb::before-save
+	 * @db: the #RbIpodDb
+	 *
+	 * Emitted before iPod database write to disk.
+	 */
+	signals[BEFORE_SAVE] =
+		g_signal_new ("before-save",
+			      RB_TYPE_IPOD_DB,
+			      G_SIGNAL_RUN_FIRST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+
 	g_type_class_add_private (klass, sizeof (RbIpodDbPrivate));
 }
 
@@ -846,6 +869,12 @@ save_timeout_cb (RbIpodDb *ipod_db)
 		g_warning ("Database is read-only, not saving");
 		return TRUE;
 	}
+
+	/* Tell everyone about the save */
+	g_signal_emit (G_OBJECT (ipod_db),
+		       signals[BEFORE_SAVE],
+		       0);
+
 	rb_debug ("Starting iPod database save");
 	rb_debug ("Switching iPod database to read-only");
 	priv->read_only = TRUE;
diff --git a/plugins/ipod/rb-ipod-source.c b/plugins/ipod/rb-ipod-source.c
index 847551e..6e70494 100644
--- a/plugins/ipod/rb-ipod-source.c
+++ b/plugins/ipod/rb-ipod-source.c
@@ -471,7 +471,99 @@ playlist_track_added (GtkTreeModel *model, GtkTreePath *path,
 	rb_ipod_db_add_to_playlist (priv->ipod_db, ipod_pl, track);
 }
 
-static void playlist_source_model_connect_signals (RBIpodStaticPlaylistSource *playlist_source)
+static void
+playlist_before_save (RbIpodDb *ipod_db, gpointer data)
+{
+	RBIpodStaticPlaylistSource *playlist = RB_IPOD_STATIC_PLAYLIST_SOURCE (data);
+	GtkTreeModel *model = GTK_TREE_MODEL (rb_playlist_source_get_query_model (RB_PLAYLIST_SOURCE (playlist)));
+	Itdb_Playlist *ipod_pl = rb_ipod_static_playlist_source_get_itdb_playlist (playlist);
+	RBiPodSource *ipod = rb_ipod_static_playlist_source_get_ipod_source (playlist);
+	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (ipod);
+	GtkTreeIter iter;
+
+	if (!rb_ipod_static_playlist_source_get_was_reordered (playlist)) {
+		return;
+	}
+
+	rb_ipod_static_playlist_source_set_was_reordered (playlist, FALSE);
+
+	/* Sanity check that all tracks are in entry_map */
+
+	if (gtk_tree_model_get_iter_first (model, &iter)) {
+		do {
+			RhythmDBEntry *entry;
+			Itdb_Track *track;
+
+			gtk_tree_model_get (model, &iter, 0, &entry, -1);
+			track = g_hash_table_lookup (priv->entry_map, entry);
+
+			g_return_if_fail (track != NULL);
+		} while (gtk_tree_model_iter_next (model, &iter));
+	}
+
+	/* Remove all tracks then re-add in correct order */
+
+	while (ipod_pl->members != NULL) {
+		Itdb_Track *track;
+
+		track = (Itdb_Track *)ipod_pl->members->data;
+
+		rb_debug ("removing \"%s\" from \"%s\"", track->title, ipod_pl->name);
+
+		/* Call directly to itdb to avoid scheduling another save */
+		itdb_playlist_remove_track (ipod_pl, track);
+	}
+
+	if (gtk_tree_model_get_iter_first (model, &iter)) {
+		do {
+			RhythmDBEntry *entry;
+			Itdb_Track *track;
+
+			gtk_tree_model_get (model, &iter, 0, &entry, -1);
+			track = g_hash_table_lookup (priv->entry_map, entry);
+
+			rb_debug ("adding \"%s\" to \"%s\"", track->title, ipod_pl->name);
+
+			/* Call directly to itdb to avoid scheduling another save */
+			itdb_playlist_add_track (ipod_pl, track, -1);
+		} while (gtk_tree_model_iter_next (model, &iter));
+	}
+}
+
+static void
+playlist_rows_reordered (GtkTreeModel *model,
+			 GtkTreePath *path,
+			 GtkTreeIter *iter,
+			 gint *order, gpointer data)
+{
+	RBIpodStaticPlaylistSource *playlist = RB_IPOD_STATIC_PLAYLIST_SOURCE (data);
+	RBiPodSource *ipod = rb_ipod_static_playlist_source_get_ipod_source (playlist);
+	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (ipod);
+
+	/* Mark as reordered; will check before save */
+	rb_ipod_static_playlist_source_set_was_reordered (playlist, TRUE);
+
+	rb_ipod_db_save_async (priv->ipod_db);
+}
+
+static void
+playlist_source_model_disconnect_signals (GObject *model, RBIpodStaticPlaylistSource *playlist_source)
+{
+	g_return_if_fail (RB_IS_IPOD_STATIC_PLAYLIST_SOURCE (playlist_source));
+
+	g_signal_handlers_disconnect_by_func (model,
+					      G_CALLBACK (playlist_track_added),
+					      playlist_source);
+	g_signal_handlers_disconnect_by_func (model,
+					      G_CALLBACK (playlist_track_removed),
+					      playlist_source);
+	g_signal_handlers_disconnect_by_func (model,
+					      G_CALLBACK (playlist_rows_reordered),
+					      playlist_source);
+}
+
+static void
+playlist_source_model_connect_signals (RBIpodStaticPlaylistSource *playlist_source)
 {
 	RhythmDBQueryModel *model;
 
@@ -485,24 +577,26 @@ static void playlist_source_model_connect_signals (RBIpodStaticPlaylistSource *p
 	g_signal_connect (model, "entry-removed",
 			  G_CALLBACK (playlist_track_removed),
 			  playlist_source);
+	g_signal_connect (model, "rows-reordered",
+			  G_CALLBACK (playlist_rows_reordered),
+			  playlist_source);
 	g_object_unref (model);
 }
 
-static void playlist_source_model_changed (GObject *obj, GParamSpec *pspec, gpointer old_model)
+static void
+playlist_source_model_changed (GObject *obj, GParamSpec *pspec, gpointer old_model)
 {
 	RBIpodStaticPlaylistSource *playlist_source;
 
 	rb_debug ("base model changed for iPod playlist");
 
 	playlist_source = RB_IPOD_STATIC_PLAYLIST_SOURCE (obj);
-	g_signal_handlers_disconnect_by_func (G_OBJECT (old_model),
-					      G_CALLBACK (playlist_track_added),
-					      playlist_source);
-	g_signal_handlers_disconnect_by_func (G_OBJECT (old_model),
-					      G_CALLBACK (playlist_track_removed),
-					      playlist_source);
+
+	playlist_source_model_disconnect_signals (G_OBJECT (old_model), playlist_source);
+
 	playlist_source_model_connect_signals (playlist_source);
 }
+
 static void
 set_podcast_icon (RBIpodStaticPlaylistSource *source)
 {
@@ -570,6 +664,9 @@ add_rb_playlist (RBiPodSource *source, Itdb_Playlist *playlist)
 	g_signal_connect (playlist_source, "notify::base-query-model",
 			  G_CALLBACK (playlist_source_model_changed),
 			  playlist_source);
+	g_signal_connect (priv->ipod_db, "before-save",
+			  G_CALLBACK (playlist_before_save),
+			  playlist_source);
 	g_object_unref (model);
 	playlist_source_model_connect_signals (playlist_source);
 
@@ -1831,12 +1928,8 @@ impl_delete_thyself (RBSource *source)
 				      "base-query-model", &model, NULL);
 
 			/* remove these to ensure they aren't called during source deletion */
-			g_signal_handlers_disconnect_by_func (model,
-							      G_CALLBACK (playlist_track_added),
-							      rb_playlist);
-			g_signal_handlers_disconnect_by_func (model,
-							      G_CALLBACK (playlist_track_removed),
-							      rb_playlist);
+			playlist_source_model_disconnect_signals (G_OBJECT (model),
+								  RB_IPOD_STATIC_PLAYLIST_SOURCE (rb_playlist));
 
 			g_object_unref (model);
 			rb_source_delete_thyself (rb_playlist);
diff --git a/plugins/ipod/rb-ipod-static-playlist-source.c b/plugins/ipod/rb-ipod-static-playlist-source.c
index d6b8171..b3bfed7 100644
--- a/plugins/ipod/rb-ipod-static-playlist-source.c
+++ b/plugins/ipod/rb-ipod-static-playlist-source.c
@@ -57,6 +57,7 @@ typedef struct
 	RbIpodDb	*ipod_db;
 	Itdb_Playlist	*itdb_playlist;
 	RBiPodSource	*ipod_source;
+	gboolean	was_reordered;
 } RBIpodStaticPlaylistSourcePrivate;
 
 RB_PLUGIN_DEFINE_TYPE(RBIpodStaticPlaylistSource,
@@ -69,7 +70,8 @@ enum {
 	PROP_0,
 	PROP_IPOD_SOURCE,
 	PROP_IPOD_DB,
-	PROP_ITDB_PLAYLIST
+	PROP_ITDB_PLAYLIST,
+	PROP_WAS_REORDERED
 };
 
 
@@ -114,6 +116,13 @@ rb_ipod_static_playlist_source_class_init (RBIpodStaticPlaylistSourceClass *klas
 							      "itdb-playlist",
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+	g_object_class_install_property (object_class,
+					 PROP_WAS_REORDERED,
+					 g_param_spec_pointer ("was-reordered",
+							       "was-reordered",
+							       "was-reordered",
+							       G_PARAM_READWRITE));
+
 	g_type_class_add_private (klass, sizeof (RBIpodStaticPlaylistSourcePrivate));
 }
 
@@ -196,6 +205,9 @@ rb_ipod_static_playlist_source_set_property (GObject *object,
 	case PROP_ITDB_PLAYLIST:
 		priv->itdb_playlist = g_value_get_pointer (value);
 		break;
+	case PROP_WAS_REORDERED:
+		priv->was_reordered = g_value_get_boolean (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -220,6 +232,9 @@ rb_ipod_static_playlist_source_get_property (GObject *object,
 	case PROP_ITDB_PLAYLIST:
 		g_value_set_pointer (value, priv->itdb_playlist);
 		break;
+	case PROP_WAS_REORDERED:
+		g_value_set_boolean (value, priv->was_reordered);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -243,6 +258,22 @@ rb_ipod_static_playlist_source_get_ipod_source (RBIpodStaticPlaylistSource *play
 	return priv->ipod_source;
 }
 
+gboolean
+rb_ipod_static_playlist_source_get_was_reordered (RBIpodStaticPlaylistSource *playlist)
+{
+	RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (playlist);
+
+	return priv->was_reordered;
+}
+
+void
+rb_ipod_static_playlist_source_set_was_reordered (RBIpodStaticPlaylistSource *playlist, gboolean was_reordered)
+{
+	RBIpodStaticPlaylistSourcePrivate *priv = IPOD_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (playlist);
+
+	priv->was_reordered = was_reordered;
+}
+
 static gboolean
 impl_show_popup (RBSource *source)
 {
diff --git a/plugins/ipod/rb-ipod-static-playlist-source.h b/plugins/ipod/rb-ipod-static-playlist-source.h
index 16f2ff3..b793806 100644
--- a/plugins/ipod/rb-ipod-static-playlist-source.h
+++ b/plugins/ipod/rb-ipod-static-playlist-source.h
@@ -56,6 +56,8 @@ RBIpodStaticPlaylistSource *	rb_ipod_static_playlist_source_new (RBShell *shell,
 
 Itdb_Playlist* rb_ipod_static_playlist_source_get_itdb_playlist (RBIpodStaticPlaylistSource *playlist);
 RBiPodSource* rb_ipod_static_playlist_source_get_ipod_source (RBIpodStaticPlaylistSource *playlist);
+gboolean rb_ipod_static_playlist_source_get_was_reordered (RBIpodStaticPlaylistSource *playlist);
+void rb_ipod_static_playlist_source_set_was_reordered (RBIpodStaticPlaylistSource *playlist, gboolean was_reordered);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]