2010-03-24 00:11:46 +01:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
#include "playlist.h"
|
2010-05-09 02:10:26 +02:00
|
|
|
#include "playlistbackend.h"
|
2010-05-22 18:36:13 +02:00
|
|
|
#include "playlistfilter.h"
|
2010-04-19 20:44:35 +02:00
|
|
|
#include "playlistundocommands.h"
|
2010-05-22 18:36:13 +02:00
|
|
|
#include "songmimedata.h"
|
|
|
|
#include "songplaylistitem.h"
|
2010-05-10 23:50:31 +02:00
|
|
|
#include "library/library.h"
|
|
|
|
#include "library/librarybackend.h"
|
|
|
|
#include "library/libraryplaylistitem.h"
|
|
|
|
#include "radio/magnatuneservice.h"
|
|
|
|
#include "radio/magnatuneplaylistitem.h"
|
|
|
|
#include "radio/radiomimedata.h"
|
|
|
|
#include "radio/radiomodel.h"
|
|
|
|
#include "radio/radioplaylistitem.h"
|
|
|
|
#include "radio/savedradio.h"
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
#include <QtDebug>
|
|
|
|
#include <QMimeData>
|
|
|
|
#include <QBuffer>
|
2009-12-24 21:40:03 +01:00
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QDirIterator>
|
2010-04-19 20:44:35 +02:00
|
|
|
#include <QUndoStack>
|
2010-05-22 18:36:13 +02:00
|
|
|
#include <QSortFilterProxyModel>
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
#include <boost/bind.hpp>
|
2010-03-08 18:55:40 +01:00
|
|
|
#include <algorithm>
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
#include <lastfm/ScrobblePoint>
|
|
|
|
|
2010-04-15 00:36:28 +02:00
|
|
|
using boost::shared_ptr;
|
|
|
|
|
2010-01-08 17:21:22 +01:00
|
|
|
const char* Playlist::kRowsMimetype = "application/x-clementine-playlist-rows";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-05-21 00:30:55 +02:00
|
|
|
Playlist::Playlist(PlaylistBackend* backend, int id, QObject *parent)
|
2010-04-14 16:48:10 +02:00
|
|
|
: QAbstractListModel(parent),
|
2010-05-22 18:36:13 +02:00
|
|
|
proxy_(new PlaylistFilter(this)),
|
2010-05-09 02:10:26 +02:00
|
|
|
backend_(backend),
|
2010-05-20 23:21:55 +02:00
|
|
|
id_(id),
|
2009-12-24 20:16:07 +01:00
|
|
|
current_is_paused_(false),
|
2010-03-08 18:55:40 +01:00
|
|
|
current_virtual_index_(-1),
|
|
|
|
is_shuffled_(false),
|
2009-12-29 20:57:33 +01:00
|
|
|
scrobble_point_(-1),
|
2009-12-29 21:11:03 +01:00
|
|
|
has_scrobbled_(false),
|
2010-03-10 01:04:04 +01:00
|
|
|
playlist_sequence_(NULL),
|
2010-04-19 20:44:35 +02:00
|
|
|
ignore_sorting_(false),
|
|
|
|
undo_stack_(new QUndoStack(this))
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-03-24 21:58:17 +01:00
|
|
|
connect(this, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SIGNAL(PlaylistChanged()));
|
|
|
|
connect(this, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SIGNAL(PlaylistChanged()));
|
2010-05-09 02:10:26 +02:00
|
|
|
|
|
|
|
Restore();
|
2010-05-22 18:36:13 +02:00
|
|
|
|
|
|
|
proxy_->setSourceModel(this);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Playlist::~Playlist() {
|
2010-04-15 00:05:41 +02:00
|
|
|
items_.clear();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant Playlist::headerData(int section, Qt::Orientation, int role) const {
|
2010-04-11 23:45:19 +02:00
|
|
|
if (role != Qt::DisplayRole && role != Qt::ToolTipRole)
|
2009-12-24 20:16:07 +01:00
|
|
|
return QVariant();
|
|
|
|
|
2010-03-24 01:12:52 +01:00
|
|
|
QString name = column_name((Playlist::Column)section);
|
|
|
|
if(name.size())
|
2010-04-11 23:45:19 +02:00
|
|
|
return name;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
2010-03-26 00:48:58 +01:00
|
|
|
bool Playlist::column_is_editable(Playlist::Column column) {
|
|
|
|
switch(column) {
|
|
|
|
case Column_Title:
|
|
|
|
case Column_Artist:
|
|
|
|
case Column_Album:
|
|
|
|
case Column_AlbumArtist:
|
|
|
|
case Column_Composer:
|
|
|
|
case Column_Track:
|
|
|
|
case Column_Disc:
|
|
|
|
case Column_Year:
|
|
|
|
case Column_Genre:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::set_column_value(Song& song, Playlist::Column column,
|
|
|
|
const QVariant& value) {
|
|
|
|
|
|
|
|
if (!song.IsEditable())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch(column) {
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Title:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_title(value.toString());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Artist:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_artist(value.toString());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Album:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_album(value.toString());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_AlbumArtist:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_albumartist(value.toString());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Composer:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_composer(value.toString());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Track:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_track(value.toInt());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Disc:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_disc(value.toInt());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Year:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_year(value.toInt());
|
|
|
|
break;
|
2010-03-28 00:45:46 +01:00
|
|
|
case Column_Genre:
|
2010-03-26 00:48:58 +01:00
|
|
|
song.set_genre(value.toString());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
QVariant Playlist::data(const QModelIndex& index, int role) const {
|
|
|
|
switch (role) {
|
|
|
|
case Role_IsCurrent:
|
2010-04-20 21:08:12 +02:00
|
|
|
return current_item_index_.isValid() && index.row() == current_item_index_.row();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case Role_IsPaused:
|
|
|
|
return current_is_paused_;
|
|
|
|
|
|
|
|
case Role_StopAfter:
|
|
|
|
return stop_after_.isValid() && stop_after_.row() == index.row();
|
|
|
|
|
2010-03-26 00:48:58 +01:00
|
|
|
case Qt::EditRole:
|
2010-04-11 23:43:37 +02:00
|
|
|
case Qt::ToolTipRole:
|
2009-12-24 20:16:07 +01:00
|
|
|
case Qt::DisplayRole: {
|
2010-04-15 00:36:28 +02:00
|
|
|
shared_ptr<PlaylistItem> item = items_[index.row()];
|
2009-12-29 20:22:02 +01:00
|
|
|
Song song = item->Metadata();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-03-23 15:24:48 +01:00
|
|
|
// Don't forget to change Playlist::CompareItems when adding new columns
|
2009-12-24 20:16:07 +01:00
|
|
|
switch (index.column()) {
|
2010-03-25 16:00:56 +01:00
|
|
|
case Column_Title:
|
|
|
|
return song.title().isEmpty() ? song.basefilename() : song.title();
|
2009-12-29 20:22:02 +01:00
|
|
|
case Column_Artist: return song.artist();
|
|
|
|
case Column_Album: return song.album();
|
|
|
|
case Column_Length: return song.length();
|
|
|
|
case Column_Track: return song.track();
|
2009-12-30 05:05:33 +01:00
|
|
|
case Column_Disc: return song.disc();
|
|
|
|
case Column_Year: return song.year();
|
|
|
|
case Column_Genre: return song.genre();
|
2010-03-07 23:46:41 +01:00
|
|
|
case Column_AlbumArtist: return song.albumartist();
|
|
|
|
case Column_Composer: return song.composer();
|
2009-12-30 05:05:33 +01:00
|
|
|
|
|
|
|
case Column_BPM: return song.bpm();
|
|
|
|
case Column_Bitrate: return song.bitrate();
|
|
|
|
case Column_Samplerate: return song.samplerate();
|
|
|
|
case Column_Filename: return song.filename();
|
2010-03-19 11:39:22 +01:00
|
|
|
case Column_BaseFilename: return song.basefilename();
|
2009-12-30 05:05:33 +01:00
|
|
|
case Column_Filesize: return song.filesize();
|
2010-03-07 23:46:41 +01:00
|
|
|
case Column_Filetype: return song.filetype();
|
|
|
|
case Column_DateModified: return song.mtime();
|
|
|
|
case Column_DateCreated: return song.ctime();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-10 19:04:05 +02:00
|
|
|
case Qt::TextAlignmentRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case Column_Length:
|
|
|
|
case Column_Track:
|
|
|
|
case Column_Disc:
|
|
|
|
case Column_Year:
|
|
|
|
case Column_BPM:
|
|
|
|
case Column_Bitrate:
|
|
|
|
case Column_Samplerate:
|
|
|
|
case Column_Filesize:
|
2010-06-11 18:41:29 +02:00
|
|
|
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
|
2010-06-10 19:04:05 +02:00
|
|
|
|
|
|
|
default:
|
2010-06-11 18:41:29 +02:00
|
|
|
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
|
2010-06-10 19:04:05 +02:00
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-11 19:58:58 +02:00
|
|
|
bool Playlist::setData(const QModelIndex &index, const QVariant &value, int) {
|
2010-03-26 00:48:58 +01:00
|
|
|
int row = index.row();
|
|
|
|
Song song = item_at(row)->Metadata();
|
|
|
|
|
2010-04-27 15:46:44 +02:00
|
|
|
if (index.data() == value)
|
|
|
|
return false;
|
|
|
|
|
2010-03-26 00:48:58 +01:00
|
|
|
if(!set_column_value(song, (Column)index.column(), value))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
song.Save();
|
|
|
|
item_at(row)->Reload();
|
2010-03-31 21:09:35 +02:00
|
|
|
emit dataChanged(index, index);
|
2010-04-11 19:58:58 +02:00
|
|
|
emit EditingFinished(index);
|
2010-03-26 00:48:58 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
int Playlist::current_index() const {
|
2010-04-20 21:08:12 +02:00
|
|
|
return current_item_index_.isValid() ? current_item_index_.row() : -1;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-04-12 02:40:03 +02:00
|
|
|
int Playlist::last_played_index() const {
|
2010-04-20 21:08:12 +02:00
|
|
|
return last_played_item_index_.isValid() ? last_played_item_index_.row() : -1;
|
2010-04-12 02:40:03 +02:00
|
|
|
}
|
|
|
|
|
2010-03-08 19:05:41 +01:00
|
|
|
void Playlist::ShuffleModeChanged(PlaylistSequence::ShuffleMode mode) {
|
|
|
|
is_shuffled_ = (mode != PlaylistSequence::Shuffle_Off);
|
2010-03-08 18:55:40 +01:00
|
|
|
ReshuffleIndices();
|
|
|
|
}
|
|
|
|
|
2010-05-22 18:36:13 +02:00
|
|
|
bool Playlist::FilterContainsVirtualIndex(int i) const {
|
|
|
|
if (i<0 || i>=virtual_items_.count())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return proxy_->filterAcceptsRow(virtual_items_[i], QModelIndex());
|
|
|
|
}
|
|
|
|
|
2010-03-08 18:55:40 +01:00
|
|
|
int Playlist::NextVirtualIndex(int i) const {
|
2010-03-08 19:05:41 +01:00
|
|
|
PlaylistSequence::RepeatMode repeat_mode = playlist_sequence_->repeat_mode();
|
|
|
|
PlaylistSequence::ShuffleMode shuffle_mode = playlist_sequence_->shuffle_mode();
|
|
|
|
bool album_only = repeat_mode == PlaylistSequence::Repeat_Album ||
|
|
|
|
shuffle_mode == PlaylistSequence::Shuffle_Album;
|
2010-03-08 18:55:40 +01:00
|
|
|
|
|
|
|
// This one's easy - if we have to repeat the current track then just return i
|
2010-05-22 18:36:13 +02:00
|
|
|
if (repeat_mode == PlaylistSequence::Repeat_Track) {
|
|
|
|
if (!FilterContainsVirtualIndex(i))
|
|
|
|
return virtual_items_.count(); // It's not in the filter any more
|
2010-03-08 18:55:40 +01:00
|
|
|
return i;
|
2010-05-22 18:36:13 +02:00
|
|
|
}
|
2010-03-08 18:55:40 +01:00
|
|
|
|
|
|
|
// If we're not bothered about whether a song is on the same album then
|
|
|
|
// return the next virtual index, whatever it is.
|
2010-05-22 18:36:13 +02:00
|
|
|
if (!album_only) {
|
|
|
|
++i;
|
|
|
|
|
|
|
|
// Advance i until we find any track that is in the filter
|
|
|
|
while (i < virtual_items_.count() && !FilterContainsVirtualIndex(i))
|
|
|
|
++i;
|
|
|
|
return i;
|
|
|
|
}
|
2010-03-08 18:55:40 +01:00
|
|
|
|
|
|
|
// 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 ; j<virtual_items_.count(); ++j) {
|
|
|
|
Song this_song = item_at(virtual_items_[j])->Metadata();
|
2010-03-24 13:07:37 +01:00
|
|
|
if (((last_song.is_compilation() && this_song.is_compilation()) ||
|
2010-03-08 18:55:40 +01:00
|
|
|
last_song.artist() == this_song.artist()) &&
|
2010-05-22 18:36:13 +02:00
|
|
|
last_song.album() == this_song.album() &&
|
|
|
|
FilterContainsVirtualIndex(j)) {
|
2010-03-08 18:55:40 +01:00
|
|
|
return j; // Found one
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Couldn't find one - return past the end of the list
|
|
|
|
return virtual_items_.count();
|
|
|
|
}
|
|
|
|
|
2010-05-17 02:02:22 +02:00
|
|
|
int Playlist::PreviousVirtualIndex(int i) const {
|
|
|
|
PlaylistSequence::RepeatMode repeat_mode = playlist_sequence_->repeat_mode();
|
|
|
|
PlaylistSequence::ShuffleMode shuffle_mode = playlist_sequence_->shuffle_mode();
|
|
|
|
bool album_only = repeat_mode == PlaylistSequence::Repeat_Album ||
|
|
|
|
shuffle_mode == PlaylistSequence::Shuffle_Album;
|
|
|
|
|
|
|
|
// This one's easy - if we have to repeat the current track then just return i
|
2010-05-22 18:36:13 +02:00
|
|
|
if (repeat_mode == PlaylistSequence::Repeat_Track) {
|
|
|
|
if (!FilterContainsVirtualIndex(i))
|
|
|
|
return -1;
|
2010-05-17 02:02:22 +02:00
|
|
|
return i;
|
2010-05-22 18:36:13 +02:00
|
|
|
}
|
2010-05-17 02:02:22 +02:00
|
|
|
|
|
|
|
// If we're not bothered about whether a song is on the same album then
|
|
|
|
// return the previous virtual index, whatever it is.
|
2010-05-22 18:36:13 +02:00
|
|
|
if (!album_only) {
|
|
|
|
--i;
|
|
|
|
|
|
|
|
// Decrement i until we find any track that is in the filter
|
|
|
|
while (i>=0 && !FilterContainsVirtualIndex(i))
|
|
|
|
--i;
|
|
|
|
return i;
|
|
|
|
}
|
2010-05-17 02:02:22 +02:00
|
|
|
|
|
|
|
// We need to decrement i until we get something else on the same album
|
|
|
|
Song last_song = current_item_metadata();
|
|
|
|
for (int j=i-1 ; j>=0; --j) {
|
|
|
|
Song this_song = item_at(virtual_items_[j])->Metadata();
|
|
|
|
if (((last_song.is_compilation() && this_song.is_compilation()) ||
|
|
|
|
last_song.artist() == this_song.artist()) &&
|
2010-05-22 18:36:13 +02:00
|
|
|
last_song.album() == this_song.album() &&
|
|
|
|
FilterContainsVirtualIndex(j)) {
|
2010-05-17 02:02:22 +02:00
|
|
|
return j; // Found one
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Couldn't find one - return before the start of the list
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
int Playlist::next_index() const {
|
2010-03-08 18:55:40 +01:00
|
|
|
// Did we want to stop after this track?
|
2009-12-29 20:57:33 +01:00
|
|
|
if (stop_after_.isValid() && current_index() == stop_after_.row())
|
2009-12-24 20:16:07 +01:00
|
|
|
return -1;
|
|
|
|
|
2010-03-08 18:55:40 +01:00
|
|
|
int next_virtual_index = NextVirtualIndex(current_virtual_index_);
|
|
|
|
if (next_virtual_index >= virtual_items_.count()) {
|
|
|
|
// We've gone off the end of the playlist.
|
|
|
|
|
2010-03-08 19:05:41 +01:00
|
|
|
switch (playlist_sequence_->repeat_mode()) {
|
|
|
|
case PlaylistSequence::Repeat_Off:
|
2010-03-08 18:55:40 +01:00
|
|
|
return -1;
|
2010-03-08 19:05:41 +01:00
|
|
|
case PlaylistSequence::Repeat_Track:
|
2010-03-08 18:55:40 +01:00
|
|
|
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];
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
int Playlist::previous_index() const {
|
2010-05-17 02:02:22 +02:00
|
|
|
int prev_virtual_index = PreviousVirtualIndex(current_virtual_index_);
|
|
|
|
if (prev_virtual_index < 0) {
|
|
|
|
// We've gone off the beginning of the playlist.
|
|
|
|
|
|
|
|
switch (playlist_sequence_->repeat_mode()) {
|
|
|
|
case PlaylistSequence::Repeat_Off:
|
|
|
|
return -1;
|
|
|
|
case PlaylistSequence::Repeat_Track:
|
|
|
|
prev_virtual_index = current_virtual_index_;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
prev_virtual_index = PreviousVirtualIndex(virtual_items_.count());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Still off the beginning? Then just give up
|
|
|
|
if (prev_virtual_index < 0)
|
2009-12-24 20:16:07 +01:00
|
|
|
return -1;
|
2010-05-17 02:02:22 +02:00
|
|
|
|
|
|
|
return virtual_items_[prev_virtual_index];
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
void Playlist::set_current_index(int i) {
|
2010-04-20 21:08:12 +02:00
|
|
|
QModelIndex old_current = current_item_index_;
|
2009-12-26 23:15:57 +01:00
|
|
|
ClearStreamMetadata();
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
current_item_index_ = QPersistentModelIndex(index(i, 0, QModelIndex()));
|
2010-04-12 03:06:47 +02:00
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
if (current_item_index_.isValid()) {
|
|
|
|
last_played_item_index_ = current_item_index_;
|
|
|
|
current_item_ = items_[current_item_index_.row()];
|
2010-04-12 03:06:47 +02:00
|
|
|
Save();
|
2010-04-20 21:08:12 +02:00
|
|
|
} else {
|
|
|
|
current_item_.reset();
|
2010-04-12 03:06:47 +02:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
if (old_current.isValid())
|
2010-05-22 18:36:13 +02:00
|
|
|
emit dataChanged(old_current, old_current.sibling(old_current.row(), ColumnCount-1));
|
2010-01-08 15:52:05 +01:00
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
if (current_item_index_.isValid() && current_item_index_ != old_current) {
|
2010-05-22 18:36:13 +02:00
|
|
|
emit dataChanged(current_item_index_, current_item_index_.sibling(current_item_index_.row(), ColumnCount-1));
|
2010-01-08 15:52:05 +01:00
|
|
|
emit CurrentSongChanged(current_item_metadata());
|
|
|
|
}
|
2009-12-29 20:57:33 +01:00
|
|
|
|
2010-03-08 18:55:40 +01:00
|
|
|
// 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;
|
|
|
|
|
2009-12-29 20:57:33 +01:00
|
|
|
UpdateScrobblePoint();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Qt::ItemFlags Playlist::flags(const QModelIndex &index) const {
|
2010-03-26 00:48:58 +01:00
|
|
|
|
|
|
|
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
|
|
|
|
|
|
|
if(column_is_editable((Column)index.column()))
|
|
|
|
flags |= Qt::ItemIsEditable;
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
if (index.isValid())
|
2010-03-26 00:48:58 +01:00
|
|
|
return flags | Qt::ItemIsDragEnabled;
|
|
|
|
|
|
|
|
return Qt::ItemIsDropEnabled;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QStringList Playlist::mimeTypes() const {
|
|
|
|
return QStringList() << "text/uri-list" << kRowsMimetype;
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::DropActions Playlist::supportedDropActions() const {
|
2010-05-05 13:50:45 +02:00
|
|
|
return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int, const QModelIndex&) {
|
|
|
|
if (action == Qt::IgnoreAction)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (const SongMimeData* song_data = qobject_cast<const SongMimeData*>(data)) {
|
2010-05-09 19:32:07 +02:00
|
|
|
// Dragged from a library
|
|
|
|
// We want to check if these songs are from the actual local file backend,
|
|
|
|
// if they are we treat them differently.
|
|
|
|
if (song_data->backend->songs_table() == Library::kSongsTable)
|
|
|
|
InsertLibraryItems(song_data->songs, row);
|
2010-05-10 16:19:43 +02:00
|
|
|
else if (song_data->backend->songs_table() == MagnatuneService::kSongsTable)
|
|
|
|
InsertMagnatuneItems(song_data->songs, row);
|
2010-05-09 19:32:07 +02:00
|
|
|
else
|
|
|
|
InsertSongs(song_data->songs, row);
|
2009-12-26 22:35:45 +01:00
|
|
|
} else if (const RadioMimeData* radio_data = qobject_cast<const RadioMimeData*>(data)) {
|
|
|
|
// Dragged from the Radio pane
|
2009-12-30 00:17:54 +01:00
|
|
|
InsertRadioStations(radio_data->items, row);
|
2009-12-24 20:16:07 +01:00
|
|
|
} else if (data->hasFormat(kRowsMimetype)) {
|
|
|
|
// Dragged from the playlist
|
|
|
|
// Rearranging it is tricky...
|
|
|
|
|
|
|
|
// Get the list of rows that were moved
|
|
|
|
QList<int> source_rows;
|
|
|
|
QDataStream stream(data->data(kRowsMimetype));
|
|
|
|
stream >> source_rows;
|
|
|
|
qStableSort(source_rows); // Make sure we take them in order
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
undo_stack_->push(new PlaylistUndoCommands::MoveItems(this, source_rows, row));
|
|
|
|
} else if (data->hasUrls()) {
|
|
|
|
// URL list dragged from the file list or some other app
|
|
|
|
InsertPaths(data->urls(), row);
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
return true;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
void Playlist::MoveItemsWithoutUndo(const QList<int> &source_rows, int pos) {
|
|
|
|
layoutAboutToBeChanged();
|
|
|
|
PlaylistItemList moved_items;
|
|
|
|
|
|
|
|
// Take the items out of the list first, keeping track of whether the
|
|
|
|
// insertion point changes
|
|
|
|
int offset = 0;
|
|
|
|
foreach (int source_row, source_rows) {
|
|
|
|
moved_items << items_.takeAt(source_row-offset);
|
|
|
|
if (pos != -1 && pos >= source_row)
|
|
|
|
pos --;
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put the items back in
|
|
|
|
const int start = pos == -1 ? items_.count() : pos;
|
|
|
|
for (int i=start ; i<start+moved_items.count() ; ++i) {
|
|
|
|
items_.insert(i, moved_items[i - start]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update persistent indexes
|
|
|
|
foreach (const QModelIndex& pidx, persistentIndexList()) {
|
|
|
|
const int dest_offset = source_rows.indexOf(pidx.row());
|
|
|
|
if (dest_offset != -1) {
|
|
|
|
// This index was moved
|
|
|
|
changePersistentIndex(pidx, index(start + dest_offset, pidx.column(), QModelIndex()));
|
|
|
|
} else {
|
|
|
|
int d = 0;
|
|
|
|
foreach (int source_row, source_rows) {
|
|
|
|
if (pidx.row() > source_row)
|
|
|
|
d --;
|
2010-02-03 22:26:54 +01:00
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
if (pidx.row() + d >= start)
|
|
|
|
d += source_rows.count();
|
|
|
|
|
|
|
|
changePersistentIndex(pidx, index(pidx.row() + d, pidx.column(), QModelIndex()));
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
}
|
|
|
|
current_virtual_index_ = virtual_items_.indexOf(current_index());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
layoutChanged();
|
|
|
|
Save();
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
void Playlist::MoveItemsWithoutUndo(int start, const QList<int>& dest_rows) {
|
|
|
|
layoutAboutToBeChanged();
|
|
|
|
PlaylistItemList moved_items;
|
|
|
|
|
|
|
|
if (start == -1)
|
|
|
|
start = items_.count() - dest_rows.count();
|
|
|
|
|
|
|
|
// Take the items out of the list first, keeping track of whether the
|
|
|
|
// insertion point changes
|
|
|
|
for (int i=start ; i<start + dest_rows.count() ; ++i)
|
|
|
|
moved_items << items_.takeAt(start);
|
|
|
|
|
|
|
|
// Put the items back in
|
|
|
|
int offset = 0;
|
|
|
|
foreach (int dest_row, dest_rows) {
|
|
|
|
items_.insert(dest_row, moved_items[offset]);
|
|
|
|
offset ++;
|
2009-12-24 23:26:58 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
// Update persistent indexes
|
|
|
|
foreach (const QModelIndex& pidx, persistentIndexList()) {
|
|
|
|
if (pidx.row() >= start && pidx.row() < start + dest_rows.count()) {
|
|
|
|
// This index was moved
|
|
|
|
const int i = pidx.row() - start;
|
|
|
|
changePersistentIndex(pidx, index(dest_rows[i], pidx.column(), QModelIndex()));
|
|
|
|
} else {
|
|
|
|
int d = 0;
|
|
|
|
if (pidx.row() >= start + dest_rows.count())
|
|
|
|
d -= dest_rows.count();
|
|
|
|
|
|
|
|
foreach (int dest_row, dest_rows) {
|
|
|
|
if (pidx.row() + d > dest_row)
|
|
|
|
d ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
changePersistentIndex(pidx, index(pidx.row() + d, pidx.column(), QModelIndex()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current_virtual_index_ = virtual_items_.indexOf(current_index());
|
|
|
|
|
|
|
|
layoutChanged();
|
|
|
|
Save();
|
2009-12-24 23:26:58 +01:00
|
|
|
}
|
2009-12-24 21:40:03 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
QModelIndex Playlist::InsertPaths(QList<QUrl> urls, int pos) {
|
2009-12-24 23:26:58 +01:00
|
|
|
SongList songs;
|
2009-12-24 23:44:12 +01:00
|
|
|
for (int i=0 ; i<urls.count() ; ++i) {
|
|
|
|
QUrl url(urls[i]);
|
2009-12-24 23:26:58 +01:00
|
|
|
if (url.scheme() != "file")
|
|
|
|
continue;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2009-12-24 23:26:58 +01:00
|
|
|
QString filename(url.toLocalFile());
|
|
|
|
QFileInfo info(filename);
|
2009-12-24 21:40:03 +01:00
|
|
|
|
2009-12-24 23:26:58 +01:00
|
|
|
if (!info.exists())
|
|
|
|
continue;
|
2009-12-24 21:40:03 +01:00
|
|
|
|
2009-12-24 23:26:58 +01:00
|
|
|
if (info.isDir()) {
|
|
|
|
// Add all the songs in the directory
|
|
|
|
QDirIterator it(filename,
|
|
|
|
QDir::Files | QDir::NoDotAndDotDot | QDir::Readable,
|
|
|
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
|
|
|
2009-12-24 23:44:12 +01:00
|
|
|
QList<QUrl> new_urls;
|
2009-12-24 23:26:58 +01:00
|
|
|
while (it.hasNext()) {
|
|
|
|
QString path(it.next());
|
2009-12-24 23:44:12 +01:00
|
|
|
new_urls << QUrl::fromLocalFile(path);
|
2009-12-24 21:40:03 +01:00
|
|
|
}
|
2009-12-24 23:44:12 +01:00
|
|
|
qSort(new_urls);
|
|
|
|
urls << new_urls;
|
2009-12-24 23:26:58 +01:00
|
|
|
} else {
|
|
|
|
Song song;
|
|
|
|
song.InitFromFile(filename, -1);
|
|
|
|
if (!song.is_valid())
|
|
|
|
continue;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2009-12-24 23:26:58 +01:00
|
|
|
songs << song;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
return InsertSongs(songs, pos);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
QModelIndex Playlist::InsertItems(const PlaylistItemList& items, int pos) {
|
2009-12-24 20:16:07 +01:00
|
|
|
if (items.isEmpty())
|
|
|
|
return QModelIndex();
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
const int start = pos == -1 ? items_.count() : pos;
|
|
|
|
undo_stack_->push(new PlaylistUndoCommands::InsertItems(this, items, pos));
|
|
|
|
|
|
|
|
return index(start, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items,
|
|
|
|
int pos) {
|
|
|
|
if (items.isEmpty())
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
const int start = pos == -1 ? items_.count() : pos;
|
2009-12-24 20:16:07 +01:00
|
|
|
const int end = start + items.count() - 1;
|
|
|
|
|
|
|
|
beginInsertRows(QModelIndex(), start, end);
|
|
|
|
for (int i=start ; i<=end ; ++i) {
|
2010-04-20 21:08:12 +02:00
|
|
|
boost::shared_ptr<PlaylistItem> item = items[i - start];
|
|
|
|
items_.insert(i, item);
|
2010-03-08 18:55:40 +01:00
|
|
|
virtual_items_ << virtual_items_.count();
|
2010-04-20 21:08:12 +02:00
|
|
|
|
|
|
|
if (item == current_item_) {
|
|
|
|
// It's one we removed before that got re-added through an undo
|
|
|
|
current_item_index_ = index(i, 0);
|
|
|
|
last_played_item_index_ = current_item_index_;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
endInsertRows();
|
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
Save();
|
2010-03-08 18:55:40 +01:00
|
|
|
ReshuffleIndices();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
return index(start, 0);
|
|
|
|
}
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
QModelIndex Playlist::InsertLibraryItems(const SongList& songs, int pos) {
|
2010-04-15 00:05:41 +02:00
|
|
|
PlaylistItemList items;
|
2010-04-14 23:03:00 +02:00
|
|
|
foreach (const Song& song, songs) {
|
2010-04-15 00:36:28 +02:00
|
|
|
items << shared_ptr<PlaylistItem>(new LibraryPlaylistItem(song));
|
2010-04-14 23:03:00 +02:00
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
return InsertItems(items, pos);
|
2010-04-14 23:03:00 +02:00
|
|
|
}
|
|
|
|
|
2010-05-10 16:19:43 +02:00
|
|
|
QModelIndex Playlist::InsertMagnatuneItems(const SongList& songs, int pos) {
|
|
|
|
PlaylistItemList items;
|
|
|
|
foreach (const Song& song, songs) {
|
|
|
|
items << shared_ptr<PlaylistItem>(new MagnatunePlaylistItem(song));
|
|
|
|
}
|
|
|
|
return InsertItems(items, pos);
|
|
|
|
}
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
QModelIndex Playlist::InsertSongs(const SongList& songs, int pos) {
|
2010-04-15 00:05:41 +02:00
|
|
|
PlaylistItemList items;
|
2009-12-24 20:16:07 +01:00
|
|
|
foreach (const Song& song, songs) {
|
2010-04-15 00:36:28 +02:00
|
|
|
items << shared_ptr<PlaylistItem>(new SongPlaylistItem(song));
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
return InsertItems(items, pos);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
QModelIndex Playlist::InsertRadioStations(const QList<RadioItem*>& items, int pos) {
|
2010-04-15 00:05:41 +02:00
|
|
|
PlaylistItemList playlist_items;
|
2009-12-30 00:17:54 +01:00
|
|
|
foreach (RadioItem* item, items) {
|
|
|
|
if (!item->playable)
|
|
|
|
continue;
|
2009-12-26 22:35:45 +01:00
|
|
|
|
2010-04-15 00:36:28 +02:00
|
|
|
playlist_items << shared_ptr<PlaylistItem>(
|
2010-04-15 00:05:41 +02:00
|
|
|
new RadioPlaylistItem(item->service, item->Url(), item->Title(), item->Artist()));
|
2009-12-26 22:35:45 +01:00
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
return InsertItems(playlist_items, pos);
|
2009-12-26 22:35:45 +01:00
|
|
|
}
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
QModelIndex Playlist::InsertStreamUrls(const QList<QUrl>& urls, int pos) {
|
2010-04-15 00:05:41 +02:00
|
|
|
PlaylistItemList playlist_items;
|
2010-02-24 23:26:01 +01:00
|
|
|
foreach (const QUrl& url, urls) {
|
2010-04-15 00:36:28 +02:00
|
|
|
playlist_items << shared_ptr<PlaylistItem>(new RadioPlaylistItem(
|
2010-04-15 00:05:41 +02:00
|
|
|
RadioModel::ServiceByName(SavedRadio::kServiceName), url.toString(), url.toString(), QString()));
|
2010-02-24 23:26:01 +01:00
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
return InsertItems(playlist_items, pos);
|
2010-02-24 23:26:01 +01:00
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
QMimeData* Playlist::mimeData(const QModelIndexList& indexes) const {
|
|
|
|
QMimeData* data = new QMimeData;
|
|
|
|
|
|
|
|
QList<QUrl> urls;
|
|
|
|
QList<int> rows;
|
|
|
|
foreach (const QModelIndex& index, indexes) {
|
|
|
|
if (index.column() != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
urls << items_[index.row()]->Url();
|
|
|
|
rows << index.row();
|
|
|
|
}
|
|
|
|
|
|
|
|
QBuffer buf;
|
|
|
|
buf.open(QIODevice::WriteOnly);
|
|
|
|
QDataStream stream(&buf);
|
|
|
|
stream << rows;
|
|
|
|
buf.close();
|
|
|
|
|
|
|
|
data->setUrls(urls);
|
|
|
|
data->setData(kRowsMimetype, buf.data());
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::CompareItems(int column, Qt::SortOrder order,
|
2010-04-15 00:36:28 +02:00
|
|
|
shared_ptr<PlaylistItem> _a,
|
|
|
|
shared_ptr<PlaylistItem> _b) {
|
|
|
|
shared_ptr<PlaylistItem> a = order == Qt::AscendingOrder ? _a : _b;
|
|
|
|
shared_ptr<PlaylistItem> b = order == Qt::AscendingOrder ? _b : _a;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-03-23 15:24:48 +01:00
|
|
|
#define cmp(field) return a->Metadata().field() < b->Metadata().field()
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
switch (column) {
|
2010-03-23 15:24:48 +01:00
|
|
|
case Column_Title: cmp(title);
|
|
|
|
case Column_Artist: cmp(artist);
|
|
|
|
case Column_Album: cmp(album);
|
|
|
|
case Column_Length: cmp(length);
|
|
|
|
case Column_Track: cmp(track);
|
|
|
|
case Column_Disc: cmp(disc);
|
|
|
|
case Column_Year: cmp(year);
|
|
|
|
case Column_Genre: cmp(genre);
|
|
|
|
case Column_AlbumArtist: cmp(albumartist);
|
|
|
|
case Column_Composer: cmp(composer);
|
|
|
|
|
|
|
|
case Column_BPM: cmp(bpm);
|
|
|
|
case Column_Bitrate: cmp(bitrate);
|
|
|
|
case Column_Samplerate: cmp(samplerate);
|
|
|
|
case Column_Filename: cmp(filename);
|
|
|
|
case Column_BaseFilename: cmp(basefilename);
|
|
|
|
case Column_Filesize: cmp(filesize);
|
|
|
|
case Column_Filetype: cmp(filetype);
|
|
|
|
case Column_DateModified: cmp(mtime);
|
|
|
|
case Column_DateCreated: cmp(ctime);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-03-23 15:24:48 +01:00
|
|
|
|
|
|
|
#undef cmp
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-03-24 01:12:52 +01:00
|
|
|
QString Playlist::column_name(Column column) {
|
|
|
|
switch (column) {
|
|
|
|
case Column_Title: return tr("Title");
|
|
|
|
case Column_Artist: return tr("Artist");
|
|
|
|
case Column_Album: return tr("Album");
|
|
|
|
case Column_Length: return tr("Length");
|
|
|
|
case Column_Track: return tr("Track");
|
|
|
|
case Column_Disc: return tr("Disc");
|
|
|
|
case Column_Year: return tr("Year");
|
|
|
|
case Column_Genre: return tr("Genre");
|
|
|
|
case Column_AlbumArtist: return tr("Album artist");
|
|
|
|
case Column_Composer: return tr("Composer");
|
|
|
|
|
|
|
|
case Column_BPM: return tr("BPM");
|
|
|
|
case Column_Bitrate: return tr("Bit rate");
|
|
|
|
case Column_Samplerate: return tr("Sample rate");
|
|
|
|
case Column_Filename: return tr("File name");
|
|
|
|
case Column_BaseFilename: return tr("File name (without path)");
|
|
|
|
case Column_Filesize: return tr("File size");
|
|
|
|
case Column_Filetype: return tr("File type");
|
|
|
|
case Column_DateModified: return tr("Date modified");
|
|
|
|
case Column_DateCreated: return tr("Date created");
|
2010-03-24 13:07:37 +01:00
|
|
|
default: return QString();
|
2010-03-24 01:12:52 +01:00
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void Playlist::sort(int column, Qt::SortOrder order) {
|
2009-12-26 16:21:36 +01:00
|
|
|
if (ignore_sorting_)
|
2009-12-24 20:16:07 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
layoutAboutToBeChanged();
|
|
|
|
|
|
|
|
// This is a slow and nasty way to keep the persistent indices
|
2010-04-15 00:36:28 +02:00
|
|
|
QMap<int, shared_ptr<PlaylistItem> > old_persistent_mappings;
|
2009-12-24 20:16:07 +01:00
|
|
|
foreach (const QModelIndex& index, persistentIndexList()) {
|
|
|
|
old_persistent_mappings[index.row()] = items_[index.row()];
|
|
|
|
}
|
|
|
|
|
|
|
|
qStableSort(items_.begin(), items_.end(),
|
|
|
|
boost::bind(&Playlist::CompareItems, column, order, _1, _2));
|
|
|
|
|
2010-04-15 00:36:28 +02:00
|
|
|
QMapIterator<int, shared_ptr<PlaylistItem> > it(old_persistent_mappings);
|
2009-12-24 20:16:07 +01:00
|
|
|
while (it.hasNext()) {
|
|
|
|
it.next();
|
|
|
|
for (int col=0 ; col<ColumnCount ; ++col) {
|
|
|
|
int new_row = items_.indexOf(it.value());
|
|
|
|
changePersistentIndex(index(it.key(), col, QModelIndex()),
|
|
|
|
index(new_row, col, QModelIndex()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
layoutChanged();
|
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
// TODO
|
|
|
|
undo_stack_->clear();
|
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
Save();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::Playing() {
|
|
|
|
SetCurrentIsPaused(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::Paused() {
|
|
|
|
SetCurrentIsPaused(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::Stopped() {
|
|
|
|
SetCurrentIsPaused(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::SetCurrentIsPaused(bool paused) {
|
|
|
|
if (paused == current_is_paused_)
|
|
|
|
return;
|
|
|
|
|
|
|
|
current_is_paused_ = paused;
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
if (current_item_index_.isValid())
|
|
|
|
dataChanged(index(current_item_index_.row(), 0),
|
2010-05-22 18:36:13 +02:00
|
|
|
index(current_item_index_.row(), ColumnCount-1));
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
void Playlist::Save() const {
|
2010-04-14 23:03:00 +02:00
|
|
|
if (!backend_)
|
|
|
|
return;
|
|
|
|
|
2010-05-21 00:30:55 +02:00
|
|
|
backend_->SavePlaylistAsync(id_, items_, last_played_index());
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
void Playlist::Restore() {
|
2010-04-14 23:03:00 +02:00
|
|
|
if (!backend_)
|
|
|
|
return;
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
items_.clear();
|
2010-03-08 18:55:40 +01:00
|
|
|
virtual_items_.clear();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-05-20 23:21:55 +02:00
|
|
|
items_ = backend_->GetPlaylistItems(id_);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-05-21 00:30:55 +02:00
|
|
|
PlaylistBackend::Playlist p = backend_->GetPlaylist(id_);
|
|
|
|
last_played_item_index_ =
|
|
|
|
p.last_played == -1 ? QModelIndex() : index(p.last_played);
|
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
for (int i=0 ; i<items_.count() ; ++i) {
|
|
|
|
virtual_items_ << i;
|
|
|
|
};
|
2010-03-10 01:04:04 +01:00
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::removeRows(int row, int count, const QModelIndex& parent) {
|
2010-03-24 21:58:17 +01:00
|
|
|
if (row < 0 || row >= items_.size() || row + count > items_.size()) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-04-19 20:44:35 +02:00
|
|
|
|
|
|
|
undo_stack_->push(new PlaylistUndoCommands::RemoveItems(this, row, count));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaylistItemList Playlist::RemoveItemsWithoutUndo(int row, int count) {
|
|
|
|
if (row < 0 || row >= items_.size() || row + count > items_.size()) {
|
|
|
|
return PlaylistItemList();
|
|
|
|
}
|
|
|
|
beginRemoveRows(QModelIndex(), row, row+count-1);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
// Remove items
|
2010-04-19 20:44:35 +02:00
|
|
|
PlaylistItemList ret;
|
2009-12-24 20:16:07 +01:00
|
|
|
for (int i=0 ; i<count ; ++i)
|
2010-04-19 20:44:35 +02:00
|
|
|
ret << items_.takeAt(row);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
endRemoveRows();
|
|
|
|
|
2010-03-08 18:55:40 +01:00
|
|
|
QList<int>::iterator it = virtual_items_.begin();
|
|
|
|
int i = 0;
|
|
|
|
while (it != virtual_items_.end()) {
|
2010-03-24 15:37:10 +01:00
|
|
|
if (*it >= items_.count())
|
2010-03-08 18:55:40 +01:00
|
|
|
it = virtual_items_.erase(it);
|
2010-03-24 15:37:10 +01:00
|
|
|
else
|
2010-03-08 18:55:40 +01:00
|
|
|
++it;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
2010-03-24 15:37:10 +01:00
|
|
|
// Reset current_virtual_index_
|
|
|
|
if (current_index() == -1)
|
|
|
|
current_virtual_index_ = -1;
|
|
|
|
else
|
|
|
|
current_virtual_index_ = virtual_items_.indexOf(current_index());
|
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
Save();
|
2010-04-19 20:44:35 +02:00
|
|
|
return ret;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::StopAfter(int row) {
|
|
|
|
QModelIndex old_stop_after = stop_after_;
|
|
|
|
|
|
|
|
if ((stop_after_.isValid() && stop_after_.row() == row) || row == -1)
|
|
|
|
stop_after_ = QModelIndex();
|
|
|
|
else
|
|
|
|
stop_after_ = index(row, 0);
|
|
|
|
|
|
|
|
if (old_stop_after.isValid())
|
2010-05-22 18:36:13 +02:00
|
|
|
emit dataChanged(old_stop_after, old_stop_after.sibling(old_stop_after.row(), ColumnCount-1));
|
2009-12-24 20:16:07 +01:00
|
|
|
if (stop_after_.isValid())
|
2010-05-22 18:36:13 +02:00
|
|
|
emit dataChanged(stop_after_, stop_after_.sibling(stop_after_.row(), ColumnCount-1));
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2009-12-26 23:15:57 +01:00
|
|
|
|
|
|
|
void Playlist::SetStreamMetadata(const QUrl& url, const Song& song) {
|
2010-04-20 21:08:12 +02:00
|
|
|
if (!current_item_)
|
2009-12-26 23:15:57 +01:00
|
|
|
return;
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
if (current_item_->Url() != url)
|
2009-12-26 23:15:57 +01:00
|
|
|
return;
|
|
|
|
|
2010-02-27 17:57:43 +01:00
|
|
|
// Don't update the metadata if it's only a minor change from before
|
2010-04-20 21:08:12 +02:00
|
|
|
if (current_item_->Metadata().artist() == song.artist() &&
|
|
|
|
current_item_->Metadata().title() == song.title())
|
2010-02-27 17:57:43 +01:00
|
|
|
return;
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
current_item_->SetTemporaryMetadata(song);
|
2009-12-29 20:57:33 +01:00
|
|
|
UpdateScrobblePoint();
|
2009-12-26 23:15:57 +01:00
|
|
|
|
2010-05-22 18:36:13 +02:00
|
|
|
emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount-1));
|
2010-01-08 15:52:05 +01:00
|
|
|
emit CurrentSongChanged(song);
|
2009-12-26 23:15:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::ClearStreamMetadata() {
|
2010-04-20 21:08:12 +02:00
|
|
|
if (!current_item_)
|
2009-12-26 23:15:57 +01:00
|
|
|
return;
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
current_item_->ClearTemporaryMetadata();
|
2009-12-29 20:57:33 +01:00
|
|
|
UpdateScrobblePoint();
|
2009-12-26 23:15:57 +01:00
|
|
|
|
2010-05-22 18:36:13 +02:00
|
|
|
emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount-1));
|
2009-12-26 23:15:57 +01:00
|
|
|
}
|
2009-12-27 00:43:38 +01:00
|
|
|
|
|
|
|
bool Playlist::stop_after_current() const {
|
2010-04-20 21:08:12 +02:00
|
|
|
return stop_after_.isValid() && current_item_index_.isValid() &&
|
|
|
|
stop_after_.row() == current_item_index_.row();
|
2009-12-29 20:57:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PlaylistItem::Options Playlist::current_item_options() const {
|
2010-04-20 21:08:12 +02:00
|
|
|
if (!current_item_)
|
2009-12-29 17:15:21 +01:00
|
|
|
return PlaylistItem::Default;
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
return current_item_->options();
|
2009-12-29 17:15:21 +01:00
|
|
|
}
|
2009-12-29 20:57:33 +01:00
|
|
|
|
|
|
|
Song Playlist::current_item_metadata() const {
|
2010-04-20 21:08:12 +02:00
|
|
|
if (!current_item_)
|
2009-12-29 20:57:33 +01:00
|
|
|
return Song();
|
|
|
|
|
2010-04-20 21:08:12 +02:00
|
|
|
return current_item_->Metadata();
|
2009-12-29 20:57:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::UpdateScrobblePoint() {
|
2010-03-06 21:08:01 +01:00
|
|
|
int length = qMin(current_item_metadata().length(), 240);
|
2009-12-29 20:57:33 +01:00
|
|
|
|
|
|
|
ScrobblePoint point(length / 2);
|
|
|
|
scrobble_point_ = point;
|
|
|
|
has_scrobbled_ = false;
|
|
|
|
}
|
2010-01-14 13:27:50 +01:00
|
|
|
|
|
|
|
void Playlist::Clear() {
|
2010-04-19 20:44:35 +02:00
|
|
|
undo_stack_->push(new PlaylistUndoCommands::RemoveItems(this, 0, items_.count()));
|
2010-03-24 15:37:10 +01:00
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
Save();
|
2010-01-14 13:27:50 +01:00
|
|
|
}
|
2010-01-16 17:12:47 +01:00
|
|
|
|
|
|
|
void Playlist::ReloadItems(const QList<int>& rows) {
|
|
|
|
foreach (int row, rows) {
|
|
|
|
item_at(row)->Reload();
|
|
|
|
emit dataChanged(index(row, 0), index(row, ColumnCount-1));
|
|
|
|
}
|
|
|
|
}
|
2010-02-04 00:56:41 +01:00
|
|
|
|
|
|
|
void Playlist::Shuffle() {
|
|
|
|
layoutAboutToBeChanged();
|
|
|
|
|
|
|
|
const int count = items_.count();
|
|
|
|
for (int i=0 ; i < count; ++i) {
|
|
|
|
int new_pos = i + (rand() % (count - i));
|
|
|
|
|
|
|
|
std::swap(items_[i], items_[new_pos]);
|
|
|
|
|
|
|
|
foreach (const QModelIndex& pidx, persistentIndexList()) {
|
|
|
|
if (pidx.row() == i)
|
|
|
|
changePersistentIndex(pidx, index(new_pos, pidx.column(), QModelIndex()));
|
|
|
|
else if (pidx.row() == new_pos)
|
|
|
|
changePersistentIndex(pidx, index(i, pidx.column(), QModelIndex()));
|
|
|
|
}
|
|
|
|
}
|
2010-04-26 13:36:10 +02:00
|
|
|
current_virtual_index_ = virtual_items_.indexOf(current_index());
|
2010-02-04 00:56:41 +01:00
|
|
|
|
|
|
|
layoutChanged();
|
2010-02-04 01:09:59 +01:00
|
|
|
|
2010-04-19 20:44:35 +02:00
|
|
|
// TODO
|
|
|
|
undo_stack_->clear();
|
|
|
|
|
2010-03-10 01:04:04 +01:00
|
|
|
Save();
|
2010-02-04 00:56:41 +01:00
|
|
|
}
|
2010-03-08 18:55:40 +01:00
|
|
|
|
|
|
|
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<int>::iterator begin = virtual_items_.begin();
|
|
|
|
if (current_virtual_index_ != -1)
|
|
|
|
std::advance(begin, current_virtual_index_ + 1);
|
|
|
|
|
|
|
|
std::random_shuffle(begin, virtual_items_.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-08 19:05:41 +01:00
|
|
|
void Playlist::set_sequence(PlaylistSequence* v) {
|
|
|
|
playlist_sequence_ = v;
|
|
|
|
connect(v, SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
|
|
|
|
SLOT(ShuffleModeChanged(PlaylistSequence::ShuffleMode)));
|
|
|
|
|
|
|
|
ShuffleModeChanged(v->shuffle_mode());
|
2010-03-08 18:55:40 +01:00
|
|
|
}
|
2010-05-22 18:36:13 +02:00
|
|
|
|
|
|
|
QSortFilterProxyModel* Playlist::proxy() const {
|
|
|
|
return proxy_;
|
|
|
|
}
|
2010-05-23 00:20:00 +02:00
|
|
|
|
|
|
|
SongList Playlist::GetAllSongs() const {
|
|
|
|
SongList ret;
|
|
|
|
foreach (boost::shared_ptr<PlaylistItem> item, items_) {
|
|
|
|
ret << item->Metadata();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|