From 11a5ee821b99da7c58da4cb2251c8686e2d615cf Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 11 Jul 2014 20:01:53 +0200
Subject: PlaylistEdit: postpone UpdateQueuedSong() when adding multiple songs

Implement a "bulk" edit mode that postpones both UpdateQueuedSong()
and OnModified().  This way, the playlist version gets incremented
only once.  More importantly: when adding multiple songs to a queue
that consists of only one song, the first song that got added will
always be played next.  By postponing this choice, all newly added
songs get a chance to become the next song.  Fixes the second (and
last) part of Mantis ticket 0004005.
---
 src/BulkEdit.hxx                 | 41 ++++++++++++++++++++++++++++++++++++++++
 src/Playlist.cxx                 |  6 ++++++
 src/Playlist.hxx                 | 19 ++++++++++++++++++-
 src/PlaylistEdit.cxx             | 35 ++++++++++++++++++++++++++++++++++
 src/command/DatabaseCommands.cxx |  3 +++
 src/command/PlaylistCommands.cxx |  3 +++
 src/command/QueueCommands.cxx    |  3 +++
 7 files changed, 109 insertions(+), 1 deletion(-)
 create mode 100644 src/BulkEdit.hxx

(limited to 'src')

diff --git a/src/BulkEdit.hxx b/src/BulkEdit.hxx
new file mode 100644
index 000000000..422dc4f38
--- /dev/null
+++ b/src/BulkEdit.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_BULK_EDIT_HXX
+#define MPD_BULK_EDIT_HXX
+
+#include "Partition.hxx"
+
+/**
+ * Begin a "bulk edit" and commit it automatically.
+ */
+class ScopeBulkEdit {
+	Partition &partition;
+
+public:
+	ScopeBulkEdit(Partition &_partition):partition(_partition) {
+		partition.playlist.BeginBulk();
+	}
+
+	~ScopeBulkEdit() {
+		partition.playlist.CommitBulk(partition.pc);
+	}
+};
+
+#endif
diff --git a/src/Playlist.cxx b/src/Playlist.cxx
index ac2cc494b..526f35298 100644
--- a/src/Playlist.cxx
+++ b/src/Playlist.cxx
@@ -103,6 +103,12 @@ playlist::UpdateQueuedSong(PlayerControl &pc, const Song *prev)
 	if (!playing)
 		return;
 
+	if (prev == nullptr && bulk_edit)
+		/* postponed until CommitBulk() to avoid always
+		   queueing the first song that is being added (in
+		   random mode) */
+		return;
+
 	assert(!queue.IsEmpty());
 	assert((queued < 0) == (prev == nullptr));
 
diff --git a/src/Playlist.hxx b/src/Playlist.hxx
index 7d7e9b154..b660ecb40 100644
--- a/src/Playlist.hxx
+++ b/src/Playlist.hxx
@@ -45,6 +45,18 @@ struct playlist {
 	 */
 	bool stop_on_error;
 
+	/**
+	 * If true, then a bulk edit has been initiated by
+	 * BeginBulk(), and UpdateQueuedSong() and OnModified() will
+	 * be postponed until CommitBulk()
+	 */
+	bool bulk_edit;
+
+	/**
+	 * Has the queue been modified during bulk edit mode?
+	 */
+	bool bulk_modified;
+
 	/**
 	 * Number of errors since playback was started.  If this
 	 * number exceeds the length of the playlist, MPD gives up,
@@ -69,7 +81,9 @@ struct playlist {
 	int queued;
 
 	playlist(unsigned max_length)
-		:queue(max_length), playing(false), current(-1), queued(-1) {
+		:queue(max_length), playing(false),
+		 bulk_edit(false),
+		 current(-1), queued(-1) {
 	}
 
 	~playlist() {
@@ -126,6 +140,9 @@ protected:
 	void UpdateQueuedSong(PlayerControl &pc, const Song *prev);
 
 public:
+	void BeginBulk();
+	void CommitBulk(PlayerControl &pc);
+
 	void Clear(PlayerControl &pc);
 
 	/**
diff --git a/src/PlaylistEdit.cxx b/src/PlaylistEdit.cxx
index 3eea2491e..0dffd3d80 100644
--- a/src/PlaylistEdit.cxx
+++ b/src/PlaylistEdit.cxx
@@ -40,6 +40,12 @@
 void
 playlist::OnModified()
 {
+	if (bulk_edit) {
+		/* postponed to CommitBulk() */
+		bulk_modified = true;
+		return;
+	}
+
 	queue.IncrementVersion();
 
 	idle_add(IDLE_PLAYLIST);
