Basic support for multiple playlists. Not everything works yet

This commit is contained in:
David Sansome 2010-05-20 21:21:55 +00:00
parent 5de851d054
commit e515724bf7
37 changed files with 834 additions and 288 deletions

View File

@ -18,6 +18,8 @@
#include "player.h"
#include "engines/enginebase.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
#include "playlist/playlistmanager.h"
#include "radio/lastfmservice.h"
#ifdef HAVE_GSTREAMER
@ -68,11 +70,11 @@ const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) {
}
#endif
Player::Player(Playlist* playlist, LastFMService* lastfm, Engine::Type engine, QObject* parent)
Player::Player(PlaylistManager* playlists, LastFMService* lastfm,
Engine::Type engine, QObject* parent)
: QObject(parent),
playlist_(playlist),
playlists_(playlists),
lastfm_(lastfm),
current_item_options_(PlaylistItem::Default),
stream_change_type_(Engine::First)
{
engine_ = createEngine(engine);
@ -151,18 +153,17 @@ void Player::HandleSpecialLoad(const PlaylistItem::SpecialLoadResult &result) {
case PlaylistItem::SpecialLoadResult::TrackAvailable: {
// Might've been an async load, so check we're still on the same item
int current_index = playlist_->current_index();
int current_index = playlists_->active()->current_index();
if (current_index == -1)
return;
shared_ptr<PlaylistItem> item = playlist_->item_at(current_index);
shared_ptr<PlaylistItem> item = playlists_->active()->item_at(current_index);
if (!item || item->Url() != result.original_url_)
return;
engine_->Play(result.media_url_, stream_change_type_);
current_item_ = item->Metadata();
current_item_options_ = item->options();
current_item_ = item;
loading_async_ = QUrl();
break;
}
@ -179,13 +180,13 @@ void Player::Next() {
}
void Player::NextInternal(Engine::TrackChangeType change) {
if (playlist_->current_item_options() & PlaylistItem::ContainsMultipleTracks) {
if (playlists_->active()->current_item()->options() & PlaylistItem::ContainsMultipleTracks) {
// The next track is already being loaded
if (playlist_->current_item()->Url() == loading_async_)
if (playlists_->active()->current_item()->Url() == loading_async_)
return;
stream_change_type_ = change;
HandleSpecialLoad(playlist_->current_item()->LoadNext());
HandleSpecialLoad(playlists_->active()->current_item()->LoadNext());
return;
}
@ -193,8 +194,8 @@ void Player::NextInternal(Engine::TrackChangeType change) {
}
void Player::NextItem(Engine::TrackChangeType change) {
int i = playlist_->next_index();
playlist_->set_current_index(i);
int i = playlists_->active()->next_index();
playlists_->active()->set_current_index(i);
if (i == -1) {
emit PlaylistFinished();
Stop();
@ -205,7 +206,7 @@ void Player::NextItem(Engine::TrackChangeType change) {
}
void Player::TrackEnded() {
if (playlist_->stop_after_current()) {
if (playlists_->active()->stop_after_current()) {
Stop();
return;
}
@ -222,7 +223,7 @@ void Player::PlayPause() {
case Engine::Playing:
// We really shouldn't pause last.fm streams
if (current_item_options_ & PlaylistItem::PauseDisabled)
if (current_item_->options() & PlaylistItem::PauseDisabled)
break;
qDebug() << "Pausing";
@ -231,11 +232,11 @@ void Player::PlayPause() {
case Engine::Empty:
case Engine::Idle: {
if (playlist_->rowCount() == 0)
if (playlists_->active()->rowCount() == 0)
break;
int i = playlist_->current_index();
if (i == -1) i = playlist_->last_played_index();
int i = playlists_->active()->current_index();
if (i == -1) i = playlists_->active()->last_played_index();
if (i == -1) i = 0;
PlayAt(i, Engine::First, true);
@ -246,12 +247,13 @@ void Player::PlayPause() {
void Player::Stop() {
engine_->Stop();
playlist_->set_current_index(-1);
playlists_->active()->set_current_index(-1);
current_item_.reset();
}
void Player::Previous() {
int i = playlist_->previous_index();
playlist_->set_current_index(i);
int i = playlists_->active()->previous_index();
playlists_->active()->set_current_index(i);
if (i == -1) {
Stop();
return;
@ -292,26 +294,24 @@ Engine::State Player::GetState() const {
void Player::PlayAt(int index, Engine::TrackChangeType change, bool reshuffle) {
if (reshuffle)
playlist_->set_current_index(-1);
playlist_->set_current_index(index);
playlists_->active()->set_current_index(-1);
playlists_->active()->set_current_index(index);
shared_ptr<PlaylistItem> item = playlist_->item_at(index);
current_item_options_ = item->options();
current_item_ = item->Metadata();
current_item_ = playlists_->active()->item_at(index);
if (item->options() & PlaylistItem::SpecialPlayBehaviour) {
if (current_item_->options() & PlaylistItem::SpecialPlayBehaviour) {
// It's already loading
if (item->Url() == loading_async_)
if (current_item_->Url() == loading_async_)
return;
HandleSpecialLoad(item->StartLoading());
HandleSpecialLoad(current_item_->StartLoading());
}
else {
loading_async_ = QUrl();
engine_->Play(item->Url(), change);
engine_->Play(current_item_->Url(), change);
if (lastfm_->IsScrobblingEnabled())
lastfm_->NowPlaying(item->Metadata());
lastfm_->NowPlaying(current_item_->Metadata());
}
emit CapsChange(GetCaps());
@ -319,7 +319,6 @@ void Player::PlayAt(int index, Engine::TrackChangeType change, bool reshuffle) {
void Player::CurrentMetadataChanged(const Song &metadata) {
lastfm_->NowPlaying(metadata);
current_item_ = metadata;
emit TrackChange(GetMetadata());
}
@ -328,11 +327,11 @@ void Player::Seek(int seconds) {
engine_->Seek(msec);
// If we seek the track we don't want to submit it to last.fm
playlist_->set_scrobbled(true);
playlists_->active()->set_scrobbled(true);
}
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
shared_ptr<PlaylistItem> item = playlist_->current_item();
shared_ptr<PlaylistItem> item = playlists_->active()->current_item();
if (item == NULL)
return;
@ -343,26 +342,26 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
if (song.title().isEmpty() && song.artist().isEmpty())
return;
playlist_->SetStreamMetadata(item->Url(), song);
playlists_->active()->SetStreamMetadata(item->Url(), song);
}
int Player::GetCaps() const {
int caps = CAN_HAS_TRACKLIST;
if (current_item_.is_valid()) { caps |= CAN_PROVIDE_METADATA; }
if (GetState() == Engine::Playing && current_item_options_ & PlaylistItem::PauseDisabled) {
if (current_item_) { caps |= CAN_PROVIDE_METADATA; }
if (GetState() == Engine::Playing && current_item_->options() & PlaylistItem::PauseDisabled) {
caps |= CAN_PAUSE;
}
if (GetState() == Engine::Paused) {
caps |= CAN_PLAY;
}
if (GetState() != Engine::Empty && current_item_.filetype() != Song::Type_Stream) {
if (GetState() != Engine::Empty && current_item_->Metadata().filetype() != Song::Type_Stream) {
caps |= CAN_SEEK;
}
if (playlist_->next_index() != -1 ||
playlist_->current_item_options() & PlaylistItem::ContainsMultipleTracks) {
if (playlists_->active()->next_index() != -1 ||
playlists_->active()->current_item_options() & PlaylistItem::ContainsMultipleTracks) {
caps |= CAN_GO_NEXT;
}
if (playlist_->previous_index() != -1) {
if (playlists_->active()->previous_index() != -1) {
caps |= CAN_GO_PREV;
}
return caps;
@ -382,11 +381,11 @@ DBusStatus Player::GetStatus() const {
status.play = DBusStatus::Mpris_Paused;
break;
}
status.random = playlist_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1;
PlaylistSequence::RepeatMode repeat_mode = playlist_->sequence()->repeat_mode();
status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1;
PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode();
status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0;
status.repeat_playlist = (repeat_mode == PlaylistSequence::Repeat_Album ||
repeat_mode == PlaylistSequence::Repeat_Playlist) ? 1 : 0;
repeat_mode == PlaylistSequence::Repeat_Playlist) ? 1 : 0;
return status;
}
@ -420,7 +419,7 @@ QVariantMap Player::GetMetadata(const PlaylistItem& item) const {
}
QVariantMap Player::GetMetadata() const {
shared_ptr<PlaylistItem> item = playlist_->current_item();
shared_ptr<PlaylistItem> item = playlists_->active()->current_item();
if (item) {
return GetMetadata(*item);
}
@ -428,10 +427,10 @@ QVariantMap Player::GetMetadata() const {
}
QVariantMap Player::GetMetadata(int track) const {
if (track >= playlist_->rowCount() || track < 0) {
if (track >= playlists_->active()->rowCount() || track < 0) {
return QVariantMap();
}
const PlaylistItem& item = *(playlist_->item_at(track));
const PlaylistItem& item = *(playlists_->active()->item_at(track));
return GetMetadata(item);
}
@ -479,12 +478,13 @@ void Player::PositionSet(int x) {
}
void Player::Repeat(bool enable) {
playlist_->sequence()->SetRepeatMode(
playlists_->sequence()->SetRepeatMode(
enable ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
}
void Player::ShowOSD() {
emit ForceShowOSD(current_item_);
if (current_item_)
emit ForceShowOSD(current_item_->Metadata());
}
void Player::VolumeDown(int change) {
@ -509,9 +509,9 @@ int Player::AddTrack(const QString& track, bool play_now) {
list << url;
QModelIndex index;
if (url.scheme() == "file") {
index = playlist_->InsertPaths(list, play_now ? playlist_->current_index() + 1 : -1);
index = playlists_->active()->InsertPaths(list, play_now ? playlists_->active()->current_index() + 1 : -1);
} else {
index = playlist_->InsertStreamUrls(list, play_now ? playlist_->current_index() + 1: -1);
index = playlists_->active()->InsertStreamUrls(list, play_now ? playlists_->active()->current_index() + 1: -1);
}
if (index.isValid()) {
@ -524,24 +524,24 @@ int Player::AddTrack(const QString& track, bool play_now) {
}
void Player::DelTrack(int index) {
playlist_->removeRows(index, 1);
playlists_->active()->removeRows(index, 1);
}
int Player::GetCurrentTrack() const {
return playlist_->current_index();
return playlists_->active()->current_index();
}
int Player::GetLength() const {
return playlist_->rowCount();
return playlists_->active()->rowCount();
}
void Player::SetLoop(bool enable) {
playlist_->sequence()->SetRepeatMode(
playlists_->active()->sequence()->SetRepeatMode(
enable ? PlaylistSequence::Repeat_Playlist : PlaylistSequence::Repeat_Off);
}
void Player::SetRandom(bool enable) {
playlist_->sequence()->SetShuffleMode(
playlists_->active()->sequence()->SetShuffleMode(
enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off);
}
@ -561,12 +561,13 @@ void Player::TrackAboutToEnd() {
} else {
// Crossfade is off, so start preloading the next track so we don't get a
// gap between songs.
if (current_item_options_ & PlaylistItem::ContainsMultipleTracks)
if (current_item_->options() & PlaylistItem::ContainsMultipleTracks)
return;
if (playlist_->next_index() == -1)
if (playlists_->active()->next_index() == -1)
return;
shared_ptr<PlaylistItem> item = playlist_->item_at(playlist_->next_index());
shared_ptr<PlaylistItem> item = playlists_->active()->item_at(
playlists_->active()->next_index());
if (!item)
return;

View File

@ -24,7 +24,7 @@
#include "engines/engine_fwd.h"
#include "playlist/playlistitem.h"
class Playlist;
class PlaylistManager;
class Settings;
class LastFMService;
@ -52,7 +52,8 @@ class Player : public QObject {
Q_OBJECT
public:
Player(Playlist* playlist, LastFMService* lastfm, Engine::Type engine, QObject* parent = 0);
Player(PlaylistManager* playlists, LastFMService* lastfm, Engine::Type engine,
QObject* parent = 0);
EngineBase* createEngine(Engine::Type engine);
void Init();
@ -61,8 +62,7 @@ class Player : public QObject {
Engine::State GetState() const;
int GetVolume() const;
PlaylistItem::Options GetCurrentItemOptions() const { return current_item_options_; }
Song GetCurrentItem() const { return current_item_; }
boost::shared_ptr<PlaylistItem> GetCurrentItem() const { return current_item_; }
// MPRIS
enum DBusCaps {
@ -165,12 +165,11 @@ class Player : public QObject {
private:
QVariantMap GetMetadata(const PlaylistItem& item) const;
Playlist* playlist_;
PlaylistManager* playlists_;
LastFMService* lastfm_;
QSettings settings_;
PlaylistItem::Options current_item_options_;
Song current_item_;
boost::shared_ptr<PlaylistItem> current_item_;
EngineBase* engine_;
Engine::TrackChangeType stream_change_type_;

View File

@ -9,6 +9,7 @@ set(SOURCES
playlistdelegates.cpp
playlistheader.cpp
playlistitem.cpp
playlistmanager.cpp
playlistsequence.cpp
playlisttabbar.cpp
playlistundocommands.cpp
@ -22,6 +23,7 @@ set(HEADERS
playlistcontainer.h
playlistdelegates.h
playlistheader.h
playlistmanager.h
playlistsequence.h
playlisttabbar.h
playlistview.h

View File

@ -47,11 +47,12 @@ using boost::shared_ptr;
const char* Playlist::kRowsMimetype = "application/x-clementine-playlist-rows";
const char* Playlist::kSettingsGroup = "Playlist";
Playlist::Playlist(PlaylistBackend* backend,
Playlist::Playlist(PlaylistBackend* backend, int id,
QObject *parent, SettingsProvider* settings)
: QAbstractListModel(parent),
settings_(settings ? settings : new DefaultSettingsProvider),
backend_(backend),
id_(id),
current_is_paused_(false),
current_virtual_index_(-1),
is_shuffled_(false),
@ -791,7 +792,7 @@ void Playlist::Save() const {
if (!backend_)
return;
backend_->SavePlaylistAsync(1, items_);
backend_->SavePlaylistAsync(id_, items_);
settings_->setValue("last_index", last_played_index());
}
@ -803,7 +804,7 @@ void Playlist::Restore() {
items_.clear();
virtual_items_.clear();
items_ = backend_->GetPlaylistItems(1);
items_ = backend_->GetPlaylistItems(id_);
for (int i=0 ; i<items_.count() ; ++i) {
virtual_items_ << i;

View File

@ -47,7 +47,7 @@ class Playlist : public QAbstractListModel {
friend class PlaylistUndoCommands::MoveItems;
public:
Playlist(PlaylistBackend* backend,
Playlist(PlaylistBackend* backend, int id,
QObject* parent = 0, SettingsProvider* settings = NULL);
~Playlist();
@ -98,6 +98,7 @@ class Playlist : public QAbstractListModel {
void Restore();
// Accessors
int id() const { return id_; }
int current_index() const;
int last_played_index() const;
int next_index() const;
@ -185,6 +186,7 @@ class Playlist : public QAbstractListModel {
boost::scoped_ptr<SettingsProvider> settings_;
PlaylistBackend* backend_;
int id_;
PlaylistItemList items_;
QList<int> virtual_items_; // Contains the indices into items_ in the order

View File

@ -31,8 +31,23 @@ PlaylistBackend::PlaylistBackend(Database* db, QObject* parent)
}
PlaylistBackend::PlaylistList PlaylistBackend::GetAllPlaylists() {
qWarning() << "Not implemented:" << __PRETTY_FUNCTION__;
return PlaylistList();
QSqlDatabase db(db_->Connect());
PlaylistList ret;
QSqlQuery q("SELECT ROWID, name FROM playlists", db);
q.exec();
if (db_->CheckErrors(q.lastError()))
return ret;
while (q.next()) {
Playlist p;
p.id = q.value(0).toInt();
p.name = q.value(1).toString();
ret << p;
}
return ret;
}
PlaylistItemList PlaylistBackend::GetPlaylistItems(int playlist) {
@ -105,3 +120,46 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList& items)
transaction.Commit();
}
int PlaylistBackend::CreatePlaylist(const QString &name) {
QSqlDatabase db(db_->Connect());
QSqlQuery q("INSERT INTO playlists (name) VALUES (:name)", db);
q.bindValue(":name", name);
q.exec();
if (db_->CheckErrors(q.lastError()))
return -1;
return q.lastInsertId().toInt();
}
void PlaylistBackend::RemovePlaylist(int id) {
QSqlDatabase db(db_->Connect());
QSqlQuery delete_playlist("DELETE FROM playlists WHERE ROWID=:id", db);
QSqlQuery delete_items("DELETE FROM playlist_items WHERE playlist=:id", db);
delete_playlist.bindValue(":id", id);
delete_items.bindValue(":id", id);
ScopedTransaction transaction(&db);
delete_playlist.exec();
if (db_->CheckErrors(delete_playlist.lastError()))
return;
delete_items.exec();
if (db_->CheckErrors(delete_items.lastError()))
return;
transaction.Commit();
}
void PlaylistBackend::RenamePlaylist(int id, const QString &new_name) {
QSqlDatabase db(db_->Connect());
QSqlQuery q("UPDATE playlists SET name=:name WHERE ROWID=:id", db);
q.bindValue(":name", new_name);
q.bindValue(":id", id);
q.exec();
db_->CheckErrors(q.lastError());
}

View File

@ -40,6 +40,10 @@ class PlaylistBackend : public QObject {
PlaylistItemList GetPlaylistItems(int playlist);
void SavePlaylistAsync(int playlist, const PlaylistItemList& items);
int CreatePlaylist(const QString& name);
void RemovePlaylist(int id);
void RenamePlaylist(int id, const QString& new_name);
public slots:
void SavePlaylist(int playlist, const PlaylistItemList& items);

View File

@ -15,12 +15,18 @@
*/
#include "playlistcontainer.h"
#include "playlistmanager.h"
#include "ui_playlistcontainer.h"
#include "ui/iconloader.h"
#include <QUndoStack>
#include <QInputDialog>
PlaylistContainer::PlaylistContainer(QWidget *parent)
: QWidget(parent),
ui_(new Ui_PlaylistContainer)
ui_(new Ui_PlaylistContainer),
undo_(NULL),
redo_(NULL)
{
ui_->setupUi(this);
@ -31,13 +37,9 @@ PlaylistContainer::PlaylistContainer(QWidget *parent)
ui_->tab_bar->setExpanding(false);
ui_->tab_bar->setMovable(true);
ui_->tab_bar->setShape(QTabBar::RoundedSouth);
ui_->tab_bar->addTab("foo");
ui_->tab_bar->addTab("bar");
// Connections
connect(ui_->clear, SIGNAL(clicked()), SLOT(ClearFilter()));
connect(ui_->tab_bar, SIGNAL(Rename(int,QString)), SIGNAL(Rename(int,QString)));
connect(ui_->tab_bar, SIGNAL(Remove(int)), SIGNAL(Remove(int)));
}
PlaylistContainer::~PlaylistContainer() {
@ -55,9 +57,84 @@ void PlaylistContainer::SetActions(
ui_->load->setDefaultAction(load_playlist);
ui_->tab_bar->SetActions(new_playlist, save_playlist, load_playlist);
connect(new_playlist, SIGNAL(triggered()), SLOT(New()));
connect(save_playlist, SIGNAL(triggered()), SLOT(Save()));
connect(load_playlist, SIGNAL(triggered()), SLOT(Load()));
}
void PlaylistContainer::ClearFilter() {
ui_->filter->clear();
ui_->filter->setFocus();
}
void PlaylistContainer::SetManager(PlaylistManager *manager) {
manager_ = manager;
connect(ui_->tab_bar, SIGNAL(currentChanged(int)),
manager, SLOT(SetCurrentPlaylist(int)));
connect(ui_->tab_bar, SIGNAL(Rename(int,QString)),
manager, SLOT(Rename(int,QString)));
connect(ui_->tab_bar, SIGNAL(Remove(int)),
manager, SLOT(Remove(int)));
connect(manager, SIGNAL(CurrentChanged(Playlist*)),
SLOT(SetViewModel(Playlist*)));
connect(manager, SIGNAL(PlaylistAdded(int,QString)),
SLOT(PlaylistAdded(int,QString)));
connect(manager, SIGNAL(PlaylistRemoved(int)),
SLOT(PlaylistRemoved(int)));
connect(manager, SIGNAL(PlaylistRenamed(int,QString)),
SLOT(PlaylistRenamed(int,QString)));
}
void PlaylistContainer::SetViewModel(Playlist* playlist) {
playlist->IgnoreSorting(true);
view()->setModel(playlist);
view()->SetItemDelegates(manager_->library_backend());
playlist->IgnoreSorting(false);
delete undo_;
delete redo_;
undo_ = playlist->undo_stack()->createUndoAction(this);
redo_ = playlist->undo_stack()->createRedoAction(this);
undo_->setIcon(IconLoader::Load("edit-undo"));
undo_->setShortcut(QKeySequence::Undo);
redo_->setIcon(IconLoader::Load("edit-redo"));
redo_->setShortcut(QKeySequence::Redo);
ui_->undo->setDefaultAction(undo_);
ui_->redo->setDefaultAction(redo_);
emit UndoRedoActionsChanged(undo_, redo_);
}
void PlaylistContainer::PlaylistAdded(int index, const QString &name) {
ui_->tab_bar->insertTab(index, name);
}
void PlaylistContainer::PlaylistRemoved(int index) {
ui_->tab_bar->removeTab(index);
}
void PlaylistContainer::PlaylistRenamed(int index, const QString &new_name) {
ui_->tab_bar->setTabText(index, new_name);
}
void PlaylistContainer::New() {
QString name = QInputDialog::getText(this, tr("New playlist"),
tr("Enter a name for the new playlist"),
QLineEdit::Normal, tr("Playlist"));
if (name.isNull())
return;
manager_->New(name);
}
void PlaylistContainer::Load() {
}
void PlaylistContainer::Save() {
}

View File

@ -21,6 +21,8 @@
class Ui_PlaylistContainer;
class Playlist;
class PlaylistManager;
class PlaylistView;
class PlaylistContainer : public QWidget {
@ -32,18 +34,34 @@ public:
void SetActions(QAction* new_playlist, QAction* save_playlist,
QAction* load_playlist);
void SetManager(PlaylistManager* manager);
PlaylistView* view() const;
signals:
void TabChanged(int index);
void Rename(int index, const QString& new_name);
void Remove(int index);
void UndoRedoActionsChanged(QAction* undo, QAction* redo);
private slots:
void ClearFilter();
void New();
void Load();
void Save();
void SetViewModel(Playlist* playlist);
void PlaylistAdded(int index, const QString& name);
void PlaylistRemoved(int index);
void PlaylistRenamed(int index, const QString& new_name);
private:
Ui_PlaylistContainer* ui_;
PlaylistManager* manager_;
QAction* undo_;
QAction* redo_;
};
#endif // PLAYLISTCONTAINER_H

View File

@ -0,0 +1,148 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "playlist.h"
#include "playlistbackend.h"
#include "playlistmanager.h"
PlaylistManager::PlaylistManager(QObject *parent)
: QObject(parent),
playlist_backend_(NULL),
library_backend_(NULL),
sequence_(NULL),
current_(-1),
active_(-1)
{
}
void PlaylistManager::Init(LibraryBackend* library_backend,
PlaylistBackend* playlist_backend,
PlaylistSequence* sequence) {
library_backend_ = library_backend;
playlist_backend_ = playlist_backend;
sequence_ = sequence;
foreach (const PlaylistBackend::Playlist& p, playlist_backend->GetAllPlaylists()) {
AddPlaylist(p.id, p.name);
}
// If no playlist exists then make a new one
if (playlists_.isEmpty())
New(tr("Playlist"));
}
Playlist* PlaylistManager::AddPlaylist(int id, const QString& name) {
Playlist* ret = new Playlist(playlist_backend_, id);
ret->set_sequence(sequence_);
connect(ret, SIGNAL(CurrentSongChanged(Song)), SIGNAL(CurrentSongChanged(Song)));
connect(ret, SIGNAL(PlaylistChanged()), SIGNAL(PlaylistChanged()));
connect(ret, SIGNAL(EditingFinished(QModelIndex)), SIGNAL(EditingFinished(QModelIndex)));
playlists_ << Data(ret, name);
int index = playlists_.count() - 1;
emit PlaylistAdded(index, name);
if (current_ == -1) {
SetCurrentPlaylist(index);
}
if (active_ == -1) {
SetActivePlaylist(index);
}
return ret;
}
void PlaylistManager::New(const QString& name) {
int id = playlist_backend_->CreatePlaylist(name);
if (id == -1)
qFatal("Couldn't create playlist");
AddPlaylist(id, name);
}
void PlaylistManager::Load(const QString& filename) {
}
void PlaylistManager::Save(int index, const QString& filename) {
Q_ASSERT(index >= 0 && index < playlists_.count());
}
void PlaylistManager::Rename(int index, const QString& new_name) {
Q_ASSERT(index >= 0 && index < playlists_.count());
playlist_backend_->RenamePlaylist(playlist(index)->id(), new_name);
playlists_[index].name = new_name;
emit PlaylistRenamed(index, new_name);
}
void PlaylistManager::Remove(int index) {
Q_ASSERT(index >= 0 && index < playlists_.count());
// Won't allow removing the last playlist
if (playlists_.count() <= 1)
return;
playlist_backend_->RemovePlaylist(playlist(index)->id());
playlists_.takeAt(index);
if (index == active_)
SetActivePlaylist(qMin(0, index-1));
if (index == current_)
SetCurrentPlaylist(qMin(0, index-1));
emit PlaylistRemoved(index);
}
void PlaylistManager::SetCurrentPlaylist(int index) {
Q_ASSERT(index >= 0 && index < playlists_.count());
current_ = index;
emit CurrentChanged(current());
}
void PlaylistManager::SetActivePlaylist(int index) {
Q_ASSERT(index >= 0 && index < playlists_.count());
active_ = index;
emit ActiveChanged(current());
}
void PlaylistManager::ClearCurrent() {
current()->Clear();
}
void PlaylistManager::ShuffleCurrent() {
current()->Shuffle();
}
void PlaylistManager::SetActivePlaying() {
active()->Playing();
}
void PlaylistManager::SetActivePaused() {
active()->Paused();
}
void PlaylistManager::SetActiveStopped() {
active()->Stopped();
}
void PlaylistManager::SetActiveStreamMetadata(const QUrl &url, const Song &song) {
active()->SetStreamMetadata(url, song);
}

View File

@ -0,0 +1,102 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PLAYLISTMANAGER_H
#define PLAYLISTMANAGER_H
#include <QObject>
class LibraryBackend;
class Playlist;
class PlaylistBackend;
class PlaylistSequence;
class Song;
class QModelIndex;
class QUrl;
class PlaylistManager : public QObject {
Q_OBJECT
public:
PlaylistManager(QObject *parent = 0);
int current_index() const { return current_; }
int active_index() const { return active_; }
Playlist* playlist(int index) const { return playlists_[index].p; }
Playlist* current() const { return playlist(current_index()); }
Playlist* active() const { return playlist(active_index()); }
QString name(int index) const { return playlists_[index].name; }
void Init(LibraryBackend* library_backend, PlaylistBackend* playlist_backend,
PlaylistSequence* sequence);
LibraryBackend* library_backend() const { return library_backend_; }
PlaylistBackend* playlist_backend() const { return playlist_backend_; }
PlaylistSequence* sequence() const { return sequence_; }
public slots:
void New(const QString& name);
void Load(const QString& filename);
void Save(int index, const QString& filename);
void Rename(int index, const QString& new_name);
void Remove(int index);
void SetCurrentPlaylist(int index);
void SetActivePlaylist(int index);
// Convenience slots that defer to either current() or active()
void ClearCurrent();
void ShuffleCurrent();
void SetActivePlaying();
void SetActivePaused();
void SetActiveStopped();
void SetActiveStreamMetadata(const QUrl& url, const Song& song);
signals:
void PlaylistAdded(int index, const QString& name);
void PlaylistRemoved(int index);
void PlaylistRenamed(int index, const QString& new_name);
void CurrentChanged(Playlist* new_playlist);
void ActiveChanged(Playlist* new_playlist);
// Forwarded from individual playlists
void CurrentSongChanged(const Song& song);
void PlaylistChanged();
void EditingFinished(const QModelIndex& index);
private:
Playlist* AddPlaylist(int id, const QString& name);
private:
struct Data {
Data(Playlist* _p, const QString& _name) : p(_p), name(_name) {}
Playlist* p;
QString name;
};
PlaylistBackend* playlist_backend_;
LibraryBackend* library_backend_;
PlaylistSequence* sequence_;
QList<Data> playlists_;
int current_;
int active_;
};
#endif // PLAYLISTMANAGER_H

View File

@ -327,6 +327,15 @@ msgstr "حُرِرَ بِتاريخ"
msgid "Date created"
msgstr "تاريخ الإنشاء"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr ""
@ -373,6 +382,15 @@ msgstr "اظهر القسم"
msgid "Hide %1"
msgstr "أخفِ %1"
msgid "Rename playlist"
msgstr ""
msgid "Remove playlist"
msgstr ""
msgid "Enter a new name for this playlist"
msgstr ""
#, c-format
msgid "add %n songs"
msgstr "أضِف %n أغاني\\أغنية"
@ -384,6 +402,9 @@ msgstr "أزِل %n أغاني\\أغنية"
msgid "move songs"
msgstr "انقل الأغاني"
msgid "Playlist search"
msgstr ""
msgid "Don't repeat"
msgstr "لا تكرر"
@ -583,8 +604,8 @@ msgid "Show the \"love\" and \"ban\" buttons"
msgstr ""
msgid ""
"Note that you must be a <span style=\" font-weight:600;\">paid "
"subscriber</span> to listen to Last.fm radio from within Clementine."
"Note that you must be a <span style=\" font-weight:600;\">paid subscriber</"
"span> to listen to Last.fm radio from within Clementine."
msgstr ""
msgid "Authenticating..."
@ -989,6 +1010,12 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "Save playlist..."
msgstr ""
msgid "Load playlist..."
msgstr ""
msgid "Library"
msgstr ""
@ -1001,9 +1028,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -328,6 +328,15 @@ msgstr "Datum úprav"
msgid "Date created"
msgstr "Datum vytvoření"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Seznam skladeb"
msgid "ASF"
msgstr "ASF"
@ -1007,9 +1016,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1028,9 +1034,6 @@ msgstr "Soubory"
msgid "Music"
msgstr "Hudba"
msgid "Playlist"
msgstr "Seznam skladeb"
msgid "Settings"
msgstr "Nastavení"

View File

@ -330,6 +330,15 @@ msgstr "Ændringsdato"
msgid "Date created"
msgstr "Oprettelsesdato"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Spilleliste"
msgid "ASF"
msgstr "ASF"
@ -1010,9 +1019,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1031,9 +1037,6 @@ msgstr "Filer"
msgid "Music"
msgstr "Musik"
msgid "Playlist"
msgstr "Spilleliste"
msgid "Settings"
msgstr "Indstillinger"

View File

@ -327,6 +327,15 @@ msgstr "Geändert"
msgid "Date created"
msgstr "Erstellt"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Wiedergabeliste"
msgid "ASF"
msgstr "ASF"
@ -1008,9 +1017,6 @@ msgstr "Zum aktuellen Stück springen"
msgid "Ctrl+J"
msgstr "Strg+J"
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1029,9 +1035,6 @@ msgstr "Dateien"
msgid "Music"
msgstr "Musik"
msgid "Playlist"
msgstr "Wiedergabeliste"
msgid "Settings"
msgstr "Einstellungen"

View File

@ -330,6 +330,15 @@ msgstr "Ημερομηνία τροποποίησης"
msgid "Date created"
msgstr "Ημερομηνία δημιουργίας"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Λίστα"
msgid "ASF"
msgstr "ASF"
@ -1011,9 +1020,6 @@ msgstr "Μετάβαση στο τρέχον κομμάτι που παίζει"
msgid "Ctrl+J"
msgstr "Ctrl+J"
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1032,9 +1038,6 @@ msgstr "Αρχεία"
msgid "Music"
msgstr "Μουσική"
msgid "Playlist"
msgstr "Λίστα"
msgid "Settings"
msgstr "Ρυθμίσεις"

View File

@ -327,6 +327,15 @@ msgstr "Date modified"
msgid "Date created"
msgstr "Date created"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Playlist"
msgid "ASF"
msgstr "ASF"
@ -1005,9 +1014,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1026,9 +1032,6 @@ msgstr "Files"
msgid "Music"
msgstr "Music"
msgid "Playlist"
msgstr "Playlist"
msgid "Settings"
msgstr "Settings"

View File

@ -333,6 +333,15 @@ msgstr "Fecha de modificación"
msgid "Date created"
msgstr "Fecha de creación"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Lista de reproducción"
msgid "ASF"
msgstr "ASF"
@ -1015,9 +1024,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1036,9 +1042,6 @@ msgstr "Archivos"
msgid "Music"
msgstr "Música"
msgid "Playlist"
msgstr "Lista de reproducción"
msgid "Settings"
msgstr "Preferencias"

View File

@ -327,6 +327,15 @@ msgstr "Muokattu"
msgid "Date created"
msgstr "Luotu"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr ""
@ -1001,9 +1010,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1022,9 +1028,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -332,6 +332,15 @@ msgstr "Date de modification"
msgid "Date created"
msgstr "Date de création"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Liste de lecture"
msgid "ASF"
msgstr "ASF"
@ -1013,9 +1022,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1034,9 +1040,6 @@ msgstr "Fichiers"
msgid "Music"
msgstr "Musique"
msgid "Playlist"
msgstr "Liste de lecture"
msgid "Settings"
msgstr "Configuration"

View File

@ -327,6 +327,15 @@ msgstr "Data de alterazón"
msgid "Date created"
msgstr "Data de criazón"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr "ASF"
@ -1003,9 +1012,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1024,9 +1030,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -330,6 +330,15 @@ msgstr "Data di creazione"
msgid "Date created"
msgstr "Data di modifica"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Scaletta"
msgid "ASF"
msgstr "ASF"
@ -1012,9 +1021,6 @@ msgstr "Salta alla traccia in riproduzione"
msgid "Ctrl+J"
msgstr "Ctrl+J"
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1033,9 +1039,6 @@ msgstr "File"
msgid "Music"
msgstr "Musica"
msgid "Playlist"
msgstr "Scaletta"
msgid "Settings"
msgstr "Impostazioni"

View File

@ -327,6 +327,15 @@ msgstr ""
msgid "Date created"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr "ASF"
@ -1003,9 +1012,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1024,9 +1030,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -328,6 +328,15 @@ msgstr "Endringsdato"
msgid "Date created"
msgstr "Laget dato"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Spilleliste"
msgid "ASF"
msgstr "ASF"
@ -1007,9 +1016,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1028,9 +1034,6 @@ msgstr "Filer"
msgid "Music"
msgstr "Musikk"
msgid "Playlist"
msgstr "Spilleliste"
msgid "Settings"
msgstr "Innstillinger"

View File

@ -327,6 +327,15 @@ msgstr "Data de modificacion"
msgid "Date created"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Lista de lectura"
msgid "ASF"
msgstr "ASF"
@ -373,6 +382,15 @@ msgstr ""
msgid "Hide %1"
msgstr "Amagar « %1 »"
msgid "Rename playlist"
msgstr ""
msgid "Remove playlist"
msgstr ""
msgid "Enter a new name for this playlist"
msgstr ""
#, c-format
msgid "add %n songs"
msgstr ""
@ -384,6 +402,9 @@ msgstr ""
msgid "move songs"
msgstr ""
msgid "Playlist search"
msgstr ""
msgid "Don't repeat"
msgstr ""
@ -583,8 +604,8 @@ msgid "Show the \"love\" and \"ban\" buttons"
msgstr ""
msgid ""
"Note that you must be a <span style=\" font-weight:600;\">paid "
"subscriber</span> to listen to Last.fm radio from within Clementine."
"Note that you must be a <span style=\" font-weight:600;\">paid subscriber</"
"span> to listen to Last.fm radio from within Clementine."
msgstr ""
msgid "Authenticating..."
@ -989,6 +1010,12 @@ msgstr ""
msgid "Ctrl+J"
msgstr "Ctrl+J"
msgid "Save playlist..."
msgstr ""
msgid "Load playlist..."
msgstr ""
msgid "Library"
msgstr "Bibliotèca"
@ -1001,9 +1028,6 @@ msgstr "Fichièrs"
msgid "Music"
msgstr "Musica"
msgid "Playlist"
msgstr "Lista de lectura"
msgid "Settings"
msgstr "Paramètres"

View File

@ -328,6 +328,15 @@ msgstr "Data modyfikacji"
msgid "Date created"
msgstr "Data utworzenia"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Playlista"
msgid "ASF"
msgstr ""
@ -1005,9 +1014,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1026,9 +1032,6 @@ msgstr "Pliki"
msgid "Music"
msgstr "Muzyka"
msgid "Playlist"
msgstr "Playlista"
msgid "Settings"
msgstr "Ustawienia"

View File

@ -327,6 +327,15 @@ msgstr "Data de alteração"
msgid "Date created"
msgstr "Data de criação"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Lista de Reprodução"
msgid "ASF"
msgstr "ASF"
@ -1008,9 +1017,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1029,9 +1035,6 @@ msgstr "Ficheiros"
msgid "Music"
msgstr "Música"
msgid "Playlist"
msgstr "Lista de Reprodução"
msgid "Settings"
msgstr "Configurações"

View File

@ -327,6 +327,15 @@ msgstr "Data de modificação"
msgid "Date created"
msgstr "Data de criação"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr ""
@ -1001,9 +1010,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1022,9 +1028,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -327,6 +327,15 @@ msgstr "Data modificării"
msgid "Date created"
msgstr "Data creării"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Listă de redare"
msgid "ASF"
msgstr "ASF"
@ -1002,9 +1011,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1023,9 +1029,6 @@ msgstr "Fișiere"
msgid "Music"
msgstr "Muzică"
msgid "Playlist"
msgstr "Listă de redare"
msgid "Settings"
msgstr "Setări"

View File

@ -327,6 +327,15 @@ msgstr "Дата изменения"
msgid "Date created"
msgstr "Дата создания"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Плейлист"
msgid "ASF"
msgstr "ASF"
@ -1007,9 +1016,6 @@ msgstr "Перейти к текущей композиции"
msgid "Ctrl+J"
msgstr "Ctrl+J"
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1028,9 +1034,6 @@ msgstr "Файлы"
msgid "Music"
msgstr "Музыка"
msgid "Playlist"
msgstr "Плейлист"
msgid "Settings"
msgstr "Настройки"

View File

@ -328,6 +328,15 @@ msgstr "Dátum zmeny"
msgid "Date created"
msgstr "Dátum vytvorenia"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Playlist"
msgid "ASF"
msgstr "ASF"
@ -1009,9 +1018,6 @@ msgstr "Preskočiť na práve prehrávanú skladbu"
msgid "Ctrl+J"
msgstr "Ctrl+J"
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1030,9 +1036,6 @@ msgstr "Súbory"
msgid "Music"
msgstr "Hudba"
msgid "Playlist"
msgstr "Playlist"
msgid "Settings"
msgstr "Nastavenia"

View File

@ -329,6 +329,15 @@ msgstr "Datum ändrat"
msgid "Date created"
msgstr "Datum skapat"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr "Spellista"
msgid "ASF"
msgstr "ASF"
@ -1010,9 +1019,6 @@ msgstr "Hoppa till det spår som spelas för tillfället"
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1031,9 +1037,6 @@ msgstr "Filer"
msgid "Music"
msgstr "Musik"
msgid "Playlist"
msgstr "Spellista"
msgid "Settings"
msgstr "Inställningar"

View File

@ -327,6 +327,15 @@ msgstr ""
msgid "Date created"
msgstr "Oluşturulduğu tarih"
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr ""
@ -1001,9 +1010,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1022,9 +1028,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -318,6 +318,15 @@ msgstr ""
msgid "Date created"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr ""
@ -992,9 +1001,6 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Save playlist..."
msgstr ""
@ -1013,9 +1019,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -327,6 +327,15 @@ msgstr ""
msgid "Date created"
msgstr ""
msgid "New playlist"
msgstr ""
msgid "Enter a name for the new playlist"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "ASF"
msgstr ""
@ -373,6 +382,15 @@ msgstr ""
msgid "Hide %1"
msgstr ""
msgid "Rename playlist"
msgstr ""
msgid "Remove playlist"
msgstr ""
msgid "Enter a new name for this playlist"
msgstr ""
#, c-format
msgid "add %n songs"
msgstr ""
@ -384,6 +402,9 @@ msgstr ""
msgid "move songs"
msgstr ""
msgid "Playlist search"
msgstr ""
msgid "Don't repeat"
msgstr ""
@ -583,8 +604,8 @@ msgid "Show the \"love\" and \"ban\" buttons"
msgstr ""
msgid ""
"Note that you must be a <span style=\" font-weight:600;\">paid "
"subscriber</span> to listen to Last.fm radio from within Clementine."
"Note that you must be a <span style=\" font-weight:600;\">paid subscriber</"
"span> to listen to Last.fm radio from within Clementine."
msgstr ""
msgid "Authenticating..."
@ -989,6 +1010,12 @@ msgstr ""
msgid "Ctrl+J"
msgstr ""
msgid "Save playlist..."
msgstr ""
msgid "Load playlist..."
msgstr ""
msgid "Library"
msgstr ""
@ -1001,9 +1028,6 @@ msgstr ""
msgid "Music"
msgstr ""
msgid "Playlist"
msgstr ""
msgid "Settings"
msgstr ""

View File

@ -33,6 +33,7 @@
#include "library/library.h"
#include "playlist/playlistbackend.h"
#include "playlist/playlist.h"
#include "playlist/playlistmanager.h"
#include "playlist/playlistsequence.h"
#include "playlist/playlistview.h"
#include "playlist/songplaylistitem.h"
@ -104,8 +105,8 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
database_(new Database(this)),
radio_model_(new RadioModel(database_, network, this)),
playlist_backend_(new PlaylistBackend(database_, this)),
playlist_(new Playlist(playlist_backend_, this)),
player_(new Player(playlist_, radio_model_->GetLastFMService(), engine, this)),
playlists_(new PlaylistManager(this)),
player_(new Player(playlists_, radio_model_->GetLastFMService(), engine, this)),
library_(new Library(database_, this)),
global_shortcuts_(new GlobalShortcuts(this)),
settings_dialog_(new SettingsDialog),
@ -142,10 +143,7 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
library_sort_model_->setDynamicSortFilter(true);
library_sort_model_->sort(0);
playlist_->IgnoreSorting(true);
ui_->playlist->view()->setModel(playlist_);
ui_->playlist->view()->SetItemDelegates(library_->model()->backend());
playlist_->IgnoreSorting(false);
ui_->playlist->SetManager(playlists_);
ui_->library_view->setModel(library_sort_model_);
ui_->library_view->SetLibrary(library_->model());
@ -197,7 +195,7 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
connect(ui_->action_stop_after_this_track, SIGNAL(triggered()), SLOT(StopAfterCurrent()));
connect(ui_->action_ban, SIGNAL(triggered()), radio_model_->GetLastFMService(), SLOT(Ban()));
connect(ui_->action_love, SIGNAL(triggered()), SLOT(Love()));
connect(ui_->action_clear_playlist, SIGNAL(triggered()), playlist_, SLOT(Clear()));
connect(ui_->action_clear_playlist, SIGNAL(triggered()), playlists_, SLOT(ClearCurrent()));
connect(ui_->action_remove_from_playlist, SIGNAL(triggered()), SLOT(PlaylistRemoveCurrent()));
connect(ui_->action_edit_track, SIGNAL(triggered()), SLOT(EditTracks()));
connect(ui_->action_renumber_tracks, SIGNAL(triggered()), SLOT(RenumberTracks()));
@ -205,7 +203,7 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
connect(ui_->action_edit_value, SIGNAL(triggered()), SLOT(EditValue()));
connect(ui_->action_configure, SIGNAL(triggered()), settings_dialog_.get(), SLOT(show()));
connect(ui_->action_about, SIGNAL(triggered()), about_dialog_.get(), SLOT(show()));
connect(ui_->action_shuffle, SIGNAL(triggered()), playlist_, SLOT(Shuffle()));
connect(ui_->action_shuffle, SIGNAL(triggered()), playlists_, SLOT(ShuffleCurrent()));
connect(ui_->action_open_media, SIGNAL(triggered()), SLOT(AddFile()));
connect(ui_->action_add_file, SIGNAL(triggered()), SLOT(AddFile()));
connect(ui_->action_add_folder, SIGNAL(triggered()), SLOT(AddFolder()));
@ -245,9 +243,9 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
connect(player_, SIGNAL(Playing()), SLOT(MediaPlaying()));
connect(player_, SIGNAL(Stopped()), SLOT(MediaStopped()));
connect(player_, SIGNAL(Paused()), playlist_, SLOT(Paused()));
connect(player_, SIGNAL(Playing()), playlist_, SLOT(Playing()));
connect(player_, SIGNAL(Stopped()), playlist_, SLOT(Stopped()));
connect(player_, SIGNAL(Paused()), playlists_, SLOT(SetActivePaused()));
connect(player_, SIGNAL(Playing()), playlists_, SLOT(SetActivePlaying()));
connect(player_, SIGNAL(Stopped()), playlists_, SLOT(SetActiveStopped()));
connect(player_, SIGNAL(Paused()), ui_->playlist->view(), SLOT(StopGlowing()));
connect(player_, SIGNAL(Playing()), ui_->playlist->view(), SLOT(StartGlowing()));
@ -259,10 +257,10 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
connect(player_, SIGNAL(VolumeChanged(int)), osd_, SLOT(VolumeChanged(int)));
connect(player_, SIGNAL(VolumeChanged(int)), ui_->volume, SLOT(setValue(int)));
connect(player_, SIGNAL(ForceShowOSD(Song)), SLOT(ForceShowOSD(Song)));
connect(playlist_, SIGNAL(CurrentSongChanged(Song)), osd_, SLOT(SongChanged(Song)));
connect(playlist_, SIGNAL(CurrentSongChanged(Song)), player_, SLOT(CurrentMetadataChanged(Song)));
connect(playlist_, SIGNAL(PlaylistChanged()), player_, SLOT(PlaylistChanged()));
connect(playlist_, SIGNAL(EditingFinished(QModelIndex)), SLOT(PlaylistEditFinished(QModelIndex)));
connect(playlists_, SIGNAL(CurrentSongChanged(Song)), osd_, SLOT(SongChanged(Song)));
connect(playlists_, SIGNAL(CurrentSongChanged(Song)), player_, SLOT(CurrentMetadataChanged(Song)));
connect(playlists_, SIGNAL(PlaylistChanged()), player_, SLOT(PlaylistChanged()));
connect(playlists_, SIGNAL(EditingFinished(QModelIndex)), SLOT(PlaylistEditFinished(QModelIndex)));
connect(ui_->playlist->view(), SIGNAL(doubleClicked(QModelIndex)), SLOT(PlayIndex(QModelIndex)));
connect(ui_->playlist->view(), SIGNAL(PlayPauseItem(QModelIndex)), SLOT(PlayIndex(QModelIndex)));
@ -290,23 +288,12 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
connect(ui_->library_filter, SIGNAL(LibraryConfigChanged()), SLOT(ReloadSettings()));
// Playlist menu
QAction* playlist_undo = playlist_->undo_stack()->createUndoAction(this);
QAction* playlist_redo = playlist_->undo_stack()->createRedoAction(this);
playlist_undo->setIcon(IconLoader::Load("edit-undo"));
playlist_undo->setShortcut(QKeySequence::Undo);
playlist_redo->setIcon(IconLoader::Load("edit-redo"));
playlist_redo->setShortcut(QKeySequence::Redo);
addAction(playlist_undo); // These seem to be required to get the keyboard
addAction(playlist_redo); // shortcuts to work
playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, SLOT(PlaylistPlay()));
playlist_menu_->addAction(ui_->action_stop);
playlist_stop_after_ = playlist_menu_->addAction(IconLoader::Load("media-playback-stop"), tr("Stop after this track"), this, SLOT(PlaylistStopAfter()));
playlist_menu_->addSeparator();
playlist_menu_->addAction(ui_->action_remove_from_playlist);
playlist_menu_->addAction(playlist_undo);
playlist_menu_->addAction(playlist_redo);
playlist_menu_->addSeparator();
playlist_undoredo_ = playlist_menu_->addSeparator();
playlist_menu_->addAction(ui_->action_edit_track);
playlist_menu_->addAction(ui_->action_edit_value);
playlist_menu_->addAction(ui_->action_renumber_tracks);
@ -315,12 +302,15 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
playlist_menu_->addAction(ui_->action_clear_playlist);
playlist_menu_->addAction(ui_->action_shuffle);
connect(ui_->playlist, SIGNAL(UndoRedoActionsChanged(QAction*,QAction*)),
SLOT(PlaylistUndoRedoChanged(QAction*,QAction*)));
// Radio connections
connect(radio_model_, SIGNAL(TaskStarted(MultiLoadingIndicator::TaskType)), multi_loading_indicator_, SLOT(TaskStarted(MultiLoadingIndicator::TaskType)));
connect(radio_model_, SIGNAL(TaskFinished(MultiLoadingIndicator::TaskType)), multi_loading_indicator_, SLOT(TaskFinished(MultiLoadingIndicator::TaskType)));
connect(radio_model_, SIGNAL(StreamError(QString)), SLOT(ReportError(QString)));
connect(radio_model_, SIGNAL(AsyncLoadFinished(PlaylistItem::SpecialLoadResult)), player_, SLOT(HandleSpecialLoad(PlaylistItem::SpecialLoadResult)));
connect(radio_model_, SIGNAL(StreamMetadataFound(QUrl,Song)), playlist_, SLOT(SetStreamMetadata(QUrl,Song)));
connect(radio_model_, SIGNAL(StreamMetadataFound(QUrl,Song)), playlists_, SLOT(SetActiveStreamMetadata(QUrl,Song)));
connect(radio_model_, SIGNAL(AddItemToPlaylist(RadioItem*)), SLOT(InsertRadioItem(RadioItem*)));
connect(radio_model_, SIGNAL(AddItemsToPlaylist(PlaylistItemList)), SLOT(InsertRadioItems(PlaylistItemList)));
connect(radio_model_->GetLastFMService(), SIGNAL(ScrobblingEnabledChanged(bool)), SLOT(ScrobblingEnabledChanged(bool)));
@ -392,7 +382,6 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
equalizer_->preamp_value(), equalizer_->gain_values());
// Statusbar widgets
playlist_->set_sequence(playlist_sequence_);
ui_->statusBar->addPermanentWidget(playlist_sequence_);
ui_->statusBar->addPermanentWidget(track_slider_);
ui_->statusBar->addWidget(multi_loading_indicator_);
@ -402,6 +391,9 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
StyleSheetLoader* css_loader = new StyleSheetLoader(this);
css_loader->SetStyleSheet(this, ":mainwindow.css");
// Load playlists
playlists_->Init(library_->backend(), playlist_backend_, playlist_sequence_);
// Load settings
settings_.beginGroup(kSettingsGroup);
@ -479,9 +471,9 @@ void MainWindow::FilesDoubleClicked(const QList<QUrl>& urls) {
void MainWindow::AddFilesToPlaylist(bool clear_first, const QList<QUrl>& urls) {
if (clear_first)
playlist_->Clear();
playlists_->ClearCurrent();
QModelIndex playlist_index = playlist_->InsertPaths(urls);
QModelIndex playlist_index = playlists_->current()->InsertPaths(urls);
if (playlist_index.isValid() && player_->GetState() != Engine::Playing)
player_->PlayAt(playlist_index.row(), Engine::First, true);
@ -529,9 +521,9 @@ void MainWindow::MediaPlaying() {
ui_->action_play_pause->setText(tr("Pause"));
ui_->action_play_pause->setEnabled(
! (player_->GetCurrentItemOptions() & PlaylistItem::PauseDisabled));
! (player_->GetCurrentItem()->options() & PlaylistItem::PauseDisabled));
bool is_lastfm = (player_->GetCurrentItemOptions() & PlaylistItem::LastFMControls);
bool is_lastfm = (player_->GetCurrentItem()->options() & PlaylistItem::LastFMControls);
LastFMService* lastfm = radio_model_->GetLastFMService();
ui_->action_ban->setEnabled(lastfm->IsScrobblingEnabled() && is_lastfm);
@ -549,7 +541,7 @@ void MainWindow::ScrobblingEnabledChanged(bool value) {
if (!player_->GetState() == Engine::Idle)
return;
bool is_lastfm = (player_->GetCurrentItemOptions() & PlaylistItem::LastFMControls);
bool is_lastfm = (player_->GetCurrentItem()->options() & PlaylistItem::LastFMControls);
ui_->action_ban->setEnabled(value && is_lastfm);
ui_->action_love->setEnabled(value);
}
@ -594,10 +586,10 @@ void MainWindow::AddLibraryItemToPlaylist(bool clear_first, const QModelIndex& i
idx = library_sort_model_->mapToSource(idx);
if (clear_first)
playlist_->Clear();
playlists_->ClearCurrent();
QModelIndex first_song =
playlist_->InsertLibraryItems(library_->model()->GetChildSongs(idx));
QModelIndex first_song = playlists_->current()->InsertLibraryItems(
library_->model()->GetChildSongs(idx));
if (first_song.isValid() && player_->GetState() != Engine::Playing)
player_->PlayAt(first_song.row(), Engine::First, true);
@ -630,7 +622,7 @@ void MainWindow::TrayClicked(QSystemTrayIcon::ActivationReason reason) {
}
void MainWindow::StopAfterCurrent() {
playlist_->StopAfter(playlist_->current_index());
playlists_->current()->StopAfter(playlists_->current()->current_index());
}
/**
@ -671,7 +663,7 @@ void MainWindow::FilePathChanged(const QString& path) {
void MainWindow::UpdateTrackPosition() {
// Track position in seconds
const int position = std::floor(float(player_->GetEngine()->position()) / 1000.0 + 0.5);
const int length = player_->GetCurrentItem().length();
const int length = player_->GetCurrentItem()->Metadata().length();
if (length <= 0) {
// Probably a stream that we don't know the length of
@ -683,10 +675,10 @@ void MainWindow::UpdateTrackPosition() {
// Time to scrobble?
LastFMService* lastfm = radio_model_->GetLastFMService();
if (!playlist_->has_scrobbled() &&
position >= playlist_->scrobble_point()) {
if (!playlists_->active()->has_scrobbled() &&
position >= playlists_->active()->scrobble_point()) {
lastfm->Scrobble();
playlist_->set_scrobbled(true);
playlists_->active()->set_scrobbled(true);
}
// Update the slider
@ -705,22 +697,22 @@ void MainWindow::Love() {
void MainWindow::RadioDoubleClick(const QModelIndex& index) {
if (autoclear_playlist_)
playlist_->Clear();
playlists_->ClearCurrent();
scoped_ptr<QMimeData> data(
radio_model_->merged_model()->mimeData(QModelIndexList() << index));
if (!data)
return;
playlist_->dropMimeData(data.get(), Qt::CopyAction, -1, 0, QModelIndex());
playlists_->current()->dropMimeData(data.get(), Qt::CopyAction, -1, 0, QModelIndex());
QModelIndex first_song = playlist_->index(0, 0);
QModelIndex first_song = playlists_->current()->index(0, 0);
if (first_song.isValid() && player_->GetState() != Engine::Playing)
player_->PlayAt(first_song.row(), Engine::First, true);
}
void MainWindow::InsertRadioItem(RadioItem* item) {
QModelIndex first_song = playlist_->InsertRadioStations(
QModelIndex first_song = playlists_->current()->InsertRadioStations(
QList<RadioItem*>() << item);
if (first_song.isValid() && player_->GetState() != Engine::Playing)
@ -728,7 +720,7 @@ void MainWindow::InsertRadioItem(RadioItem* item) {
}
void MainWindow::InsertRadioItems(const PlaylistItemList& items) {
QModelIndex first_song = playlist_->InsertItems(items);
QModelIndex first_song = playlists_->current()->InsertItems(items);
if (first_song.isValid() && player_->GetState() != Engine::Playing)
player_->PlayAt(first_song.row(), Engine::First, true);
@ -737,7 +729,7 @@ void MainWindow::InsertRadioItems(const PlaylistItemList& items) {
void MainWindow::PlaylistRightClick(const QPoint& global_pos, const QModelIndex& index) {
playlist_menu_index_ = index;
if (playlist_->current_index() == index.row() && player_->GetState() == Engine::Playing) {
if (playlists_->current()->current_index() == index.row() && player_->GetState() == Engine::Playing) {
playlist_play_pause_->setText(tr("Pause"));
playlist_play_pause_->setIcon(IconLoader::Load("media-playback-pause"));
} else {
@ -747,8 +739,8 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos, const QModelIndex&
if (index.isValid()) {
playlist_play_pause_->setEnabled(
playlist_->current_index() != index.row() ||
! (playlist_->item_at(index.row())->options() & PlaylistItem::PauseDisabled));
playlists_->current()->current_index() != index.row() ||
! (playlists_->current()->item_at(index.row())->options() & PlaylistItem::PauseDisabled));
} else {
playlist_play_pause_->setEnabled(false);
}
@ -761,7 +753,7 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos, const QModelIndex&
foreach (const QModelIndex& index, selection) {
if (index.column() != 0)
continue;
if (playlist_->item_at(index.row())->Metadata().IsEditable()) {
if (playlists_->current()->item_at(index.row())->Metadata().IsEditable()) {
editable++;
}
}
@ -786,7 +778,7 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos, const QModelIndex&
ui_->action_edit_value->isVisible() && editable);
QString column_name = Playlist::column_name(column);
QString column_value = playlist_->data(index).toString();
QString column_value = playlists_->current()->data(index).toString();
if (column_value.length() > 25)
column_value = column_value.left(25) + "...";
@ -799,7 +791,7 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos, const QModelIndex&
}
void MainWindow::PlaylistPlay() {
if (playlist_->current_index() == playlist_menu_index_.row()) {
if (playlists_->current()->current_index() == playlist_menu_index_.row()) {
player_->PlayPause();
} else {
player_->PlayAt(playlist_menu_index_.row(), Engine::Manual, true);
@ -807,7 +799,7 @@ void MainWindow::PlaylistPlay() {
}
void MainWindow::PlaylistStopAfter() {
playlist_->StopAfter(playlist_menu_index_.row());
playlists_->current()->StopAfter(playlist_menu_index_.row());
}
void MainWindow::EditTracks() {
@ -818,7 +810,7 @@ void MainWindow::EditTracks() {
ui_->playlist->view()->selectionModel()->selection().indexes()) {
if (index.column() != 0)
continue;
Song song = playlist_->item_at(index.row())->Metadata();
Song song = playlists_->current()->item_at(index.row())->Metadata();
if (song.IsEditable()) {
songs << song;
@ -832,7 +824,7 @@ void MainWindow::EditTracks() {
if (edit_tag_dialog_->exec() == QDialog::Rejected)
return;
playlist_->ReloadItems(rows);
playlists_->current()->ReloadItems(rows);
}
void MainWindow::RenumberTracks() {
@ -844,7 +836,7 @@ void MainWindow::RenumberTracks() {
// if first selected song has a track number set, start from that offset
if (indexes.size()) {
Song first_song=playlist_->item_at(indexes[0].row())->Metadata();
Song first_song=playlists_->current()->item_at(indexes[0].row())->Metadata();
if (int first_track = first_song.track())
track = first_track;
}
@ -854,12 +846,12 @@ void MainWindow::RenumberTracks() {
continue;
int row = index.row();
Song song = playlist_->item_at(row)->Metadata();
Song song = playlists_->current()->item_at(row)->Metadata();
if (song.IsEditable()) {
song.set_track(track);
song.Save();
playlist_->item_at(row)->Reload();
playlists_->current()->item_at(row)->Reload();
}
track++;
}
@ -867,7 +859,7 @@ void MainWindow::RenumberTracks() {
void MainWindow::SelectionSetValue() {
Playlist::Column column = (Playlist::Column)playlist_menu_index_.column();
QVariant column_value = playlist_->data(playlist_menu_index_);
QVariant column_value = playlists_->current()->data(playlist_menu_index_);
QModelIndexList indexes=ui_->playlist->view()->selectionModel()->selection().indexes();
foreach (const QModelIndex& index, indexes) {
@ -875,11 +867,11 @@ void MainWindow::SelectionSetValue() {
continue;
int row = index.row();
Song song = playlist_->item_at(row)->Metadata();
Song song = playlists_->current()->item_at(row)->Metadata();
if(Playlist::set_column_value(song, column, column_value)) {
song.Save();
playlist_->item_at(row)->Reload();
playlists_->current()->item_at(row)->Reload();
}
}
}
@ -920,13 +912,13 @@ void MainWindow::AddFile() {
file.open(QIODevice::ReadOnly);
M3UParser parser(&file, info.dir());
const SongList& songs = parser.Parse();
playlist_->InsertSongs(songs);
playlists_->current()->InsertSongs(songs);
} else if (path.endsWith(".xspf") || path.endsWith(".xml")) {
QFile file(path);
file.open(QIODevice::ReadOnly);
XSPFParser parser(&file);
const SongList& songs = parser.Parse();
playlist_->InsertSongs(songs);
playlists_->current()->InsertSongs(songs);
} else {
QUrl url(QUrl::fromLocalFile(path));
if (url.scheme().isEmpty())
@ -934,7 +926,7 @@ void MainWindow::AddFile() {
urls << url;
}
}
playlist_->InsertPaths(urls);
playlists_->current()->InsertPaths(urls);
}
void MainWindow::AddFolder() {
@ -953,7 +945,7 @@ void MainWindow::AddFolder() {
QUrl url(QUrl::fromLocalFile(directory));
if (url.scheme().isEmpty())
url.setScheme("file");
playlist_->InsertPaths(QList<QUrl>() << url);
playlists_->current()->InsertPaths(QList<QUrl>() << url);
}
void MainWindow::AddStream() {
@ -964,7 +956,7 @@ void MainWindow::AddStreamAccepted() {
QList<QUrl> urls;
urls << add_stream_dialog_->url();
playlist_->InsertStreamUrls(urls);
playlists_->current()->InsertStreamUrls(urls);
}
void MainWindow::PlaylistRemoveCurrent() {
@ -1022,11 +1014,11 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
switch (options.url_list_action()) {
case CommandlineOptions::UrlList_Load:
playlist_->Clear();
playlists_->ClearCurrent();
// fallthrough
case CommandlineOptions::UrlList_Append:
playlist_->InsertPaths(options.urls(), -1);
playlists_->current()->InsertPaths(options.urls(), -1);
break;
}
@ -1067,3 +1059,8 @@ void MainWindow::CheckForUpdates() {
mac::CheckForUpdates();
#endif
}
void MainWindow::PlaylistUndoRedoChanged(QAction *undo, QAction *redo) {
playlist_menu_->insertAction(playlist_undoredo_, undo);
playlist_menu_->insertAction(playlist_undoredo_, redo);
}

View File

@ -27,7 +27,7 @@
#include "library/librarymodel.h"
#include "playlist/playlistitem.h"
class Playlist;
class PlaylistManager;
class Player;
class Library;
class PlaylistBackend;
@ -102,6 +102,7 @@ class MainWindow : public QMainWindow {
void RenumberTracks();
void SelectionSetValue();
void EditValue();
void PlaylistUndoRedoChanged(QAction* undo, QAction* redo);
void PlayIndex(const QModelIndex& index);
void StopAfterCurrent();
@ -160,7 +161,7 @@ class MainWindow : public QMainWindow {
Database* database_;
RadioModel* radio_model_;
PlaylistBackend* playlist_backend_;
Playlist* playlist_;
PlaylistManager* playlists_;
Player* player_;
Library* library_;
GlobalShortcuts* global_shortcuts_;
@ -175,6 +176,7 @@ class MainWindow : public QMainWindow {
QMenu* playlist_menu_;
QAction* playlist_play_pause_;
QAction* playlist_stop_after_;
QAction* playlist_undoredo_;
QModelIndex playlist_menu_index_;
QSortFilterProxyModel* library_sort_model_;