From 85d5e398dab51f9d450a0ce259f78b10b22c2003 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Mon, 8 Mar 2010 17:55:40 +0000 Subject: [PATCH] Shuffle and repeat buttons now work. Fixes issue #20 --- src/mainwindow.cpp | 6 +- src/playlist.cpp | 124 +++++++++++++++++++++++++++++++++++--- src/playlist.h | 18 +++++- src/shufflerepeatwidget.h | 20 +++--- 4 files changed, 150 insertions(+), 18 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a89f2537b..bdc3529be 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -689,7 +689,8 @@ void MainWindow::SetCurrentPlaylist(PlaylistView* pCurrent){ current_playlist_view_ = pCurrent ; current_playlist_ = qobject_cast< Playlist* >( pCurrent->model() ); player_->SetCurrentPlaylist(current_playlist_); - + + current_playlist_->set_shuffle_repeat_widget(shuffle_repeat_widget_); // connects !! :) @@ -709,6 +710,9 @@ void MainWindow::SetCurrentPlaylist(PlaylistView* pCurrent){ connect(player_, SIGNAL(Stopped()), current_playlist_view_, SLOT(StopGlowing())); connect(radio_model_, SIGNAL(StreamMetadataFound(QUrl,Song)), current_playlist_, SLOT(SetStreamMetadata(QUrl,Song))); + + connect(shuffle_repeat_widget_, SIGNAL(ShuffleModeChanged(ShuffleRepeatWidget::ShuffleMode)), + current_playlist_, SLOT(ShuffleModeChanged(ShuffleRepeatWidget::ShuffleMode))); } void MainWindow::CurrentTabChanged(int index ){ diff --git a/src/playlist.cpp b/src/playlist.cpp index 7b309c2b0..4972ed0fe 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -14,6 +14,7 @@ #include #include +#include #include @@ -23,11 +24,14 @@ const char* Playlist::kSettingsGroup = "Playlist"; Playlist::Playlist(QObject *parent) : QAbstractListModel(parent), current_is_paused_(false), + current_virtual_index_(-1), + is_shuffled_(false), scrobble_point_(-1), has_scrobbled_(false), ignore_sorting_(false), title_(""), - index_(-1) + index_(-1), + shuffle_repeat_widget_(NULL) { } @@ -111,14 +115,68 @@ int Playlist::current_index() const { return current_item_.isValid() ? current_item_.row() : -1; } +void Playlist::ShuffleModeChanged(ShuffleRepeatWidget::ShuffleMode mode) { + is_shuffled_ = (mode != ShuffleRepeatWidget::Shuffle_Off); + ReshuffleIndices(); +} + +int Playlist::NextVirtualIndex(int i) const { + ShuffleRepeatWidget::RepeatMode repeat_mode = shuffle_repeat_widget_->repeat_mode(); + ShuffleRepeatWidget::ShuffleMode shuffle_mode = shuffle_repeat_widget_->shuffle_mode(); + bool album_only = repeat_mode == ShuffleRepeatWidget::Repeat_Album || + shuffle_mode == ShuffleRepeatWidget::Shuffle_Album; + + // This one's easy - if we have to repeat the current track then just return i + if (repeat_mode == ShuffleRepeatWidget::Repeat_Track) + return i; + + // If we're not bothered about whether a song is on the same album then + // return the next virtual index, whatever it is. + if (!album_only) + return i+1; + + // We need to advance i until we get something else on the same album + Song last_song = current_item_metadata(); + for (int j=i+1 ; jMetadata(); + if ((last_song.is_compilation() && this_song.is_compilation() || + last_song.artist() == this_song.artist()) && + last_song.album() == this_song.album()) { + return j; // Found one + } + } + + // Couldn't find one - return past the end of the list + return virtual_items_.count(); +} + int Playlist::next_index() const { - int i = current_index() + 1; - if (i >= items_.count()) - return -1; + // Did we want to stop after this track? if (stop_after_.isValid() && current_index() == stop_after_.row()) return -1; - return i; + int next_virtual_index = NextVirtualIndex(current_virtual_index_); + if (next_virtual_index >= virtual_items_.count()) { + // We've gone off the end of the playlist. + + switch (shuffle_repeat_widget_->repeat_mode()) { + case ShuffleRepeatWidget::Repeat_Off: + return -1; + case ShuffleRepeatWidget::Repeat_Track: + next_virtual_index = current_virtual_index_; + break; + + default: + next_virtual_index = NextVirtualIndex(-1); + break; + } + } + + // Still off the end? Then just give up + if (next_virtual_index >= virtual_items_.count()) + return -1; + + return virtual_items_[next_virtual_index]; } int Playlist::previous_index() const { @@ -142,6 +200,23 @@ void Playlist::set_current_index(int i) { emit CurrentSongChanged(current_item_metadata()); } + // Update the virtual index + if (i == -1) + current_virtual_index_ = -1; + else if (is_shuffled_ && current_virtual_index_ == -1) { + // This is the first thing we're playing so we want to make sure the array + // is shuffled + ReshuffleIndices(); + + // Bring the one we've been asked to play to the start of the list + virtual_items_.takeAt(virtual_items_.indexOf(i)); + virtual_items_.prepend(i); + current_virtual_index_ = 0; + } else if (is_shuffled_) + current_virtual_index_ = virtual_items_.indexOf(i); + else + current_virtual_index_ = i; + UpdateScrobblePoint(); } @@ -275,14 +350,14 @@ QModelIndex Playlist::InsertItems(const QList& items, int after) const int end = start + items.count() - 1; beginInsertRows(QModelIndex(), start, end); - for (int i=start ; i<=end ; ++i) { items_.insert(i, items[i - start]); + virtual_items_ << virtual_items_.count(); } - endInsertRows(); // Save(); + ReshuffleIndices(); return index(start, 0); } @@ -428,6 +503,7 @@ void Playlist::SaveR() const { void Playlist::RestoreR() { qDeleteAll(items_); items_.clear(); + virtual_items_.clear(); QSettings s; s.beginGroup(kSettingsGroup); @@ -444,6 +520,7 @@ void Playlist::RestoreR() { item->Restore(s); items_ << item; + virtual_items_ << virtual_items_.count(); } s.endArray(); s.endGroup(); @@ -460,6 +537,20 @@ bool Playlist::removeRows(int row, int count, const QModelIndex& parent) { endRemoveRows(); + QList::iterator it = virtual_items_.begin(); + int i = 0; + while (it != virtual_items_.end()) { + if (*it >= items_.count()) { + if (i >= current_virtual_index_) + current_virtual_index_ --; + + it = virtual_items_.erase(it); + } else { + ++it; + } + ++i; + } + // Save(); return true; } @@ -582,3 +673,22 @@ void Playlist::Shuffle() { // Save(); } + +void Playlist::ReshuffleIndices() { + if (!is_shuffled_) { + std::sort(virtual_items_.begin(), virtual_items_.end()); + if (current_index() != -1) + current_virtual_index_ = virtual_items_.indexOf(current_index()); + } else { + QList::iterator begin = virtual_items_.begin(); + if (current_virtual_index_ != -1) + std::advance(begin, current_virtual_index_ + 1); + + std::random_shuffle(begin, virtual_items_.end()); + } +} + +void Playlist::set_shuffle_repeat_widget(ShuffleRepeatWidget* w) { + shuffle_repeat_widget_ = w; + ShuffleModeChanged(w->shuffle_mode()); +} diff --git a/src/playlist.h b/src/playlist.h index a2e40d98a..1abc27d09 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -7,6 +7,7 @@ #include "playlistitem.h" #include "song.h" #include "radioitem.h" +#include "shufflerepeatwidget.h" class RadioService; @@ -72,7 +73,10 @@ class Playlist : public QAbstractListModel { void SetTitle(const QString& title) { title_ = title; } void SetPlaylistIndex( int ipos ) { index_ = ipos ; } - int GetPlaylistIndex() const { return index_ ; } + int GetPlaylistIndex() const { return index_ ; } + + void set_shuffle_repeat_widget(ShuffleRepeatWidget* w); + ShuffleRepeatWidget* shuffle_repeat_widget() const { return shuffle_repeat_widget_; } // Scrobbling int scrobble_point() const { return scrobble_point_; } @@ -114,12 +118,16 @@ class Playlist : public QAbstractListModel { void Clear(); void Shuffle(); + void ShuffleModeChanged(ShuffleRepeatWidget::ShuffleMode mode); + signals: void CurrentSongChanged(const Song& metadata); private: void SetCurrentIsPaused(bool paused); void UpdateScrobblePoint(); + void ReshuffleIndices(); + int NextVirtualIndex(int i) const; // Persistence void SaveR() const; @@ -127,10 +135,14 @@ class Playlist : public QAbstractListModel { private: QList items_; + QList virtual_items_; // Contains the indices into items_ in the order + // that they will be played. + bool is_shuffled_; QPersistentModelIndex current_item_; QPersistentModelIndex stop_after_; bool current_is_paused_; + int current_virtual_index_; int scrobble_point_; bool has_scrobbled_; @@ -138,7 +150,9 @@ class Playlist : public QAbstractListModel { // Hack to stop QTreeView::setModel sorting the playlist bool ignore_sorting_; QString title_; - int index_ ; + int index_ ; + + ShuffleRepeatWidget* shuffle_repeat_widget_; }; #endif // PLAYLIST_H diff --git a/src/shufflerepeatwidget.h b/src/shufflerepeatwidget.h index fad7c5ad2..edfa5ea1f 100644 --- a/src/shufflerepeatwidget.h +++ b/src/shufflerepeatwidget.h @@ -3,10 +3,10 @@ #include -class QMenu; - #include "ui_shufflerepeatwidget.h" +class QMenu; + class ShuffleRepeatWidget : public QWidget { Q_OBJECT @@ -27,21 +27,25 @@ class ShuffleRepeatWidget : public QWidget { static const char* kSettingsGroup; - void Load(); - void Save(); + RepeatMode repeat_mode() const { return repeat_mode_; } + ShuffleMode shuffle_mode() const { return shuffle_mode_; } public slots: - void SetRepeatMode(RepeatMode mode); - void SetShuffleMode(ShuffleMode mode); + void SetRepeatMode(ShuffleRepeatWidget::RepeatMode mode); + void SetShuffleMode(ShuffleRepeatWidget::ShuffleMode mode); signals: - void RepeatModeChanged(RepeatMode mode); - void ShuffleModeChanged(ShuffleMode mode); + void RepeatModeChanged(ShuffleRepeatWidget::RepeatMode mode); + void ShuffleModeChanged(ShuffleRepeatWidget::ShuffleMode mode); private slots: void RepeatActionTriggered(QAction*); void ShuffleActionTriggered(QAction*); + private: + void Load(); + void Save(); + private: Ui::ShuffleRepeatWidget ui_;