Includes, comments and bugfixes

- Fix includes
- Use common regex (Song::kCoverRemoveDisc) for removing Disc/CD from album
- Remove Disc/CD from album when creating hash
- Make imobiledevice support compile
- Fix setting device on windows
This commit is contained in:
Jonas Kvinge
2018-05-01 00:41:33 +02:00
parent fccbd6790c
commit e337b7933b
518 changed files with 7003 additions and 4693 deletions

View File

@@ -20,45 +20,69 @@
#include "config.h"
#include <stdlib.h>
#include <memory>
#include <utility>
#include <algorithm>
#include <functional>
#include <memory>
#include <iterator>
#include <type_traits>
#include <unordered_map>
#include <stdbool.h>
#include <QApplication>
#include <QBuffer>
#include <QtGlobal>
#include <QObject>
#include <QCoreApplication>
#include <QDirIterator>
#include <QFileInfo>
#include <QLinkedList>
#include <QMimeData>
#include <QMutableListIterator>
#include <QSortFilterProxyModel>
#include <QUndoStack>
#include <QtAlgorithms>
#include <QtConcurrentRun>
#include <QtDebug>
#include <QFuture>
#include <QIODevice>
#include <QDataStream>
#include <QBuffer>
#include <QFile>
#include <QList>
#include <QMap>
#include <QSet>
#include <QMimeData>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QColor>
#include <QFont>
#include <QBrush>
#include <QLinkedList>
#include <QUndoStack>
#include <QUndoCommand>
#include <QModelIndex>
#include <QAbstractListModel>
#include <QPersistentModelIndex>
#include <QMutableListIterator>
#include <QMutableLinkedListIterator>
#include <QFlags>
#include <QSettings>
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/mimedata.h"
#include "core/tagreaderclient.h"
#include "collection/collection.h"
#include "collection/collectionbackend.h"
#include "collection/collectionplaylistitem.h"
#include "playlist.h"
#include "playlistitem.h"
#include "playlistview.h"
#include "playlistsequence.h"
#include "playlistbackend.h"
#include "playlistfilter.h"
#include "playlistitemmimedata.h"
#include "playlistundocommands.h"
#include "playlistview.h"
#include "queue.h"
#include "songloaderinserter.h"
#include "songmimedata.h"
#include "songplaylistitem.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
//#include "core/qhash_qurl.h"
#include "core/tagreaderclient.h"
#include "core/timeconstants.h"
#include "collection/collection.h"
#include "collection/collectionbackend.h"
#include "collection/collectionmodel.h"
#include "collection/collectionplaylistitem.h"
#include "tagreadermessages.pb.h"
using std::placeholders::_1;
using std::placeholders::_2;
@@ -292,8 +316,7 @@ QVariant Playlist::data(const QModelIndex &index, int role) const {
case Qt::ForegroundRole:
if (data(index, Role_IsCurrent).toBool()) {
// Ignore any custom colours for the currently playing item - they might
// clash with the glowing current track indicator.
// Ignore any custom colours for the currently playing item - they might clash with the glowing current track indicator.
return QVariant();
}
@@ -307,8 +330,7 @@ QVariant Playlist::data(const QModelIndex &index, int role) const {
case Qt::BackgroundRole:
if (data(index, Role_IsCurrent).toBool()) {
// Ignore any custom colours for the currently playing item - they might
// clash with the glowing current track indicator.
// Ignore any custom colours for the currently playing item - they might clash with the glowing current track indicator.
return QVariant();
}
@@ -342,7 +364,7 @@ bool Playlist::setData(const QModelIndex &index, const QVariant &value, int role
if (!set_column_value(song, (Column)index.column(), value)) return false;
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile( song.url().toLocalFile(), song);
NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(SongSaveComplete(TagReaderReply*,QPersistentModelIndex)), reply, QPersistentModelIndex(index));
NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(SongSaveComplete(TagReaderReply*, QPersistentModelIndex)), reply, QPersistentModelIndex(index));
return true;
@@ -409,13 +431,11 @@ int Playlist::NextVirtualIndex(int i, bool ignore_repeat_track) const {
return i;
}
// If we're not bothered about whether a song is on the same album then
// return the next virtual index, whatever it is.
// If we're not bothered about whether a song is on the same album then return the next virtual index, whatever it is.
if (!album_only) {
++i;
// Advance i until we find any track that is in the filter, skipping
// the selected to be skipped
// Advance i until we find any track that is in the filter, skipping the selected to be skipped
while (i < virtual_items_.count() && (!FilterContainsVirtualIndex(i) || item_at(virtual_items_[i])->GetShouldSkip())) {
++i;
}
@@ -454,8 +474,7 @@ int Playlist::PreviousVirtualIndex(int i, bool ignore_repeat_track) const {
return i;
}
// If we're not bothered about whether a song is on the same album then
// return the previous virtual index, whatever it is.
// If we're not bothered about whether a song is on the same album then return the previous virtual index, whatever it is.
if (!album_only) {
--i;
@@ -563,8 +582,7 @@ void Playlist::set_current_row(int i, bool is_stopping) {
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
// 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
@@ -629,8 +647,7 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro
if (const SongMimeData *song_data = qobject_cast<const SongMimeData*>(data)) {
// Dragged from a collection
// We want to check if these songs are from the actual local file backend,
// if they are we treat them differently.
// 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 && song_data->backend->songs_table() == Collection::kSongsTable)
InsertSongItems<CollectionPlaylistItem>(song_data->songs, row, play_now, enqueue_now);
else
@@ -672,8 +689,7 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro
for (int row : source_rows) items << source_playlist->item_at(row);
if (items.count() > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it
// might have been invalidated.
// Too big to keep in the undo stack. Also clear the stack because it might have been invalidated.
InsertItemsWithoutUndo(items, row, false);
undo_stack_->clear();
}
@@ -725,8 +741,7 @@ void Playlist::MoveItemsWithoutUndo(const QList<int> &source_rows, int pos) {
pos = items_.count();
}
// Take the items out of the list first, keeping track of whether the
// insertion point changes
// Take the items out of the list first, keeping track of whether the insertion point changes
int offset = 0;
int start = pos;
for (int source_row : source_rows) {
@@ -749,7 +764,8 @@ void Playlist::MoveItemsWithoutUndo(const QList<int> &source_rows, int pos) {
if (dest_offset != -1) {
// This index was moved
changePersistentIndex(pidx, index(start + dest_offset, pidx.column(), QModelIndex()));
} else {
}
else {
int d = 0;
for (int source_row : source_rows) {
if (pidx.row() > source_row) d--;
@@ -763,11 +779,11 @@ void Playlist::MoveItemsWithoutUndo(const QList<int> &source_rows, int pos) {
layoutChanged();
Save();
}
void Playlist::MoveItemsWithoutUndo(int start, const QList<int> &dest_rows) {
layoutAboutToBeChanged();
PlaylistItemList moved_items;
@@ -833,8 +849,7 @@ void Playlist::InsertItems(const PlaylistItemList &itemsIn, int pos, bool play_n
const int song_count = songs.length();
QSet<Song> vetoed;
for (SongInsertVetoListener *listener : veto_listeners_) {
for (const Song &song :
listener->AboutToInsertSongs(GetAllSongs(), songs)) {
for (const Song &song : listener->AboutToInsertSongs(GetAllSongs(), songs)) {
// avoid veto-ing a song multiple times
vetoed.insert(song);
}
@@ -865,8 +880,7 @@ void Playlist::InsertItems(const PlaylistItemList &itemsIn, int pos, bool play_n
const int start = pos == -1 ? items_.count() : pos;
if (items.count() > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it
// might have been invalidated.
// Too big to keep in the undo stack. Also clear the stack because it might have been invalidated.
InsertItemsWithoutUndo(items, pos, enqueue);
undo_stack_->clear();
} else {
@@ -943,12 +957,10 @@ void Playlist::InsertSongsOrCollectionItems(const SongList &songs, int pos, bool
void Playlist::UpdateItems(const SongList &songs) {
qLog(Debug) << "Updating playlist with new tracks' info";
// We first convert our songs list into a linked list (a 'real' list),
// because removals are faster with QLinkedList.
// We first convert our songs list into a linked list (a 'real' list), because removals are faster with QLinkedList.
// Next, we walk through the list of playlist's items then the list of songs
// we want to update: if an item corresponds to the song (we rely on URL for
// this), we update the item with the new metadata, then we remove song from
// our list because we will not need to check it again.
// we want to update: if an item corresponds to the song (we rely on URL for this), we update the item with the new metadata,
// then we remove song from our list because we will not need to check it again.
// And we also update undo actions.
QLinkedList<Song> songs_list;
for (const Song &song : songs) songs_list.append(song);
@@ -994,8 +1006,7 @@ QMimeData *Playlist::mimeData(const QModelIndexList &indexes) const {
if (indexes.isEmpty()) return nullptr;
// We only want one index per row, but we can't just take column 0 because
// the user might have hidden it.
// We only want one index per row, but we can't just take column 0 because the user might have hidden it.
const int first_column = indexes.first().column();
QMimeData *data = new QMimeData;
@@ -1164,8 +1175,7 @@ void Playlist::sort(int column, Qt::SortOrder order) {
qStableSort(begin, new_items.end(), std::bind(&Playlist::CompareItems, Column_Album, order, _1, _2));
}
else if (column == Column_Filename) {
// When sorting by full paths we also expect a hierarchical order. This
// returns a breath-first ordering of paths.
// When sorting by full paths we also expect a hierarchical order. This returns a breath-first ordering of paths.
qStableSort(begin, new_items.end(), std::bind(&Playlist::CompareItems, Column_Filename, order, _1, _2));
qStableSort(begin, new_items.end(), std::bind(&Playlist::ComparePathDepths, order, _1, _2));
}
@@ -1250,8 +1260,7 @@ void Playlist::ItemsLoaded(QFuture<PlaylistItemList> future) {
PlaylistItemList items = future.result();
// backend returns empty elements for collection items which it couldn't
// match (because they got deleted); we don't need those
// Backend returns empty elements for collection items which it couldn't match (because they got deleted); we don't need those
QMutableListIterator<PlaylistItemPtr> it(items);
while (it.hasNext()) {
PlaylistItemPtr item = it.next();
@@ -1267,8 +1276,7 @@ void Playlist::ItemsLoaded(QFuture<PlaylistItemList> future) {
PlaylistBackend::Playlist p = backend_->GetPlaylist(id_);
// the newly loaded list of items might be shorter than it was before so
// look out for a bad last_played index
// The newly loaded list of items might be shorter than it was before so look out for a bad last_played index
last_played_item_index_ = p.last_played == -1 || p.last_played >= rowCount() ? QModelIndex() : index(p.last_played);
emit RestoreFinished();
@@ -1276,7 +1284,7 @@ void Playlist::ItemsLoaded(QFuture<PlaylistItemList> future) {
QSettings s;
s.beginGroup(kSettingsGroup);
// should we gray out deleted songs asynchronously on startup?
// Should we gray out deleted songs asynchronously on startup?
if (s.value("greyoutdeleted", false).toBool()) {
QtConcurrent::run(this, &Playlist::InvalidateDeletedSongs);
}
@@ -1287,16 +1295,14 @@ static bool DescendingIntLessThan(int a, int b) { return a > b; }
void Playlist::RemoveItemsWithoutUndo(const QList<int> &indicesIn) {
// Sort the indices descending because removing elements 'backwards'
// is easier - indices don't 'move' in the process.
// Sort the indices descending because removing elements 'backwards' is easier - indices don't 'move' in the process.
QList<int> indices = indicesIn;
qSort(indices.begin(), indices.end(), DescendingIntLessThan);
for (int j = 0; j < indices.count(); j++) {
int beginning = indices[j], end = indices[j];
// Splits the indices into sequences. For example this: [1, 2, 4],
// will get split into [1, 2] and [4].
// Splits the indices into sequences. For example this: [1, 2, 4], will get split into [1, 2] and [4].
while (j != indices.count() - 1 && indices[j] == indices[j + 1] + 1) {
beginning--;
j++;
@@ -1315,8 +1321,7 @@ bool Playlist::removeRows(int row, int count, const QModelIndex &parent) {
}
if (count > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it
// might have been invalidated.
// Too big to keep in the undo stack. Also clear the stack because it might have been invalidated.
RemoveItemsWithoutUndo(row, count);
undo_stack_->clear();
}
@@ -1337,14 +1342,12 @@ bool Playlist::removeRows(QList<int> &rows) {
return false;
}
// start from the end to be sure that indices won't 'move' during
// the removal process
// Start from the end to be sure that indices won't 'move' during the removal process
qSort(rows.begin(), rows.end(), qGreater<int>());
QList<int> part;
while (!rows.isEmpty()) {
// we're splitting the input list into sequences of consecutive
// numbers
// we're splitting the input list into sequences of consecutive numbers
part.append(rows.takeFirst());
while (!rows.isEmpty() && rows.first() == part.last() - 1) {
part.append(rows.takeFirst());
@@ -1492,11 +1495,11 @@ void Playlist::Clear() {
const int count = items_.count();
if (count > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it
// might have been invalidated.
// Too big to keep in the undo stack. Also clear the stack because it might have been invalidated.
RemoveItemsWithoutUndo(0, count);
undo_stack_->clear();
} else {
}
else {
undo_stack_->push(new PlaylistUndoCommands::RemoveItems(this, 0, count));
}
@@ -1520,8 +1523,7 @@ void Playlist::RemoveItemsNotInQueue() {
start++;
}
// Figure out how many rows to remove - keep going until we find a row
// that is in the queue
// Figure out how many rows to remove - keep going until we find a row that is in the queue
int count = 1;
forever {
if (start + count >= rowCount()) break;
@@ -1608,8 +1610,7 @@ void Playlist::ReshuffleIndices() {
return;
}
// If the user is already playing a song, advance the begin iterator to
// only shuffle items that haven't been played yet.
// If the user is already playing a song, advance the begin iterator to only shuffle items that haven't been played yet.
QList<int>::iterator begin = virtual_items_.begin();
QList<int>::iterator end = virtual_items_.end();
if (current_virtual_index_ != -1)
@@ -1642,8 +1643,7 @@ void Playlist::ReshuffleIndices() {
std::random_shuffle(shuffled_album_keys.begin(), shuffled_album_keys.end());
// If the user is currently playing a song, force its album to be first
// Or if the song was not playing but it was selected, force its album
// to be first.
// Or if the song was not playing but it was selected, force its album to be first.
if (current_virtual_index_ != -1 || current_row() != -1) {
const QString key = items_[current_row()]->Metadata().AlbumKey();
const int pos = shuffled_album_keys.indexOf(key);
@@ -1741,8 +1741,7 @@ void Playlist::InformOfCurrentSongChange() {
emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount - 1));
// if the song is invalid, we won't play it - there's no point in
// informing anybody about the change
// if the song is invalid, we won't play it - there's no point in informing anybody about the change
const Song metadata(current_item_metadata());
if (metadata.is_valid()) {
emit CurrentSongChanged(metadata);
@@ -1845,7 +1844,7 @@ void Playlist::RemoveUnavailableSongs() {
PlaylistItemPtr item = items_[row];
const Song &song = item->Metadata();
// check only local files
// Check only local files
if (song.url().isLocalFile() && !QFile::exists(song.url().toLocalFile())) {
rows_to_remove.append(row);
}
@@ -1862,12 +1861,12 @@ bool Playlist::ApplyValidityOnCurrentSong(const QUrl &url, bool valid) {
if (current) {
Song current_song = current->Metadata();
// if validity has changed, reload the item
// If validity has changed, reload the item
if(!current_song.is_cdda() && current_song.url() == url && current_song.is_valid() != QFile::exists(current_song.url().toLocalFile())) {
ReloadItems(QList<int>() << current_row());
}
// gray out the song if it's now broken; otherwise undo the gray color
// Gray out the song if it's now broken; otherwise undo the gray color
if (valid) {
current->RemoveForegroundColor(kInvalidSongPriority);
} else {

View File

@@ -23,13 +23,31 @@
#include "config.h"
#include <QAbstractItemModel>
#include <QList>
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
#include <QFuture>
#include <QList>
#include <QMap>
#include <QMetaType>
#include <QMimeData>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QColor>
#include <QModelIndex>
#include <QAbstractItemModel>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <QPersistentModelIndex>
#include <QUndoStack>
#include "core/song.h"
#include "core/tagreaderclient.h"
#include "playlistitem.h"
#include "playlistsequence.h"
#include "core/tagreaderclient.h"
#include "core/song.h"
class CollectionBackend;
class PlaylistBackend;
@@ -37,33 +55,29 @@ class PlaylistFilter;
class Queue;
class TaskManager;
class QSortFilterProxyModel;
class QUndoStack;
namespace PlaylistUndoCommands {
class InsertItems;
class RemoveItems;
class MoveItems;
class ReOrderItems;
class SortItems;
class RemoveItems;
class ShuffleItems;
class SortItems;
}
typedef QMap<int, Qt::Alignment> ColumnAlignmentMap;
Q_DECLARE_METATYPE(Qt::Alignment);
Q_DECLARE_METATYPE(ColumnAlignmentMap);
// Objects that may prevent a song being added to the playlist. When there
// is something about to be inserted into it, Playlist notifies all of it's
// listeners about the fact and every one of them picks 'invalid' songs.
// Objects that may prevent a song being added to the playlist.
// When there is something about to be inserted into it,
// Playlist notifies all of it's listeners about the fact and every one of them picks 'invalid' songs.
class SongInsertVetoListener : public QObject {
Q_OBJECT
public:
// Listener returns a list of 'invalid' songs. 'old_songs' are songs that are
// currently in the playlist and 'new_songs' are the songs about to be added if
// nobody exercises a veto.
virtual SongList AboutToInsertSongs(const SongList& old_songs, const SongList& new_songs) = 0;
// Listener returns a list of 'invalid' songs.
// 'old_songs' are songs that are currently in the playlist and 'new_songs' are the songs about to be added if nobody exercises a veto.
virtual SongList AboutToInsertSongs(const SongList &old_songs, const SongList &new_songs) = 0;
};
class Playlist : public QAbstractListModel {
@@ -75,10 +89,10 @@ class Playlist : public QAbstractListModel {
friend class PlaylistUndoCommands::ReOrderItems;
public:
Playlist(PlaylistBackend *backend, TaskManager *task_manager, CollectionBackend *collection, int id, const QString& special_type = QString(), bool favorite = false, QObject *parent = nullptr);
Playlist(PlaylistBackend *backend, TaskManager *task_manager, CollectionBackend *collection, int id, const QString &special_type = QString(), bool favorite = false, QObject *parent = nullptr);
~Playlist();
void SkipTracks(const QModelIndexList& source_indexes);
void SkipTracks(const QModelIndexList &source_indexes);
// Always add new columns to the end of this enum - the values are persisted
enum Column {
@@ -123,8 +137,7 @@ class Playlist : public QAbstractListModel {
Path_Automatic = 0, // Automatically select path type
Path_Absolute, // Always use absolute paths
Path_Relative, // Always use relative paths
Path_Ask_User, // Only used in preferences: to ask user which of the
// previous values he wants to use.
Path_Ask_User, // Only used in preferences: to ask user which of the previous values he wants to use.
};
static const char *kCddaMimeType;
@@ -151,7 +164,7 @@ class Playlist : public QAbstractListModel {
static QString abbreviated_column_name(Column column);
static bool column_is_editable(Playlist::Column column);
static bool set_column_value(Song& song, Column column, const QVariant& value);
static bool set_column_value(Song &song, Column column, const QVariant &value);
// Persistence
void Save() const;
@@ -162,7 +175,7 @@ class Playlist : public QAbstractListModel {
Queue *queue() const { return queue_; }
int id() const { return id_; }
const QString& ui_path() const { return ui_path_; }
const QString &ui_path() const { return ui_path_; }
void set_ui_path(const QString &path) { ui_path_ = path; }
bool is_favorite() const { return favorite_; }
void set_favorite(bool favorite) { favorite_ = favorite; }
@@ -207,14 +220,12 @@ class Playlist : public QAbstractListModel {
void ReshuffleIndices();
// If this playlist contains the current item, this method will apply the "valid" flag on it.
// If the "valid" flag is false, the song will be greyed out. Otherwise the grey color will
// be undone.
// If the "valid" flag is false, the song will be greyed out. Otherwise the grey color will be undone.
// If the song is a local file and it's valid but non existent or invalid but exists, the
// song will be reloaded to even out the situation because obviously something has changed.
// This returns true if this playlist had current item when the method was invoked.
bool ApplyValidityOnCurrentSong(const QUrl &url, bool valid);
// Grays out and reloads all deleted songs in all playlists. Also, "ungreys" those songs
// which were once deleted but now got restored somehow.
// Grays out and reloads all deleted songs in all playlists. Also, "ungreys" those songs which were once deleted but now got restored somehow.
void InvalidateDeletedSongs();
// Removes from the playlist all local files that don't exist anymore.
void RemoveDeletedSongs();
@@ -223,8 +234,7 @@ class Playlist : public QAbstractListModel {
void ReloadItems(const QList<int> &rows);
void InformOfCurrentSongChange();
// Registers an object which will get notifications when new songs
// are about to be inserted into this playlist.
// Registers an object which will get notifications when new songs are about to be inserted into this playlist.
void AddSongInsertVetoListener(SongInsertVetoListener *listener);
// Unregisters a SongInsertVetoListener object.
void RemoveSongInsertVetoListener(SongInsertVetoListener *listener);
@@ -276,15 +286,13 @@ signals:
void EditingFinished(const QModelIndex &index);
void PlayRequested(const QModelIndex &index);
// Signals that the underlying list of items was changed, meaning that
// something was added to it, removed from it or the ordering changed.
// Signals that the underlying list of items was changed, meaning that something was added to it, removed from it or the ordering changed.
void PlaylistChanged();
void DynamicModeChanged(bool dynamic);
void Error(const QString &message);
// Signals that the queue has changed, meaning that the remaining queued
// items should update their position.
// Signals that the queue has changed, meaning that the remaining queued items should update their position.
void QueueChanged();
private:
@@ -296,8 +304,7 @@ private:
template <typename T>
void InsertSongItems(const SongList &songs, int pos, bool play_now, bool enqueue);
// Modify the playlist without changing the undo stack. These are used by
// our friends in PlaylistUndoCommands
// Modify the playlist without changing the undo stack. These are used by our friends in PlaylistUndoCommands
void InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bool enqueue = false);
PlaylistItemList RemoveItemsWithoutUndo(int pos, int count);
void MoveItemsWithoutUndo(const QList<int> &source_rows, int pos);
@@ -335,10 +342,9 @@ private:
bool favorite_;
PlaylistItemList items_;
QList<int> virtual_items_; // Contains the indices into items_ in the order
// that they will be played.
// A map of collection ID to playlist item - for fast lookups when collection
// items change.
// Contains the indices into items_ in the order that they will be played.
QList<int> virtual_items_;
// A map of collection ID to playlist item - for fast lookups when collection items change.
QMultiMap<int, PlaylistItemPtr> collection_items_by_id_;
QPersistentModelIndex current_item_index_;

View File

@@ -18,16 +18,23 @@
*
*/
#include "config.h"
#include "playlistbackend.h"
#include <memory>
#include <functional>
#include <QObject>
#include <QMutex>
#include <QIODevice>
#include <QDir>
#include <QFile>
#include <QByteArray>
#include <QHash>
#include <QMutexLocker>
#include <QList>
#include <QVariant>
#include <QString>
#include <QStringBuilder>
#include <QStringList>
#include <QUrl>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QtDebug>
@@ -38,7 +45,9 @@
#include "core/song.h"
#include "collection/collectionbackend.h"
#include "collection/sqlrow.h"
#include "playlist/songplaylistitem.h"
#include "playlistitem.h"
#include "songplaylistitem.h"
#include "playlistbackend.h"
#include "playlistparsers/cueparser.h"
using std::placeholders::_1;
@@ -46,7 +55,7 @@ using std::shared_ptr;
const int PlaylistBackend::kSongTableJoins = 2;
PlaylistBackend::PlaylistBackend(Application* app, QObject* parent)
PlaylistBackend::PlaylistBackend(Application *app, QObject *parent)
: QObject(parent), app_(app), db_(app_->database()) {}
PlaylistBackend::PlaylistList PlaylistBackend::GetAllPlaylists() {
@@ -63,8 +72,6 @@ PlaylistBackend::PlaylistList PlaylistBackend::GetAllFavoritePlaylists() {
PlaylistBackend::PlaylistList PlaylistBackend::GetPlaylists(GetPlaylistsFlags flags) {
//qLog(Debug) << __PRETTY_FUNCTION__;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@@ -157,12 +164,10 @@ QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) {
QList<PlaylistItemPtr> PlaylistBackend::GetPlaylistItems(int playlist) {
QSqlQuery q = GetPlaylistRows(playlist);
// Note that as this only accesses the query, not the db, we don't need the
// mutex.
// Note that as this only accesses the query, not the db, we don't need the mutex.
if (db_->CheckErrors(q)) return QList<PlaylistItemPtr>();
// it's probable that we'll have a few songs associated with the
// same CUE so we're caching results of parsing CUEs
// it's probable that we'll have a few songs associated with the same CUE so we're caching results of parsing CUEs
std::shared_ptr<NewSongFromQueryState> state_ptr(new NewSongFromQueryState());
QList<PlaylistItemPtr> playlistitems;
while (q.next()) {
@@ -175,12 +180,10 @@ QList<PlaylistItemPtr> PlaylistBackend::GetPlaylistItems(int playlist) {
QList<Song> PlaylistBackend::GetPlaylistSongs(int playlist) {
QSqlQuery q = GetPlaylistRows(playlist);
// Note that as this only accesses the query, not the db, we don't need the
// mutex.
// Note that as this only accesses the query, not the db, we don't need the mutex.
if (db_->CheckErrors(q)) return QList<Song>();
// it's probable that we'll have a few songs associated with the
// same CUE so we're caching results of parsing CUEs
// it's probable that we'll have a few songs associated with the same CUE so we're caching results of parsing CUEs
std::shared_ptr<NewSongFromQueryState> state_ptr(new NewSongFromQueryState());
QList<Song> songs;
while (q.next()) {
@@ -248,10 +251,8 @@ PlaylistItemPtr PlaylistBackend::RestoreCueData(PlaylistItemPtr item, std::share
}
for (const Song& from_list : song_list) {
if (from_list.url().toEncoded() == song.url().toEncoded() &&
from_list.beginning_nanosec() == song.beginning_nanosec()) {
// we found a matching section; replace the input
// item with a new one containing CUE metadata
if (from_list.url().toEncoded() == song.url().toEncoded() && from_list.beginning_nanosec() == song.beginning_nanosec()) {
// we found a matching section; replace the input item with a new one containing CUE metadata
return PlaylistItemPtr(new SongPlaylistItem(from_list));
}
}
@@ -268,7 +269,7 @@ void PlaylistBackend::SavePlaylistAsync(int playlist, const PlaylistItemList &it
}
void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList& items, int last_played) {
void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList &items, int last_played) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@@ -308,7 +309,7 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList& items,
}
int PlaylistBackend::CreatePlaylist(const QString& name, const QString& special_type) {
int PlaylistBackend::CreatePlaylist(const QString &name, const QString &special_type) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());

View File

@@ -23,11 +23,20 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QObject>
#include <QMutex>
#include <QHash>
#include <QList>
#include <QMutex>
#include <QObject>
#include <QSet>
#include <QString>
#include <QVector>
#include <QSqlQuery>
#include "core/song.h"
#include "collection/sqlrow.h"
#include "playlistitem.h"
class Application;

View File

@@ -18,25 +18,43 @@
*
*/
#include "config.h"
#include <QApplication>
#include <QObject>
#include <QWidget>
#include <QAction>
#include <QList>
#include <QVariant>
#include <QPoint>
#include <QString>
#include <QRegExp>
#include <QSize>
#include <QFont>
#include <QIcon>
#include <QColor>
#include <QFrame>
#include <QPalette>
#include <QModelIndex>
#include <QItemSelectionModel>
#include <QSortFilterProxyModel>
#include <QTimer>
#include <QTimeLine>
#include <QFileDialog>
#include <QLabel>
#include <QKeySequence>
#include <QToolButton>
#include <QUndoStack>
#include <QtEvents>
#include <QSettings>
#include "core/iconloader.h"
#include "playlist.h"
#include "playlisttabbar.h"
#include "playlistview.h"
#include "playlistcontainer.h"
#include "playlistmanager.h"
#include "ui_playlistcontainer.h"
#include "core/logging.h"
#include "core/iconloader.h"
#include "playlistparsers/playlistparser.h"
#include <QFileDialog>
#include <QInputDialog>
#include <QKeyEvent>
#include <QLabel>
#include <QMessageBox>
#include <QSettings>
#include <QSortFilterProxyModel>
#include <QTimeLine>
#include <QTimer>
#include <QUndoStack>
#include "ui_playlistcontainer.h"
#include "3rdparty/qocoa/qsearchfield.h"
const char *PlaylistContainer::kSettingsGroup = "Playlist";
const int PlaylistContainer::kFilterDelayMs = 100;
@@ -127,7 +145,7 @@ void PlaylistContainer::SetActions(QAction *new_playlist, QAction *load_playlist
}
void PlaylistContainer::SetManager(PlaylistManager *manager) {
manager_ = manager;
ui_->tab_bar->SetManager(manager);
@@ -149,7 +167,7 @@ void PlaylistContainer::SetManager(PlaylistManager *manager) {
void PlaylistContainer::SetViewModel(Playlist *playlist) {
if (view()->selectionModel()) {
disconnect(view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(SelectionChanged()));
disconnect(view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(SelectionChanged()));
}
if (playlist_ && playlist_->proxy()) {
disconnect(playlist_->proxy(), SIGNAL(modelReset()), this, SLOT(UpdateNoMatchesLabel()));
@@ -172,7 +190,7 @@ void PlaylistContainer::SetViewModel(Playlist *playlist) {
view()->selectionModel()->select(manager_->current_selection(), QItemSelectionModel::ClearAndSelect);
playlist->IgnoreSorting(false);
connect(view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(SelectionChanged()));
connect(view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(SelectionChanged()));
emit ViewSelectionModelChanged();
// Update filter
@@ -180,11 +198,11 @@ void PlaylistContainer::SetViewModel(Playlist *playlist) {
// Update the no matches label
connect(playlist_->proxy(), SIGNAL(modelReset()), SLOT(UpdateNoMatchesLabel()));
connect(playlist_->proxy(), SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_->proxy(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_->proxy(), SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_->proxy(), SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_, SIGNAL(modelReset()), SLOT(UpdateNoMatchesLabel()));
connect(playlist_, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(UpdateNoMatchesLabel()));
connect(playlist_, SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(UpdateNoMatchesLabel()));
UpdateNoMatchesLabel();
// Ensure that tab is current
@@ -241,13 +259,11 @@ void PlaylistContainer::PlaylistAdded(int id, const QString &name, bool favorite
}
if (ui_->tab_bar->count() > 1) {
// Have to do this here because sizeHint() is only valid when there's a
// tab in the bar.
// Have to do this here because sizeHint() is only valid when there's a tab in the bar.
tab_bar_animation_->setFrameRange(0, ui_->tab_bar->sizeHint().height());
if (!isVisible()) {
// Skip the animation since the window is hidden (eg. if we're still
// loading the UI).
// Skip the animation since the window is hidden (eg. if we're still loading the UI).
tab_bar_visible_ = true;
ui_->tab_bar->setMaximumHeight(tab_bar_animation_->endFrame());
} else {
@@ -271,8 +287,6 @@ void PlaylistContainer::PlaylistRenamed(int id, const QString &new_name) {
void PlaylistContainer::NewPlaylist() { manager_->New(tr("Playlist")); }
void PlaylistContainer::LoadPlaylist() {
//qLog(Debug) << __PRETTY_FUNCTION__;
QString filename = settings_.value("last_load_playlist").toString();
filename = QFileDialog::getOpenFileName(this, tr("Load playlist"), filename, manager_->parser()->filters());
@@ -296,8 +310,7 @@ void PlaylistContainer::ClearPlaylist() {
void PlaylistContainer::GoToNextPlaylistTab() {
// Get the next tab' id
int id_next = ui_->tab_bar->id_of((ui_->tab_bar->currentIndex() + 1) %
ui_->tab_bar->count());
int id_next = ui_->tab_bar->id_of((ui_->tab_bar->currentIndex() + 1) % ui_->tab_bar->count());
// Switch to next tab
manager_->SetCurrentPlaylist(id_next);
}
@@ -330,8 +343,7 @@ void PlaylistContainer::SetTabBarHeight(int height) {
void PlaylistContainer::MaybeUpdateFilter() {
// delaying the filter update on small playlists is undesirable
// and an empty filter applies very quickly, too
// delaying the filter update on small playlists is undesirable and an empty filter applies very quickly, too
if (manager_->current()->rowCount() < kFilterDelayPlaylistSizeThreshold || ui_->filter->text().isEmpty()) {
UpdateFilter();
}

View File

@@ -23,19 +23,28 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QString>
#include <QIcon>
#include <QTimer>
#include <QTimeLine>
#include <QSettings>
#include <QAction>
#include <QEvent>
#include <QLabel>
#include <QtEvents>
class Ui_PlaylistContainer;
class QKeyEvent;
class QResizeEvent;
class LineEditInterface;
class Playlist;
class PlaylistManager;
class PlaylistView;
class QTimeLine;
class QTimer;
class QLabel;
class Ui_PlaylistContainer;
class PlaylistContainer : public QWidget {
Q_OBJECT

View File

@@ -20,29 +20,55 @@
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QtConcurrentRun>
#include <QFuture>
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QCompleter>
#include <QDateTime>
#include <QDir>
#include <QFuture>
#include <QFont>
#include <QFontMetrics>
#include <QHeaderView>
#include <QHelpEvent>
#include <QLinearGradient>
#include <QLineEdit>
#include <QLocale>
#include <QMetaType>
#include <QVariant>
#include <QString>
#include <QStringBuilder>
#include <QUrl>
#include <QIcon>
#include <QPixmap>
#include <QPainter>
#include <QColor>
#include <QPen>
#include <QBrush>
#include <QPoint>
#include <QRect>
#include <QSize>
#include <QLineEdit>
#include <QScrollBar>
#include <QTextDocument>
#include <QToolTip>
#include <QTreeView>
#include <QWhatsThis>
#include <QtConcurrentRun>
#include <QStyledItemDelegate>
#include <QStyleOptionViewItem>
#include <QtDebug>
#include <QtEvents>
#include <QLinearGradient>
#include "playlistdelegates.h"
#include "queue.h"
#include "core/closure.h"
#include "core/iconloader.h"
#include "core/logging.h"
#include "core/player.h"
#include "core/song.h"
#include "core/urlhandler.h"
#include "core/utilities.h"
#include "core/iconloader.h"
#include "collection/collectionbackend.h"
#include "widgets/trackslider.h"
#include "playlist/playlist.h"
#include "playlistdelegates.h"
#ifdef Q_OS_DARWIN
#include "core/mac_utilities.h"
@@ -202,8 +228,7 @@ QStyleOptionViewItemV4 PlaylistDelegateBase::Adjusted(const QStyleOptionViewItem
QStyleOptionViewItemV4 ret(option);
if (index.data(Playlist::Role_IsCurrent).toBool()) {
// Move the text in a bit on the first column for the song that's currently
// playing
// Move the text in a bit on the first column for the song that's currently playing
ret.rect.setLeft(ret.rect.left() + 20);
}
@@ -213,9 +238,7 @@ QStyleOptionViewItemV4 PlaylistDelegateBase::Adjusted(const QStyleOptionViewItem
bool PlaylistDelegateBase::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) {
// This function is copied from QAbstractItemDelegate, and changed to show
// displayText() in the tooltip, rather than the index's naked
// Qt::ToolTipRole text.
// This function is copied from QAbstractItemDelegate, and changed to show displayText() in the tooltip, rather than the index's naked Qt::ToolTipRole text.
Q_UNUSED(option);
@@ -246,7 +269,8 @@ bool PlaylistDelegateBase::helpEvent(QHelpEvent *event, QAbstractItemView *view,
is_elided = displayed_text.width() < real_text.width();
if (is_elided) {
QToolTip::showText(he->globalPos(), text, view);
} else { // in case that another text was previously displayed
}
else { // in case that another text was previously displayed
QToolTip::hideText();
}
return true;

View File

@@ -23,16 +23,36 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QFuture>
#include <QCompleter>
#include <QLocale>
#include <QVariant>
#include <QUrl>
#include <QPixmap>
#include <QPixmapCache>
#include <QPainter>
#include <QRect>
#include <QColor>
#include <QSize>
#include <QFont>
#include <QString>
#include <QStringListModel>
#include <QModelIndex>
#include <QStyleOption>
#include <QStyledItemDelegate>
#include <QAbstractItemView>
#include <QStyleOptionViewItem>
#include <QHelpEvent>
#include <QLineEdit>
#include <QTreeView>
#include "playlist.h"
#include "collection/collection.h"
#include "widgets/ratingwidget.h"
class CollectionBackend;
class Player;
class QueuedItemDelegate : public QStyledItemDelegate {
@@ -142,8 +162,7 @@ class TagCompletionItemDelegate : public PlaylistDelegateBase {
public:
TagCompletionItemDelegate(QObject *parent, CollectionBackend *backend, Playlist::Column column) : PlaylistDelegateBase(parent), backend_(backend), column_(column) {};
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
CollectionBackend *backend_;

View File

@@ -20,11 +20,18 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QString>
#include <QRegExp>
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include "playlist/playlist.h"
#include "playlistfilter.h"
#include "playlistfilterparser.h"
#include <QtDebug>
PlaylistFilter::PlaylistFilter(QObject *parent)
: QSortFilterProxyModel(parent),
filter_tree_(new NopFilter),

View File

@@ -23,13 +23,18 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
#include <QMap>
#include <QSet>
#include <QScopedPointer>
#include <QString>
#include <QSortFilterProxyModel>
#include "playlist.h"
#include <QSet>
class FilterTree;
class PlaylistFilter : public QSortFilterProxyModel {

View File

@@ -20,12 +20,21 @@
#include "config.h"
#include "playlistfilterparser.h"
#include "playlist.h"
#include "core/logging.h"
#include <stdbool.h>
#include <QList>
#include <QMap>
#include <QSet>
#include <QChar>
#include <QScopedPointer>
#include <QVariant>
#include <QString>
#include <QtAlgorithms>
#include <QAbstractItemModel>
#include "playlist.h"
#include "playlistfilterparser.h"
class SearchTermComparator {
public:
virtual ~SearchTermComparator() {}
@@ -143,11 +152,9 @@ class LeComparator : public SearchTermComparator {
int search_term_;
};
// The length field of the playlist (entries) contains a
// song's running time in nano seconds. However, We don't
// really care about nano seconds, just seconds. Thus, with
// this decorator we drop the last 9 digits, if that many
// are present.
// The length field of the playlist (entries) contains a song's running time in nano seconds.
// However, We don't really care about nano seconds, just seconds.
// Thus, with this decorator we drop the last 9 digits, if that many are present.
class DropTailComparatorDecorator : public SearchTermComparator {
public:
explicit DropTailComparatorDecorator(SearchTermComparator *cmp) : cmp_(cmp) {}
@@ -178,8 +185,7 @@ class FilterTerm : public FilterTree {
public:
explicit FilterTerm(SearchTermComparator *comparator, const QList<int> &columns) : cmp_(comparator), columns_(columns) {}
virtual bool accept(int row, const QModelIndex &parent,
const QAbstractItemModel *const model) const {
virtual bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const {
for (int i : columns_) {
QModelIndex idx(model->index(row, i, parent));
if (cmp_->Matches(idx.data().toString().toLower())) return true;
@@ -399,8 +405,7 @@ FilterTree *FilterParser::parseSearchTerm() {
*iter_ == '-') {
break;
} else if (buf_.isEmpty()) {
// we don't know whether there is a column part in this search term
// thus we assume the latter and just try and read a prefix
// we don't know whether there is a column part in this search term thus we assume the latter and just try and read a prefix
if (prefix.isEmpty() && (*iter_ == '>' || *iter_ == '<' || *iter_ == '=' || *iter_ == '!')) {
prefix += *iter_;
}
@@ -435,8 +440,7 @@ FilterTree *FilterParser::createSearchTermTreeNode(
cmp = new NeComparator(search);
}
else if (!col.isEmpty() && columns_.contains(col) && numerical_columns_.contains(columns_[col])) {
// the length column contains the time in seconds (nano seconds, actually -
// the "nano" part is handled by the DropTailComparatorDecorator, though).
// the length column contains the time in seconds (nano seconds, actually - the "nano" part is handled by the DropTailComparatorDecorator, though).
int search_value;
if (columns_[col] == Playlist::Column_Length) {
search_value = parseTime(search);

View File

@@ -23,12 +23,14 @@
#include "config.h"
#include <stdbool.h>
#include <QMap>
#include <QModelIndex>
#include <QSet>
#include <QString>
#include <QAbstractItemModel>
class QAbstractItemModel;
class QModelIndex;
// structure for filter parse tree
class FilterTree {
@@ -80,9 +82,7 @@ class FilterParser {
void advance();
FilterTree *parseOrGroup();
FilterTree *parseAndGroup();
// check if iter is at the start of 'AND'
// if so, step over it and return true
// it not, return false and leave iter where it was
// check if iter is at the start of 'AND' if so, step over it and return true if not, return false and leave iter where it was
bool checkAnd();
// check if iter is at the start of 'OR'
bool checkOr(bool step_over = true);

View File

@@ -20,13 +20,23 @@
#include "config.h"
#include "playlistheader.h"
#include "playlistview.h"
#include <stdbool.h>
#include <QtDebug>
#include <QContextMenuEvent>
#include <QWidget>
#include <QAbstractItemModel>
#include <QFlags>
#include <QMenu>
#include <QSignalMapper>
#include <QVariant>
#include <QString>
#include <QtAlgorithms>
#include <QAction>
#include <QActionGroup>
#include <QContextMenuEvent>
#include <QtEvents>
#include "playlistheader.h"
#include "playlistview.h"
PlaylistHeader::PlaylistHeader(Qt::Orientation orientation, PlaylistView *view, QWidget *parent)
: StretchHeaderView(orientation, parent),

View File

@@ -23,13 +23,21 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QList>
#include <QAction>
#include <QMenu>
#include <QSignalMapper>
#include <QString>
#include <QtEvents>
#include "widgets/stretchheaderview.h"
class PlaylistView;
class QMenu;
class QSignalMapper;
class PlaylistHeader : public StretchHeaderView {
Q_OBJECT

View File

@@ -20,17 +20,22 @@
#include "config.h"
#include "playlistitem.h"
#include "songplaylistitem.h"
#include "core/logging.h"
#include "core/song.h"
#include "collection/collection.h"
#include "collection/collectionplaylistitem.h"
#include <stdbool.h>
#include <QSqlQuery>
#include <QtConcurrentRun>
#include <QFuture>
#include <QString>
#include <QColor>
#include <QSqlQuery>
#include <QtDebug>
#include "core/logging.h"
#include "core/song.h"
#include "collection/collection.h"
#include "collection/collectionplaylistitem.h"
#include "playlistitem.h"
#include "songplaylistitem.h"
PlaylistItem::~PlaylistItem() {
}

View File

@@ -24,16 +24,24 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QFuture>
#include <QMap>
#include <QFlags>
#include <QMetaType>
#include <QStandardItem>
#include <QList>
#include <QMap>
#include <QSet>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QColor>
#include <QVector>
#include <QAction>
#include <QSqlQuery>
#include "core/song.h"
class QAction;
class SqlRow;
class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
@@ -87,11 +95,8 @@ class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
QColor GetCurrentForegroundColor() const;
bool HasCurrentForegroundColor() const;
// Convenience function to find out whether this item is from the local
// collection, as opposed to a device, a file on disk, or a stream.
// Remember that even if this returns true, the collection item might be
// invalid so you might want to check that its id is not equal to -1
// before actually using it.
// Convenience function to find out whether this item is from the local collection, as opposed to a device, a file on disk, or a stream.
// Remember that even if this returns true, the collection item might be invalid so you might want to check that its id is not equal to -1 before actually using it.
virtual bool IsLocalCollectionItem() const { return false; }
void SetShouldSkip(bool val);
bool GetShouldSkip() const;

View File

@@ -23,8 +23,10 @@
#include "config.h"
#include "playlistitem.h"
#include <QObject>
#include "core/mimedata.h"
#include "playlistitem.h"
class PlaylistItemMimeData : public MimeData {
Q_OBJECT

View File

@@ -20,23 +20,39 @@
#include "config.h"
#include <QContextMenuEvent>
#include <QInputDialog>
#include <QObject>
#include <QWidget>
#include <QAction>
#include <QList>
#include <QSet>
#include <QVariant>
#include <QString>
#include <QStringBuilder>
#include <QMenu>
#include <QMessageBox>
#include <QPixmap>
#include <QPainter>
#include <QSortFilterProxyModel>
#include <QIcon>
#include <QSize>
#include <QStandardItemModel>
#include <QAbstractItemModel>
#include <QItemSelectionModel>
#include <QPersistentModelIndex>
#include <QSortFilterProxyModel>
#include <QMessageBox>
#include <QInputDialog>
#include <QToolButton>
#include <QtEvents>
#include "core/application.h"
#include "core/iconloader.h"
#include "core/player.h"
#include "playlist.h"
#include "playlistbackend.h"
#include "playlistlistview.h"
#include "playlistlistcontainer.h"
#include "playlistlistmodel.h"
#include "playlistmanager.h"
#include "ui_playlistlistcontainer.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/player.h"
#include "core/iconloader.h"
class PlaylistListSortFilterModel : public QSortFilterProxyModel {
public:
@@ -50,8 +66,7 @@ public:
if (ret < 0) return true;
if (ret > 0) return false;
// Now use the source model row order to ensure we always get a
// deterministic sorting even when two items are named the same.
// Now use the source model row order to ensure we always get a deterministic sorting even when two items are named the same.
return left.row() < right.row();
}
};
@@ -152,7 +167,7 @@ void PlaylistListContainer::SetApplication(Application *app) {
connect(manager, SIGNAL(CurrentChanged(Playlist*)), SLOT(CurrentChanged(Playlist*)));
connect(manager, SIGNAL(ActiveChanged(Playlist*)), SLOT(ActiveChanged(Playlist*)));
connect(model_, SIGNAL(PlaylistRenamed(int,QString)), manager, SLOT(Rename(int,QString)));
connect(model_, SIGNAL(PlaylistRenamed(int, QString)), manager, SLOT(Rename(int, QString)));
connect(player, SIGNAL(Paused()), SLOT(ActivePaused()));
connect(player, SIGNAL(Playing()), SLOT(ActivePlaying()));
@@ -187,8 +202,7 @@ void PlaylistListContainer::AddPlaylist(int id, const QString &name, bool favori
}
if (model_->PlaylistById(id)) {
// We know about this playlist already - it was probably one of the open
// ones that was loaded on startup.
// We know about this playlist already - it was probably one of the open ones that was loaded on startup.
return;
}

View File

@@ -23,14 +23,19 @@
#include "config.h"
#include "playlistbackend.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QString>
#include <QIcon>
#include <QAction>
#include <QMenu>
#include <QSortFilterProxyModel>
#include <QStandardItem>
#include <QtEvents>
class QMenu;
class QSortFilterProxyModel;
class QStandardItemModel;
class QModelIndex;
class Application;
class Playlist;
class PlaylistListModel;

View File

@@ -20,10 +20,17 @@
#include "config.h"
#include "playlistlistmodel.h"
#include "core/logging.h"
#include <QObject>
#include <QFlags>
#include <QMimeData>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QIcon>
#include <QStandardItemModel>
#include <QAbstractItemModel>
#include "playlistlistmodel.h"
PlaylistListModel::PlaylistListModel(QObject *parent) : QStandardItemModel(parent), dropping_rows_(false) {
@@ -112,7 +119,7 @@ void PlaylistListModel::AddRowItem(QStandardItem *item, const QString &parent_pa
}
void PlaylistListModel::RowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) {
void PlaylistListModel::RowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
for (int i = start; i <= end; ++i) {
const QModelIndex idx = index(i, 0, parent);

View File

@@ -23,7 +23,17 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QStandardItemModel>
#include <QMap>
#include <QIcon>
#include <QVariant>
#include <QString>
#include <QMimeData>
class QModelIndex;
class PlaylistListModel : public QStandardItemModel {
Q_OBJECT

View File

@@ -20,9 +20,16 @@
#include "config.h"
#include "playlistlistview.h"
#include <stdbool.h>
#include <QWidget>
#include <QFlags>
#include <QFont>
#include <QPainter>
#include <QPalette>
#include <QRect>
#include "playlistlistview.h"
PlaylistListView::PlaylistListView(QWidget *parent)
: AutoExpandingTreeView(parent) {}

View File

@@ -18,8 +18,16 @@
*
*/
#ifndef PLAYLISTVIEW_H
#define PLAYLISTVIEW_H
#include "config.h"
#include <QObject>
#include <QWidget>
#include <QString>
#include <QPaintEvent>
#include "widgets/autoexpandingtreeview.h"
class PlaylistListView : public AutoExpandingTreeView {
@@ -31,5 +39,7 @@ class PlaylistListView : public AutoExpandingTreeView {
protected:
// QWidget
void paintEvent(QPaintEvent* event);
void paintEvent(QPaintEvent *event);
};
#endif // PLAYLISTVIEW_H

View File

@@ -20,29 +20,45 @@
#include "config.h"
#include <memory>
#include <QtGlobal>
#include <QObject>
#include <QDialog>
#include <QtConcurrentRun>
#include <QFuture>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QFuture>
#include <QMessageBox>
#include <QtConcurrentRun>
#include <QList>
#include <QSet>
#include <QVariant>
#include <QString>
#include <QStringBuilder>
#include <QRegExp>
#include <QUrl>
#include <QAbstractItemModel>
#include <QSettings>
#include <QtDebug>
#include "playlistmanager.h"
#include "playlistbackend.h"
#include "playlistcontainer.h"
#include "playlistmanager.h"
#include "playlistsaveoptionsdialog.h"
#include "playlistview.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/player.h"
#include "core/songloader.h"
#include "core/utilities.h"
#include "collection/collectionbackend.h"
#include "collection/collectionplaylistitem.h"
#include "playlist.h"
#include "playlistbackend.h"
#include "playlistcontainer.h"
#include "playlistmanager.h"
#include "playlistitem.h"
#include "playlistview.h"
#include "playlistsaveoptionsdialog.h"
#include "playlistparsers/playlistparser.h"
class ParserBase;
PlaylistManager::PlaylistManager(Application *app, QObject *parent)
: PlaylistManagerInterface(app, parent),
app_(app),
@@ -176,9 +192,7 @@ void PlaylistManager::Save(int id, const QString &filename, Playlist::Path path_
parser_->Save(playlist(id)->GetAllSongs(), filename, path_type);
}
else {
// Playlist is not in the playlist manager: probably save action was
// triggered
// from the left side bar and the playlist isn't loaded.
// Playlist is not in the playlist manager: probably save action was triggered from the left side bar and the playlist isn't loaded.
QFuture<QList<Song>> future = QtConcurrent::run(playlist_backend_, &PlaylistBackend::GetPlaylistSongs, id);
NewClosure(future, this, SLOT(ItemsLoadedForSavePlaylist(QFuture<SongList>, QString, Playlist::Path)), future, filename, path_type);
@@ -272,17 +286,14 @@ void PlaylistManager::Rename(int id, const QString &new_name) {
void PlaylistManager::Favorite(int id, bool favorite) {
if (playlists_.contains(id)) {
// If playlists_ contains this playlist, its means it's opened: star or
// unstar it.
// If playlists_ contains this playlist, its means it's opened: star or unstar it.
playlist_backend_->FavoritePlaylist(id, favorite);
playlists_[id].p->set_favorite(favorite);
}
else {
Q_ASSERT(!favorite);
// Otherwise it means user wants to remove this playlist from the left
// panel,
// while it's not visible in the playlist tabbar either, because it has been
// closed: delete it.
// Otherwise it means user wants to remove this playlist from the left panel,
// while it's not visible in the playlist tabbar either, because it has been closed: delete it.
playlist_backend_->RemovePlaylist(id);
}
emit PlaylistFavorited(id, favorite);
@@ -359,9 +370,8 @@ void PlaylistManager::SetActivePlaylist(int id) {
void PlaylistManager::SetActiveToCurrent() {
// Check if we need to update the active playlist.
// By calling SetActiveToCurrent, the playlist manager emits the signal
// "ActiveChanged". This signal causes the network remote module to
// send all playlists to the clients, even no change happend.
// By calling SetActiveToCurrent, the playlist manager emits the signal "ActiveChanged".
// This signal causes the network remote module to send all playlists to the clients, even no change happend.
if (current_id() != active_id()) {
SetActivePlaylist(current_id());
}

View File

@@ -23,11 +23,16 @@
#include "config.h"
#include <QColor>
#include <QItemSelection>
#include <QMap>
#include <stdbool.h>
#include <QObject>
#include <QSettings>
#include <QList>
#include <QMap>
#include <QFuture>
#include <QString>
#include <QUrl>
#include <QModelIndex>
#include <QItemSelectionModel>
#include "core/song.h"
#include "playlist.h"
@@ -38,10 +43,6 @@ class PlaylistBackend;
class PlaylistContainer;
class PlaylistParser;
class PlaylistSequence;
class TaskManager;
class QModelIndex;
class QUrl;
class PlaylistManagerInterface : public QObject {
Q_OBJECT
@@ -76,22 +77,22 @@ public:
virtual PlaylistContainer *playlist_container() const = 0;
public slots:
virtual void New(const QString& name, const SongList& songs = SongList(), const QString& special_type = QString()) = 0;
virtual void Load(const QString& filename) = 0;
virtual void Save(int id, const QString& filename, Playlist::Path path_type) = 0;
virtual void Rename(int id, const QString& new_name) = 0;
virtual void New(const QString &name, const SongList& songs = SongList(), const QString &special_type = QString()) = 0;
virtual void Load(const QString &filename) = 0;
virtual void Save(int id, const QString &filename, Playlist::Path path_type) = 0;
virtual void Rename(int id, const QString &new_name) = 0;
virtual void Delete(int id) = 0;
virtual bool Close(int id) = 0;
virtual void Open(int id) = 0;
virtual void ChangePlaylistOrder(const QList<int>& ids) = 0;
virtual void SongChangeRequestProcessed(const QUrl& url, bool valid) = 0;
virtual void SongChangeRequestProcessed(const QUrl &url, bool valid) = 0;
virtual void SetCurrentPlaylist(int id) = 0;
virtual void SetActivePlaylist(int id) = 0;
virtual void SetActiveToCurrent() = 0;
virtual void SelectionChanged(const QItemSelection& selection) = 0;
virtual void SelectionChanged(const QItemSelection &selection) = 0;
// Convenience slots that defer to either current() or active()
virtual void ClearCurrent() = 0;
@@ -105,22 +106,21 @@ public slots:
signals:
void PlaylistManagerInitialized();
void PlaylistAdded(int id, const QString& name, bool favorite);
void PlaylistAdded(int id, const QString &name, bool favorite);
void PlaylistDeleted(int id);
void PlaylistClosed(int id);
void PlaylistRenamed(int id, const QString& new_name);
void PlaylistRenamed(int id, const QString &new_name);
void PlaylistFavorited(int id, bool favorite);
void CurrentChanged(Playlist *new_playlist);
void ActiveChanged(Playlist *new_playlist);
void Error(const QString& message);
void SummaryTextChanged(const QString& summary);
void Error(const QString &message);
void SummaryTextChanged(const QString &summary);
// Forwarded from individual playlists
void CurrentSongChanged(const Song& song);
// Signals that one of manager's playlists has changed (new items, new
// ordering etc.) - the argument shows which.
// Signals that one of manager's playlists has changed (new items, new ordering etc.) - the argument shows which.
void PlaylistChanged(Playlist *playlist);
void EditingFinished(const QModelIndex& index);
void PlayRequested(const QModelIndex& index);
@@ -168,12 +168,12 @@ class PlaylistManager : public PlaylistManagerInterface {
PlaylistContainer *playlist_container() const { return playlist_container_; }
public slots:
void New(const QString& name, const SongList& songs = SongList(), const QString& special_type = QString());
void Load(const QString& filename);
void Save(int id, const QString& filename, Playlist::Path path_type);
void New(const QString &name, const SongList &songs = SongList(), const QString &special_type = QString());
void Load(const QString &filename);
void Save(int id, const QString &filename, Playlist::Path path_type);
// Display a file dialog to let user choose a file before saving the file
void SaveWithUI(int id, const QString& playlist_name);
void Rename(int id, const QString& new_name);
void SaveWithUI(int id, const QString &playlist_name);
void Rename(int id, const QString &new_name);
void Favorite(int id, bool favorite);
void Delete(int id);
bool Close(int id);
@@ -184,7 +184,7 @@ public slots:
void SetActivePlaylist(int id);
void SetActiveToCurrent();
void SelectionChanged(const QItemSelection& selection);
void SelectionChanged(const QItemSelection &selection);
// Makes a playlist current if it's open already, or opens it and makes it current if it is hidden.
void SetCurrentOrOpen(int id);
@@ -216,7 +216,7 @@ public slots:
void ItemsLoadedForSavePlaylist(QFuture<SongList> future, const QString& filename, Playlist::Path path_type);
private:
Playlist *AddPlaylist(int id, const QString& name, const QString& special_type, const QString& ui_path, bool favorite);
Playlist *AddPlaylist(int id, const QString& name, const QString &special_type, const QString& ui_path, bool favorite);
private:
struct Data {

View File

@@ -20,13 +20,17 @@
#include "config.h"
#include "playlistsaveoptionsdialog.h"
#include "ui_playlistsaveoptionsdialog.h"
#include "playlistparsers/parserbase.h"
#include <QWidget>
#include <QDialog>
#include <QVariant>
#include <QCheckBox>
#include <QComboBox>
#include <QSettings>
#include "playlist.h"
#include "playlistsaveoptionsdialog.h"
#include "ui_playlistsaveoptionsdialog.h"
const char *PlaylistSaveOptionsDialog::kSettingsGroup = "PlaylistSaveOptionsDialog";
PlaylistSaveOptionsDialog::PlaylistSaveOptionsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::PlaylistSaveOptionsDialog) {

View File

@@ -23,7 +23,10 @@
#include "config.h"
#include <QObject>
#include <QWidget>
#include <QDialog>
#include <QString>
#include "playlist.h"

View File

@@ -20,15 +20,21 @@
#include "config.h"
#include <QWidget>
#include <QMenu>
#include <QSize>
#include <QVariant>
#include <QIcon>
#include <QPixmap>
#include <QPainter>
#include <QAction>
#include <QActionGroup>
#include <QToolButton>
#include "core/iconloader.h"
#include "core/settingsprovider.h"
#include "playlistsequence.h"
#include "ui_playlistsequence.h"
#include "core/iconloader.h"
#include <QMenu>
#include <QActionGroup>
#include <QSettings>
#include <QtDebug>
#include <QPainter>
const char *PlaylistSequence::kSettingsGroup = "PlaylistSequence";
@@ -40,8 +46,7 @@ PlaylistSequence::PlaylistSequence(QWidget *parent, SettingsProvider *settings)
shuffle_menu_(new QMenu(this)),
loading_(false),
repeat_mode_(Repeat_Off),
shuffle_mode_(Shuffle_Off),
dynamic_(false)
shuffle_mode_(Shuffle_Off)
{
ui_->setupUi(this);
@@ -189,7 +194,6 @@ void PlaylistSequence::SetShuffleMode(ShuffleMode mode) {
case Shuffle_Albums: ui_->action_shuffle_albums->setChecked(true); break;
}
if (mode != shuffle_mode_) {
shuffle_mode_ = mode;
emit ShuffleModeChanged(mode);
@@ -199,23 +203,12 @@ void PlaylistSequence::SetShuffleMode(ShuffleMode mode) {
}
void PlaylistSequence::SetUsingDynamicPlaylist(bool dynamic) {
dynamic_ = dynamic;
const QString not_available(tr("Not available while using a dynamic playlist"));
setEnabled(!dynamic);
ui_->shuffle->setToolTip(dynamic ? not_available : tr("Shuffle"));
ui_->repeat->setToolTip(dynamic ? not_available : tr("Repeat"));
}
PlaylistSequence::ShuffleMode PlaylistSequence::shuffle_mode() const {
return dynamic_ ? Shuffle_Off : shuffle_mode_;
return shuffle_mode_;
}
PlaylistSequence::RepeatMode PlaylistSequence::repeat_mode() const {
return dynamic_ ? Repeat_Off : repeat_mode_;
return repeat_mode_;
}
//called from global shortcut

View File

@@ -24,13 +24,17 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QString>
#include <QIcon>
#include <QPixmap>
#include <QAction>
#include <QMenu>
#include "core/settingsprovider.h"
class QMenu;
class SettingsProvider;
class Ui_PlaylistSequence;
class PlaylistSequence : public QWidget {
@@ -68,7 +72,6 @@ class PlaylistSequence : public QWidget {
void SetShuffleMode(PlaylistSequence::ShuffleMode mode);
void CycleShuffleMode();
void CycleRepeatMode();
void SetUsingDynamicPlaylist(bool dynamic);
signals:
void RepeatModeChanged(PlaylistSequence::RepeatMode mode);
@@ -94,7 +97,7 @@ class PlaylistSequence : public QWidget {
bool loading_;
RepeatMode repeat_mode_;
ShuffleMode shuffle_mode_;
bool dynamic_;
};
#endif // PLAYLISTSEQUENCE_H

View File

@@ -20,25 +20,39 @@
#include "config.h"
#include <QWidget>
#include <QAbstractItemModel>
#include <QList>
#include <QVariant>
#include <QString>
#include <QIcon>
#include <QAction>
#include <QGridLayout>
#include <QInputDialog>
#include <QLayout>
#include <QLineEdit>
#include <QMimeData>
#include <QRect>
#include <QSize>
#include <QToolTip>
#include <QLayoutItem>
#include <QMenu>
#include <QMessageBox>
#include <QTabBar>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QtEvents>
#include <QSettings>
#include "core/iconloader.h"
#include "core/logging.h"
#include "core/mimedata.h"
#include "core/song.h"
#include "widgets/favoritewidget.h"
#include "widgets/renametablineedit.h"
#include "playlist.h"
#include "playlistmanager.h"
#include "playlisttabbar.h"
#include "playlistview.h"
#include "songmimedata.h"
#include "core/logging.h"
#include "core/iconloader.h"
#include "widgets/renametablineedit.h"
#include "widgets/favoritewidget.h"
#include <QCheckBox>
#include <QContextMenuEvent>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QToolTip>
const char *PlaylistTabBar::kSettingsGroup = "PlaylistTabBar";
@@ -91,8 +105,7 @@ void PlaylistTabBar::SetManager(PlaylistManager *manager) {
void PlaylistTabBar::PlaylistManagerInitialized() {
// Signal that we are done loading and thus further changes should be
// committed to the db.
// Signal that we are done loading and thus further changes should be committed to the db.
initialized_ = true;
disconnect(manager_, SIGNAL(PlaylistManagerInitialized()), this, SLOT(PlaylistManagerInitialized()));
@@ -100,7 +113,7 @@ void PlaylistTabBar::PlaylistManagerInitialized() {
void PlaylistTabBar::contextMenuEvent(QContextMenuEvent *e) {
//we need to finish the renaming action before showing context menu
// We need to finish the renaming action before showing context menu
if (rename_editor_->isVisible()) {
//discard any change
HideEditor();
@@ -128,10 +141,10 @@ void PlaylistTabBar::mouseReleaseEvent(QMouseEvent *e) {
}
void PlaylistTabBar::mouseDoubleClickEvent(QMouseEvent *e) {
int index = tabAt(e->pos());
// discard a double click with the middle button
// Discard a double click with the middle button
if (e->button() != Qt::MidButton) {
if (index == -1) {
new_->activate(QAction::Trigger);
@@ -172,10 +185,10 @@ void PlaylistTabBar::RenameInline() {
void PlaylistTabBar::HideEditor() {
//editingFinished() will be called twice due to Qt bug #40, so we reuse the same instance, don't delete it
// editingFinished() will be called twice due to Qt bug #40, so we reuse the same instance, don't delete it
rename_editor_->setVisible(false);
// hack to give back focus to playlist view
// Hack to give back focus to playlist view
manager_->SetCurrentPlaylist(manager_->current()->id());
}
@@ -232,10 +245,8 @@ void PlaylistTabBar::Close() {
}
}
// Close the playlist. If the playlist is not a favorite playlist, it will be
// deleted, as it will not be visible after being closed. Otherwise, the tab
// is closed but the playlist still exists and can be resurrected from the
// "Playlists" tab.
// Close the playlist. If the playlist is not a favorite playlist, it will be deleted, as it will not be visible after being closed.
// Otherwise, the tab is closed but the playlist still exists and can be resurrected from the "Playlists" tab.
emit Close(playlist_id);
// Select the nearest tab.
@@ -305,8 +316,8 @@ void PlaylistTabBar::CurrentIndexChanged(int index) {
if (!suppress_current_changed_) emit CurrentIdChanged(tabData(index).toInt());
}
void PlaylistTabBar::InsertTab(int id, int index, const QString &text,
bool favorite) {
void PlaylistTabBar::InsertTab(int id, int index, const QString &text, bool favorite) {
suppress_current_changed_ = true;
insertTab(index, text);
setTabData(index, id);
@@ -319,8 +330,7 @@ void PlaylistTabBar::InsertTab(int id, int index, const QString &text,
setTabButton(index, QTabBar::LeftSide, widget);
suppress_current_changed_ = false;
// If we are still starting up, we don't need to do this, as the
// tab ordering after startup will be the same as was already in the db.
// If we are still starting up, we don't need to do this, as the tab ordering after startup will be the same as was already in the db.
if (initialized_) {
if (currentIndex() == index) emit CurrentIdChanged(id);
@@ -330,11 +340,13 @@ void PlaylistTabBar::InsertTab(int id, int index, const QString &text,
}
void PlaylistTabBar::TabMoved() {
QList<int> ids;
for (int i = 0; i < count(); ++i) {
ids << tabData(i).toInt();
}
emit PlaylistOrderChanged(ids);
}
void PlaylistTabBar::dragEnterEvent(QDragEnterEvent *e) {
@@ -364,6 +376,7 @@ void PlaylistTabBar::dragLeaveEvent(QDragLeaveEvent*) {
}
void PlaylistTabBar::timerEvent(QTimerEvent *e) {
QTabBar::timerEvent(e);
if (e->timerId() == drag_hover_timer_.timerId()) {

View File

@@ -23,15 +23,31 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QBasicTimer>
#include <QList>
#include <QString>
#include <QIcon>
#include <QTabBar>
#include <QAction>
#include <QMenu>
#include <QtEvents>
class QEvent;
class QContextMenuEvent;
class QDragEnterEvent;
class QDragLeaveEvent;
class QDragMoveEvent;
class QDropEvent;
class QMouseEvent;
class QTimerEvent;
class PlaylistManager;
class RenameTabLineEdit;
class QMenu;
class PlaylistTabBar : public QTabBar {
Q_OBJECT

View File

@@ -20,8 +20,16 @@
#include "config.h"
#include "playlistundocommands.h"
#include <memory>
#include <QList>
#include <QUrl>
#include <QUndoStack>
#include "core/song.h"
#include "playlist.h"
#include "playlistitem.h"
#include "playlistundocommands.h"
namespace PlaylistUndoCommands {
@@ -66,8 +74,7 @@ RemoveItems::RemoveItems(Playlist *playlist, int pos, int count) : Base(playlist
void RemoveItems::redo() {
for (int i = 0; i < ranges_.count(); ++i)
ranges_[i].items_ =
playlist_->RemoveItemsWithoutUndo(ranges_[i].pos_, ranges_[i].count_);
ranges_[i].items_ = playlist_->RemoveItemsWithoutUndo(ranges_[i].pos_, ranges_[i].count_);
}
void RemoveItems::undo() {
@@ -111,9 +118,9 @@ void ReOrderItems::undo() { playlist_->ReOrderWithoutUndo(old_items_); }
void ReOrderItems::redo() { playlist_->ReOrderWithoutUndo(new_items_); }
SortItems::SortItems(Playlist* playlist, int column, Qt::SortOrder order, const PlaylistItemList &new_items)
: ReOrderItems(playlist, new_items),
column_(column),
order_(order)
: ReOrderItems(playlist, new_items)
//column_(column),
//order_(order)
{
setText(tr("sort songs"));
}

View File

@@ -23,14 +23,18 @@
#include "config.h"
#include <QUndoCommand>
#include <stdbool.h>
#include <QCoreApplication>
#include <QList>
#include <QUndoStack>
#include "playlistitem.h"
class Playlist;
namespace PlaylistUndoCommands {
enum Types {
Type_RemoveItems = 0,
};
@@ -52,9 +56,8 @@ namespace PlaylistUndoCommands {
void undo();
void redo();
// When load is async, items have already been pushed, so we need to update them.
// This function try to find the equivalent item, and replace it with the
// new (completely loaded) one.
// return true if the was found (and updated), false otherwise
// This function try to find the equivalent item, and replace it with the new (completely loaded) one.
// Return true if the was found (and updated), false otherwise
bool UpdateItem(const PlaylistItemPtr &updated_item);
private:
@@ -110,11 +113,11 @@ namespace PlaylistUndoCommands {
class SortItems : public ReOrderItems {
public:
SortItems(Playlist *playlist, int column, Qt::SortOrder order, const PlaylistItemList &new_items);
SortItems(Playlist *playlist, int column, Qt::SortOrder order, const PlaylistItemList &new_items);
private:
int column_;
Qt::SortOrder order_;
//int column_;
//Qt::SortOrder order_;
};
class ShuffleItems : public ReOrderItems {

View File

@@ -22,33 +22,60 @@
#include <math.h>
#include <QCommonStyle>
#include <QClipboard>
#include <QPainter>
#include <QHeaderView>
#include <QSettings>
#include <QtDebug>
#include <QTimer>
#include <QKeyEvent>
#include <QApplication>
#include <QSortFilterProxyModel>
#include <QScrollBar>
#include <QTimeLine>
#include <QObject>
#include <QWidget>
#include <QList>
#include <QAbstractItemView>
#include <QByteArray>
#include <QClipboard>
#include <QCommonStyle>
#include <QFontMetrics>
#include <QHeaderView>
#include <QItemSelectionModel>
#include <QKeySequence>
#include <QMimeData>
#include <QSize>
#include <QSortFilterProxyModel>
#include <QTimeLine>
#include <QTimer>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QStringBuilder>
#include <QUrl>
#include <QImage>
#include <QPixmap>
#include <QPainter>
#include <QPalette>
#include <QColor>
#include <QBrush>
#include <QPen>
#include <QPoint>
#include <QRect>
#include <QRegion>
#include <QtAlgorithms>
#include <QStyleOptionHeader>
#include <QStyleOptionViewItem>
#include <QProxyStyle>
#include <QTreeView>
#include <QLinearGradient>
#include <QScrollBar>
#include <QtEvents>
#include <QSettings>
#include "core/application.h"
#include "core/player.h"
#include "core/qt_blurimage.h"
#include "core/song.h"
#include "playlist.h"
#include "playlistview.h"
#include "playlistdelegates.h"
#include "playlistheader.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/player.h"
#include "core/iconloader.h"
#include "core/qt_blurimage.h"
#include "playlistview.h"
#include "covermanager/currentartloader.h"
#include "settings/appearancesettingspage.h"
#include "settings/playbacksettingspage.h"
#include "settings/playlistsettingspage.h"
#include "settings/appearancesettingspage.h"
const int PlaylistView::kStateVersion = 6;
const int PlaylistView::kGlowIntensitySteps = 24;
@@ -72,7 +99,7 @@ void PlaylistProxyStyle::drawControl(ControlElement element, const QStyleOption
const QString &text = header_option->text;
const QFontMetrics &font_metrics = header_option->fontMetrics;
// spaces added to make transition less abrupt
// Spaces added to make transition less abrupt
if (rect.width() < font_metrics.width(text + " ")) {
const Playlist::Column column = static_cast<Playlist::Column>(header_option->section);
QStyleOptionHeader new_option(*header_option);
@@ -132,8 +159,6 @@ PlaylistView::PlaylistView(QWidget *parent)
drag_over_(false)
{
//qLog(Debug) << __PRETTY_FUNCTION__;
setHeader(header_);
header_->setSectionsMovable(true);
setStyle(style_);
@@ -248,8 +273,7 @@ void PlaylistView::setModel(QAbstractItemModel *m) {
disconnect(model(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(InvalidateCachedCurrentPixmap()));
//disconnect(model(), SIGNAL(layoutAboutToBeChanged()), this, SLOT(RatingHoverOut()));
// When changing the model, always invalidate the current pixmap.
// If a remote client uses "stop after", without invaliding the stop
// mark would not appear.
// If a remote client uses "stop after", without invaliding the stop mark would not appear.
InvalidateCachedCurrentPixmap();
}
@@ -323,8 +347,6 @@ void PlaylistView::SaveGeometry() {
void PlaylistView::ReloadBarPixmaps() {
//qLog(Debug) << __PRETTY_FUNCTION__;
currenttrack_bar_left_ = LoadBarPixmap(":/pictures/currenttrack_bar_left.png");
currenttrack_bar_mid_ = LoadBarPixmap(":/pictures/currenttrack_bar_mid.png");
currenttrack_bar_right_ = LoadBarPixmap(":/pictures/currenttrack_bar_right.png");
@@ -413,13 +435,11 @@ void PlaylistView::drawRow(QPainter *painter, const QStyleOptionViewItem &option
opt.palette.setColor(QPalette::AlternateBase, Qt::transparent);
opt.decorationSize = QSize(20, 20);
// Draw the actual row data on top. We cache this, because it's fairly
// expensive (1-2ms), and we do it many times per second.
// Draw the actual row data on top. We cache this, because it's fairly expensive (1-2ms), and we do it many times per second.
const bool cache_dirty = cached_current_row_rect_ != opt.rect || cached_current_row_row_ != index.row() || cached_current_row_.isNull();
// We can't update the cache if we're not drawing the entire region,
// QTreeView clips its drawing to only the columns in the region, so it
// wouldn't update the whole pixmap properly.
// QTreeView clips its drawing to only the columns in the region, so it wouldn't update the whole pixmap properly.
const bool whole_region = current_paint_region_.boundingRect().width() == viewport()->width();
if (!cache_dirty) {
@@ -560,8 +580,7 @@ void PlaylistView::RemoveSelected(bool deleting_from_disk) {
// Store the last selected row, which is the last in the list
int last_row = selection.last().top();
// Sort the selection so we remove the items at the *bottom* first, ensuring
// we don't have to mess around with changing row numbers
// Sort the selection so we remove the items at the *bottom* first, ensuring we don't have to mess around with changing row numbers
qSort(selection.begin(), selection.end(), CompareSelectionRanges);
for (const QItemSelectionRange &range : selection) {
@@ -580,8 +599,7 @@ void PlaylistView::RemoveSelected(bool deleting_from_disk) {
// Select the new current item, we want always the item after the last selected
if (new_index.isValid()) {
// Workaround to update keyboard selected row, if it's not the first row
// (this also triggers selection)
// Workaround to update keyboard selected row, if it's not the first row (this also triggers selection)
if (new_row != 0)
keyPressEvent(new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier));
// Update visual selection with the entire row
@@ -677,8 +695,6 @@ void PlaylistView::leaveEvent(QEvent *e) {
}
void PlaylistView::mousePressEvent(QMouseEvent *event) {
//qLog(Debug) << __PRETTY_FUNCTION__;
if (editTriggers() & QAbstractItemView::NoEditTriggers) {
QTreeView::mousePressEvent(event);
@@ -710,8 +726,7 @@ void PlaylistView::scrollContentsBy(int dx, int dy) {
}
void PlaylistView::InhibitAutoscrollTimeout() {
// For 30 seconds after the user clicks on or scrolls the playlist we promise
// not to automatically scroll the view to keep up with a track change.
// For 30 seconds after the user clicks on or scrolls the playlist we promise not to automatically scroll the view to keep up with a track change.
inhibit_autoscroll_ = false;
}
@@ -766,11 +781,9 @@ void PlaylistView::paintEvent(QPaintEvent *event) {
// Reimplemented to draw the background image.
// Reimplemented also to draw the drop indicator
// When the user is dragging some stuff over the playlist paintEvent gets
// called for the entire viewport every time the user moves the mouse.
// The drawTree is kinda expensive, so we cache the result and draw from the
// cache while the user is dragging. The cached pixmap gets invalidated in
// dragLeaveEvent, dropEvent and scrollContentsBy.
// When the user is dragging some stuff over the playlist paintEvent gets called for the entire viewport every time the user moves the mouse.
// The drawTree is kinda expensive, so we cache the result and draw from the cache while the user is dragging.
// The cached pixmap gets invalidated in dragLeaveEvent, dropEvent and scrollContentsBy.
// Draw background
if (background_image_type_ == Custom || background_image_type_ == Album) {
@@ -908,8 +921,6 @@ void PlaylistView::PlaylistDestroyed() {
}
void PlaylistView::ReloadSettings() {
//qLog(Debug) << __PRETTY_FUNCTION__;
QSettings s;
@@ -979,10 +990,9 @@ void PlaylistView::ReloadSettings() {
int opacity_level = s.value("opacity_level", kDefaultOpacityLevel).toInt();
s.endGroup();
// Check if background properties have changed.
// We change properties only if they have actually changed, to avoid to call
// set_background_image when it is not needed, as this will cause the fading
// animation to start again. This also avoid to do useless
// "force_background_redraw".
// We change properties only if they have actually changed, to avoid to call set_background_image when it is not needed,
// as this will cause the fading animation to start again.
// This also avoid to do useless "force_background_redraw".
if (!background_initialized_ || background_image_filename != background_image_filename_ || background_type != background_image_type_ || blur_radius_ != blur_radius || opacity_level_ != opacity_level) {
background_initialized_ = true;
@@ -998,10 +1008,8 @@ void PlaylistView::ReloadSettings() {
set_background_image(current_song_cover_art_);
}
else {
// User changed background image type to something that will not be
// painted through paintEvent: reset all background images.
// This avoid to use old (deprecated) images for fading when selecting
// Album or Custom background image type later.
// User changed background image type to something that will not be painted through paintEvent: reset all background images.
// This avoid to use old (deprecated) images for fading when selecting Album or Custom background image type later.
//set_background_image(QImage(":/pictures/playlistbg.png"));
set_background_image(QImage());
cached_scaled_background_image_ = QPixmap();
@@ -1064,8 +1072,7 @@ void PlaylistView::rowsInserted(const QModelIndex &parent, int start, int end) {
QTreeView::rowsInserted(parent, start, end);
if (at_end) {
// If the rows were inserted at the end of the playlist then let's scroll
// the view so the user can see.
// If the rows were inserted at the end of the playlist then let's scroll the view so the user can see.
scrollTo(model()->index(start, 0, parent), QAbstractItemView::PositionAtTop);
}
@@ -1211,10 +1218,8 @@ void PlaylistView::focusInEvent(QFocusEvent *event) {
if (event->reason() == Qt::TabFocusReason ||
event->reason() == Qt::BacktabFocusReason) {
// If there's a current item but no selection it probably means the list was
// filtered, and the selected item does not match the filter. If there's
// only 1 item in the view it is now impossible to select that item without
// using the mouse.
// If there's a current item but no selection it probably means the list was filtered, and the selected item does not match the filter.
// If there's only 1 item in the view it is now impossible to select that item without using the mouse.
const QModelIndex &current = selectionModel()->currentIndex();
if (current.isValid() && selectionModel()->selectedIndexes().isEmpty()) {
QItemSelection new_selection(current.sibling(current.row(), 0), current.sibling(current.row(), current.model()->columnCount(current.parent()) - 1));

View File

@@ -24,25 +24,56 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QBasicTimer>
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QList>
#include <QString>
#include <QImage>
#include <QPixmap>
#include <QRect>
#include <QRegion>
#include <QStyle>
#include <QStyleOption>
#include <QProxyStyle>
#include <QTreeView>
#include <QPoint>
#include <QTimer>
#include <QBasicTimer>
#include <QTimeLine>
#include <QCommonStyle>
#include <QPainter>
#include <QAbstractItemDelegate>
#include <QAbstractItemModel>
#include <QStyleOptionViewItem>
#include <QtEvents>
#include "playlist.h"
class QCommonStyle;
class QEvent;
class QShowEvent;
class QContextMenuEvent;
class QDragEnterEvent;
class QDragLeaveEvent;
class QDragMoveEvent;
class QDropEvent;
class QFocusEvent;
class QHideEvent;
class QKeyEvent;
class QMouseEvent;
class QPaintEvent;
class QTimerEvent;
class Application;
class Song;
class CollectionBackend;
class PlaylistHeader;
class QTimeLine;
// This proxy style works around a bug/feature introduced in Qt 4.7's QGtkStyle
// that uses Gtk to paint row backgrounds, ignoring any custom brush or palette
// the caller set in the QStyleOption. That breaks our currently playing track
// animation, which relies on the background painted by Qt to be transparent.
// that uses Gtk to paint row backgrounds, ignoring any custom brush or palette the caller set in the QStyleOption.
// That breaks our currently playing track animation, which relies on the background painted by Qt to be transparent.
// This proxy style uses QCommonStyle to paint the affected elements.
// This class is used by the global search view as well.
class PlaylistProxyStyle : public QProxyStyle {
@@ -164,8 +195,7 @@ class PlaylistView : public QTreeView {
background_image_type_ = bg;
emit BackgroundPropertyChanged();
}
// Save image as the background_image_ after applying some modifications
// (opacity, ...).
// Save image as the background_image_ after applying some modifications (opacity, ...).
// Should be used instead of modifying background_image_ directly
void set_background_image(const QImage &image);
@@ -190,9 +220,9 @@ class PlaylistView : public QTreeView {
bool background_initialized_;
BackgroundImageType background_image_type_;
// Stores the background image to be displayed. As we want this image to be
// particular (in terms of format, opacity), you should probably use
// set_background_image_type instead of modifying background_image_ directly
// Stores the background image to be displayed.
// As we want this image to be particular (in terms of format, opacity),
// you should probably use set_background_image_type instead of modifying background_image_ directly
QImage background_image_;
int blur_radius_;
int opacity_level_;

View File

@@ -20,11 +20,24 @@
#include "config.h"
#include "queue.h"
#include <QObject>
#include <QIODevice>
#include <QDataStream>
#include <QBuffer>
#include <QFlags>
#include <QList>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QStringBuilder>
#include <QMimeData>
#include <QtDebug>
#include <QtAlgorithms>
#include <QAbstractItemModel>
#include <QAbstractProxyModel>
#include <QPersistentModelIndex>
#include "playlist.h"
#include "queue.h"
const char *Queue::kRowsMimetype = "application/x-strawberry-queue-rows";
@@ -63,15 +76,15 @@ QModelIndex Queue::mapToSource(const QModelIndex &proxy_index) const {
void Queue::setSourceModel(QAbstractItemModel *source_model) {
if (sourceModel()) {
disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(SourceDataChanged(QModelIndex,QModelIndex)));
disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(SourceLayoutChanged()));
disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(SourceDataChanged(QModelIndex, QModelIndex)));
disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(SourceLayoutChanged()));
disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(SourceLayoutChanged()));
}
QAbstractProxyModel::setSourceModel(source_model);
connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(SourceDataChanged(QModelIndex,QModelIndex)));
connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(SourceLayoutChanged()));
connect(sourceModel(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(SourceDataChanged(QModelIndex, QModelIndex)));
connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(SourceLayoutChanged()));
connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(SourceLayoutChanged()));
}
@@ -94,7 +107,6 @@ void Queue::SourceLayoutChanged() {
beginRemoveRows(QModelIndex(), i, i);
source_indexes_.removeAt(i);
endRemoveRows();
--i;
}
}
@@ -181,8 +193,7 @@ void Queue::Move(const QList<int> &proxy_rows, int pos) {
layoutAboutToBeChanged();
QList<QPersistentModelIndex> moved_items;
// Take the items out of the list first, keeping track of whether the
// insertion point changes
// Take the items out of the list first, keeping track of whether the insertion point changes
int offset = 0;
for (int row : proxy_rows) {
moved_items << source_indexes_.takeAt(row - offset);
@@ -343,15 +354,15 @@ QVariant Queue::headerData(int section, Qt::Orientation orientation, int role) c
void Queue::Remove(QList<int> &proxy_rows) {
// order the rows
// Order the rows
qStableSort(proxy_rows);
// reflects immediately changes in the playlist
// Reflects immediately changes in the playlist
layoutAboutToBeChanged();
int removed_rows = 0;
for (int row : proxy_rows) {
// after the first row, the row number needs to be updated
// After the first row, the row number needs to be updated
const int real_row = row - removed_rows;
beginRemoveRows(QModelIndex(), real_row, real_row);
source_indexes_.removeAt(real_row);

View File

@@ -23,9 +23,16 @@
#include "config.h"
#include "playlist.h"
#include <stdbool.h>
#include <QObject>
#include <QAbstractItemModel>
#include <QAbstractProxyModel>
#include <QList>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QMimeData>
class Queue : public QAbstractProxyModel {
Q_OBJECT

View File

@@ -18,18 +18,26 @@
*
*/
#include "config.h"
#include <stdbool.h>
#include <QWidget>
#include <QDialog>
#include <QAbstractItemModel>
#include <QItemSelectionModel>
#include <QKeySequence>
#include <QList>
#include <QPushButton>
#include <QShortcut>
#include <QTreeView>
#include <QtAlgorithms>
#include "core/iconloader.h"
#include "playlist.h"
#include "playlistdelegates.h"
#include "playlistmanager.h"
#include "queue.h"
#include "queuemanager.h"
#include "ui_queuemanager.h"
#include "core/iconloader.h"
#include <QKeySequence>
#include <QShortcut>
QueueManager::QueueManager(QWidget *parent)
: QDialog(parent),
@@ -75,8 +83,8 @@ void QueueManager::SetPlaylistManager(PlaylistManager *manager) {
void QueueManager::CurrentPlaylistChanged(Playlist *playlist) {
if (current_playlist_) {
disconnect(current_playlist_->queue(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(UpdateButtonState()));
disconnect(current_playlist_->queue(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(UpdateButtonState()));
disconnect(current_playlist_->queue(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(UpdateButtonState()));
disconnect(current_playlist_->queue(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(UpdateButtonState()));
disconnect(current_playlist_->queue(), SIGNAL(layoutChanged()), this, SLOT(UpdateButtonState()));
disconnect(current_playlist_, SIGNAL(destroyed()), this, SLOT(PlaylistDestroyed()));
}

View File

@@ -23,14 +23,15 @@
#include "config.h"
#include <QObject>
#include <QWidget>
#include <QDialog>
#include <QString>
class Playlist;
class PlaylistManager;
class Ui_QueueManager;
class QModelIndex;
class QueueManager : public QDialog {
Q_OBJECT
@@ -58,4 +59,3 @@ private:
};
#endif // QUEUEMANAGER_H

View File

@@ -21,12 +21,16 @@
#include "config.h"
#include <QtConcurrentRun>
#include <QtAlgorithms>
#include <QList>
#include <QUrl>
#include "playlist.h"
#include "songloaderinserter.h"
#include "core/closure.h"
#include "core/logging.h"
#include "core/songloader.h"
#include "core/taskmanager.h"
#include "playlist.h"
#include "songloaderinserter.h"
SongLoaderInserter::SongLoaderInserter(TaskManager *task_manager, CollectionBackendInterface *collection, const Player *player)
: task_manager_(task_manager),
@@ -50,7 +54,7 @@ void SongLoaderInserter::Load(Playlist *destination, int row, bool play_now, boo
connect(this, SIGNAL(PreloadFinished()), SLOT(InsertSongs()));
connect(this, SIGNAL(EffectiveLoadFinished(const SongList&)), destination, SLOT(UpdateItems(const SongList&)));
for (const QUrl& url : urls) {
for (const QUrl &url : urls) {
SongLoader *loader = new SongLoader(collection_, player_, this);
SongLoader::Result ret = loader->Load(url);
@@ -122,8 +126,7 @@ void SongLoaderInserter::AudioCDTagsLoaded(bool success) {
}
void SongLoaderInserter::InsertSongs() {
// Insert songs (that haven't been completely loaded) to allow user to see
// and play them while not loaded completely
// Insert songs (that haven't been completely loaded) to allow user to see and play them while not loaded completely
if (destination_) {
destination_->InsertSongsOrCollectionItems(songs_, row_, play_now_, enqueue_);
}
@@ -140,9 +143,8 @@ void SongLoaderInserter::AsyncLoad() {
loader->LoadFilenamesBlocking();
task_manager_->SetTaskProgress(async_load_id, ++async_progress);
if (i == 0) {
// Load everything from the first song. It'll start playing as soon as
// we emit PreloadFinished, so it needs to have the duration set to show
// properly in the UI.
// Load everything from the first song.
// It'll start playing as soon as we emit PreloadFinished, so it needs to have the duration set to show properly in the UI.
loader->LoadMetadataBlocking();
}
songs_ << loader->songs();

View File

@@ -23,19 +23,20 @@
#include "config.h"
#include <QList>
#include <stdbool.h>
#include <QObject>
#include <QList>
#include <QString>
#include <QUrl>
#include "core/song.h"
class CollectionBackendInterface;
class Player;
class Playlist;
class SongLoader;
class TaskManager;
class QModelIndex;
class CollectionBackendInterface;
class Playlist;
class SongLoaderInserter : public QObject {
Q_OBJECT

View File

@@ -23,6 +23,7 @@
#include "config.h"
#include <QObject>
#include <QMimeData>
#include "core/mimedata.h"

View File

@@ -20,15 +20,14 @@
#include "config.h"
#include "playlistbackend.h"
#include "songplaylistitem.h"
#include <QString>
#include <QStringList>
#include <QUrl>
#include "core/tagreaderclient.h"
#include "collection/sqlrow.h"
#include <QtDebug>
#include <QFile>
#include <QSettings>
#include "playlistitem.h"
#include "songplaylistitem.h"
SongPlaylistItem::SongPlaylistItem(const QString &type) : PlaylistItem(type) {}

View File

@@ -23,17 +23,21 @@
#include "config.h"
#include "playlistitem.h"
#include <stdbool.h>
#include <QString>
#include <QUrl>
#include "core/song.h"
#include "collection/sqlrow.h"
#include "playlistitem.h"
class SongPlaylistItem : public PlaylistItem {
public:
SongPlaylistItem(const QString& type);
SongPlaylistItem(const Song& song);
SongPlaylistItem(const QString &type);
SongPlaylistItem(const Song &song);
// Restores a stream- or file-related playlist item using query row.
// If it's a file related playlist item, this will restore it's CUE
// attributes (if any) but won't parse the CUE!
// If it's a file related playlist item, this will restore it's CUE attributes (if any) but won't parse the CUE!
bool InitFromQuery(const SqlRow& query);
void Reload();