@@ -56,6 +62,35 @@ playlist::Clear(PlayerControl &pc)
 	OnModified();
 }
 
+void
+playlist::BeginBulk()
+{
+	assert(!bulk_edit);
+
+	bulk_edit = true;
+	bulk_modified = false;
+}
+
+void
+playlist::CommitBulk(PlayerControl &pc)
+{
+	assert(bulk_edit);
+
+	bulk_edit = false;
+	if (!bulk_modified)
+		return;
+
+	if (queued < 0)
+		/* if no song was queued, UpdateQueuedSong() is being
+		   ignored in "bulk" edit mode; now that we have
+		   shuffled all new songs, we can pick a random one
+		   (instead of always picking the first one that was
+		   added) */
+		UpdateQueuedSong(pc, nullptr);
+
+	OnModified();
+}
+
 PlaylistResult
 playlist::AppendFile(PlayerControl &pc,
 		     const char *path_utf8, unsigned *added_id)
diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx
index b86cbdae7..a7d2467b8 100644
--- a/src/command/DatabaseCommands.cxx
+++ b/src/command/DatabaseCommands.cxx
@@ -30,6 +30,7 @@
 #include "util/Error.hxx"
 #include "SongFilter.hxx"
 #include "protocol/Result.hxx"
+#include "BulkEdit.hxx"
 
 #include <assert.h>
 #include <string.h>
@@ -92,6 +93,8 @@ handle_match_add(Client &client, int argc, char *argv[], bool fold_case)
 		return CommandResult::ERROR;
 	}
 
+	const ScopeBulkEdit bulk_edit(client.partition);
+
 	const DatabaseSelection selection("", true, &filter);
 	Error error;
 	return AddFromDatabase(client.partition, selection, error)
diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx
index d178fa097..c4441293e 100644
--- a/src/command/PlaylistCommands.cxx
+++ b/src/command/PlaylistCommands.cxx
@@ -26,6 +26,7 @@
 #include "PlaylistFile.hxx"
 #include "PlaylistVector.hxx"
 #include "PlaylistQueue.hxx"
+#include "BulkEdit.hxx"
 #include "TimePrint.hxx"
 #include "Client.hxx"
 #include "protocol/ArgParser.hxx"
@@ -67,6 +68,8 @@ handle_load(Client &client, int argc, char *argv[])
 	} else if (!check_range(client, &start_index, &end_index, argv[2]))
 		return CommandResult::ERROR;
 
+	const ScopeBulkEdit bulk_edit(client.partition);
+
 	const PlaylistResult result =
 		playlist_open_into_queue(argv[1],
 					 start_index, end_index,
diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx
index a20b24132..a987e1bc9 100644
--- a/src/command/QueueCommands.cxx
+++ b/src/command/QueueCommands.cxx
@@ -28,6 +28,7 @@
 #include "ClientFile.hxx"
 #include "Client.hxx"
 #include "Partition.hxx"
+#include "BulkEdit.hxx"
 #include "protocol/ArgParser.hxx"
 #include "protocol/Result.hxx"
 #include "ls.hxx"
@@ -73,6 +74,8 @@ handle_add(Client &client, gcc_unused int argc, char *argv[])
 		return print_playlist_result(client, result);
 	}
 
+	const ScopeBulkEdit bulk_edit(client.partition);
+
 	const DatabaseSelection selection(uri, true);
 	Error error;
 	return AddFromDatabase(client.partition, selection, error)
-- 
cgit v1.2.3