New context with albums and lyrics +++ much more
* Added new lyrics provider with lyrics from AudD and API Seeds * New improved context widget with albums and lyrics * Fixed playing and context widget getting stuck in play mode when there was an error * Changed icons for artists in collection, tidal and cover manager * Removed "search" icon from "Search automatically" checkbox (right click) that looked ugly * Removed some unused widgets from the src/widgets directory * Fixed initial size of window and side panel * Fixed saving window size correctly
This commit is contained in:
527
src/context/contextalbumsmodel.cpp
Normal file
527
src/context/contextalbumsmodel.cpp
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This code was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QtAlgorithms>
|
||||
#include <QMutex>
|
||||
#include <QMimeData>
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QPixmapCache>
|
||||
#include <QSettings>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/closure.h"
|
||||
#include "core/database.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/logging.h"
|
||||
#include "collection/collectionquery.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
#include "collection/collectionitem.h"
|
||||
#include "collection/sqlrow.h"
|
||||
#include "playlist/playlistmanager.h"
|
||||
#include "playlist/songmimedata.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
|
||||
#include "contextalbumsmodel.h"
|
||||
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
const int ContextAlbumsModel::kPrettyCoverSize = 32;
|
||||
const qint64 ContextAlbumsModel::kIconCacheSize = 100000000; //~100MB
|
||||
|
||||
ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent) :
|
||||
SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
|
||||
backend_(backend),
|
||||
app_(app),
|
||||
artist_icon_(IconLoader::Load("folder-sound")),
|
||||
album_icon_(IconLoader::Load("cdcase")),
|
||||
playlists_dir_icon_(IconLoader::Load("folder-sound")),
|
||||
playlist_icon_(IconLoader::Load("albums")),
|
||||
use_pretty_covers_(true)
|
||||
{
|
||||
|
||||
root_->lazy_loaded = true;
|
||||
|
||||
cover_loader_options_.desired_height_ = kPrettyCoverSize;
|
||||
cover_loader_options_.pad_output_image_ = true;
|
||||
cover_loader_options_.scale_output_image_ = true;
|
||||
|
||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)), SLOT(AlbumArtLoaded(quint64, QImage)));
|
||||
|
||||
QIcon nocover = IconLoader::Load("cdcase");
|
||||
no_cover_icon_ = nocover.pixmap(nocover.availableSizes().last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
}
|
||||
|
||||
ContextAlbumsModel::~ContextAlbumsModel() { delete root_; }
|
||||
|
||||
void ContextAlbumsModel::set_pretty_covers(bool use_pretty_covers) {
|
||||
|
||||
if (use_pretty_covers != use_pretty_covers_) {
|
||||
use_pretty_covers_ = use_pretty_covers;
|
||||
Reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::AddSongs(const SongList &songs) {
|
||||
|
||||
for (const Song &song : songs) {
|
||||
if (song_nodes_.contains(song.id())) continue;
|
||||
|
||||
// Before we can add each song we need to make sure the required container items already exist in the tree.
|
||||
|
||||
// Find parent containers in the tree
|
||||
CollectionItem *container = root_;
|
||||
|
||||
// Does it exist already?
|
||||
if (!container_nodes_.contains(song.album())) {
|
||||
// Create the container
|
||||
container_nodes_[song.album()] = ItemFromSong(CollectionItem::Type_Container, true, container, song, 0);
|
||||
}
|
||||
container = container_nodes_[song.album()];
|
||||
if (!container->lazy_loaded) continue;
|
||||
|
||||
// We've gone all the way down to the deepest level and everything was already lazy loaded, so now we have to create the song in the container.
|
||||
song_nodes_[song.id()] = ItemFromSong(CollectionItem::Type_Song, true, container, song, -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QString ContextAlbumsModel::AlbumIconPixmapCacheKey(const QModelIndex &index) const {
|
||||
|
||||
QStringList path;
|
||||
QModelIndex index_copy(index);
|
||||
while (index_copy.isValid()) {
|
||||
path.prepend(index_copy.data().toString());
|
||||
index_copy = index_copy.parent();
|
||||
}
|
||||
return "contextalbumsart:" + path.join("/");
|
||||
|
||||
}
|
||||
|
||||
QVariant ContextAlbumsModel::AlbumIcon(const QModelIndex &index) {
|
||||
|
||||
CollectionItem *item = IndexToItem(index);
|
||||
if (!item) return no_cover_icon_;
|
||||
|
||||
// Check the cache for a pixmap we already loaded.
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(index);
|
||||
|
||||
QPixmap cached_pixmap;
|
||||
if (QPixmapCache::find(cache_key, &cached_pixmap)) {
|
||||
return cached_pixmap;
|
||||
}
|
||||
|
||||
// Maybe we're loading a pixmap already?
|
||||
if (pending_cache_keys_.contains(cache_key)) {
|
||||
return no_cover_icon_;
|
||||
}
|
||||
|
||||
// No art is cached and we're not loading it already. Load art for the first song in the album.
|
||||
SongList songs = GetChildSongs(index);
|
||||
if (!songs.isEmpty()) {
|
||||
const quint64 id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, songs.first());
|
||||
pending_art_[id] = ItemAndCacheKey(item, cache_key);
|
||||
pending_cache_keys_.insert(cache_key);
|
||||
}
|
||||
|
||||
return no_cover_icon_;
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::AlbumArtLoaded(quint64 id, const QImage &image) {
|
||||
|
||||
ItemAndCacheKey item_and_cache_key = pending_art_.take(id);
|
||||
CollectionItem *item = item_and_cache_key.first;
|
||||
const QString &cache_key = item_and_cache_key.second;
|
||||
|
||||
if (!item) return;
|
||||
|
||||
pending_cache_keys_.remove(cache_key);
|
||||
|
||||
// Insert this image in the cache.
|
||||
if (image.isNull()) {
|
||||
// Set the no_cover image so we don't continually try to load art.
|
||||
QPixmapCache::insert(cache_key, no_cover_icon_);
|
||||
}
|
||||
else {
|
||||
//qLog(Debug) << cache_key;
|
||||
QPixmap image_pixmap;
|
||||
image_pixmap = QPixmap::fromImage(image);
|
||||
QPixmapCache::insert(cache_key, image_pixmap);
|
||||
}
|
||||
|
||||
const QModelIndex index = ItemToIndex(item);
|
||||
emit dataChanged(index, index);
|
||||
|
||||
}
|
||||
|
||||
QVariant ContextAlbumsModel::data(const QModelIndex &index, int role) const {
|
||||
|
||||
const CollectionItem *item = IndexToItem(index);
|
||||
|
||||
// Handle a special case for returning album artwork instead of a generic CD icon.
|
||||
// This is here instead of in the other data() function to let us use the QModelIndex& version of GetChildSongs,
|
||||
// which satisfies const-ness, instead of the CollectionItem *version, which doesn't.
|
||||
if (use_pretty_covers_) {
|
||||
bool is_album_node = false;
|
||||
if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container) {
|
||||
is_album_node = (item->container_level == 0);
|
||||
}
|
||||
if (is_album_node) {
|
||||
// It has const behaviour some of the time - that's ok right?
|
||||
return const_cast<ContextAlbumsModel*>(this)->AlbumIcon(index);
|
||||
}
|
||||
}
|
||||
|
||||
return data(item, role);
|
||||
|
||||
}
|
||||
|
||||
QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::ToolTipRole:
|
||||
return item->DisplayText();
|
||||
|
||||
case Qt::DecorationRole:
|
||||
switch (item->type) {
|
||||
case CollectionItem::Type_PlaylistContainer:
|
||||
return playlists_dir_icon_;
|
||||
case CollectionItem::Type_Container:
|
||||
if (item->type == CollectionItem::Type_Container && item->container_level == 0) { return album_icon_; }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Role_Type:
|
||||
return item->type;
|
||||
|
||||
case Role_IsDivider:
|
||||
return item->type == CollectionItem::Type_Divider;
|
||||
|
||||
case Role_ContainerType:
|
||||
return item->type;
|
||||
|
||||
case Role_Key:
|
||||
return item->key;
|
||||
|
||||
case Role_Artist:
|
||||
return item->metadata.artist();
|
||||
|
||||
case Role_Editable:
|
||||
if (!item->lazy_loaded) {
|
||||
const_cast<ContextAlbumsModel*>(this)->LazyPopulate(const_cast<CollectionItem*>(item), true);
|
||||
}
|
||||
|
||||
if (item->type == CollectionItem::Type_Container) {
|
||||
// if we have even one non editable item as a child, we ourselves are not available for edit
|
||||
if (!item->children.isEmpty()) {
|
||||
for (CollectionItem *child : item->children) {
|
||||
if (!data(child, role).toBool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (item->type == CollectionItem::Type_Song) {
|
||||
return item->metadata.IsEditable();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
case Role_SortText:
|
||||
return item->SortText();
|
||||
}
|
||||
return QVariant();
|
||||
|
||||
}
|
||||
|
||||
ContextAlbumsModel::QueryResult ContextAlbumsModel::RunQuery(CollectionItem *parent) {
|
||||
|
||||
QueryResult result;
|
||||
CollectionQuery q(query_options_);
|
||||
q.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
|
||||
// Walk up through the item's parents adding filters as necessary
|
||||
CollectionItem *p = parent;
|
||||
while (p && p->type == CollectionItem::Type_Container) {
|
||||
if (p->container_level == 0) {
|
||||
q.AddWhere("album", p->key);
|
||||
}
|
||||
p = p->parent;
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
QMutexLocker l(backend_->db()->Mutex());
|
||||
|
||||
if (!backend_->ExecQuery(&q)) return result;
|
||||
|
||||
while (q.Next()) {
|
||||
result.rows << SqlRow(q);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::PostQuery(CollectionItem *parent, const ContextAlbumsModel::QueryResult &result, bool signal) {
|
||||
|
||||
int child_level = (parent == root_ ? 0 : parent->container_level + 1);
|
||||
|
||||
for (const SqlRow &row : result.rows) {
|
||||
|
||||
CollectionItem::Type item_type = (parent == root_ ? CollectionItem::Type_Container : CollectionItem::Type_Song);
|
||||
|
||||
if (signal) beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
|
||||
|
||||
CollectionItem *item = new CollectionItem(item_type, parent);
|
||||
item->container_level = child_level;
|
||||
item->metadata.InitFromQuery(row, true);
|
||||
item->key = item->metadata.title();
|
||||
item->display_text = item->metadata.TitleWithCompilationArtist();
|
||||
item->sort_text = SortTextForSong(item->metadata);
|
||||
if (parent != root_) item->lazy_loaded = true;
|
||||
|
||||
if (signal) endInsertRows();
|
||||
|
||||
if (parent == root_) container_nodes_[item->key] = item;
|
||||
else song_nodes_[item->metadata.id()] = item;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::LazyPopulate(CollectionItem *parent, bool signal) {
|
||||
|
||||
if (parent->lazy_loaded) return;
|
||||
parent->lazy_loaded = true;
|
||||
|
||||
QueryResult result = RunQuery(parent);
|
||||
PostQuery(parent, result, signal);
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::Reset() {
|
||||
|
||||
beginResetModel();
|
||||
delete root_;
|
||||
song_nodes_.clear();
|
||||
container_nodes_.clear();
|
||||
pending_art_.clear();
|
||||
|
||||
root_ = new CollectionItem(this);
|
||||
root_->lazy_loaded = false;
|
||||
endResetModel();
|
||||
|
||||
}
|
||||
|
||||
CollectionItem *ContextAlbumsModel::ItemFromSong(CollectionItem::Type item_type, bool signal, CollectionItem *parent, const Song &s, int container_level) {
|
||||
|
||||
if (signal) beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
|
||||
|
||||
CollectionItem *item = new CollectionItem(item_type, parent);
|
||||
item->container_level = container_level;
|
||||
|
||||
if (item->key.isNull()) item->key = s.album();
|
||||
//if (item->key.isNull()) item->key = s.effective_albumartist();
|
||||
item->display_text = TextOrUnknown(item->key);
|
||||
item->sort_text = SortTextForArtist(item->key);
|
||||
|
||||
if (item_type == CollectionItem::Type_Song) item->lazy_loaded = true;
|
||||
if (signal) endInsertRows();
|
||||
|
||||
return item;
|
||||
|
||||
}
|
||||
|
||||
QString ContextAlbumsModel::TextOrUnknown(const QString &text) {
|
||||
|
||||
if (text.isEmpty()) return tr("Unknown");
|
||||
return text;
|
||||
|
||||
}
|
||||
|
||||
QString ContextAlbumsModel::SortText(QString text) {
|
||||
|
||||
if (text.isEmpty()) {
|
||||
text = " unknown";
|
||||
}
|
||||
else {
|
||||
text = text.toLower();
|
||||
}
|
||||
text = text.remove(QRegExp("[^\\w ]"));
|
||||
|
||||
return text;
|
||||
|
||||
}
|
||||
|
||||
QString ContextAlbumsModel::SortTextForArtist(QString artist) {
|
||||
|
||||
artist = SortText(artist);
|
||||
|
||||
if (artist.startsWith("the ")) {
|
||||
artist = artist.right(artist.length() - 4) + ", the";
|
||||
}
|
||||
else if (artist.startsWith("a ")) {
|
||||
artist = artist.right(artist.length() - 2) + ", a";
|
||||
}
|
||||
else if (artist.startsWith("an ")) {
|
||||
artist = artist.right(artist.length() - 3) + ", an";
|
||||
}
|
||||
|
||||
return artist;
|
||||
|
||||
}
|
||||
|
||||
QString ContextAlbumsModel::SortTextForSong(const Song &song) {
|
||||
|
||||
QString ret = QString::number(qMax(0, song.disc()) * 1000 + qMax(0, song.track()));
|
||||
ret.prepend(QString("0").repeated(6 - ret.length()));
|
||||
ret.append(song.url().toString());
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
Qt::ItemFlags ContextAlbumsModel::flags(const QModelIndex &index) const {
|
||||
|
||||
switch (IndexToItem(index)->type) {
|
||||
case CollectionItem::Type_Song:
|
||||
case CollectionItem::Type_Container:
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
|
||||
case CollectionItem::Type_Divider:
|
||||
case CollectionItem::Type_Root:
|
||||
case CollectionItem::Type_LoadingIndicator:
|
||||
default:
|
||||
return Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QStringList ContextAlbumsModel::mimeTypes() const {
|
||||
return QStringList() << "text/uri-list";
|
||||
}
|
||||
|
||||
QMimeData *ContextAlbumsModel::mimeData(const QModelIndexList &indexes) const {
|
||||
|
||||
if (indexes.isEmpty()) return nullptr;
|
||||
|
||||
SongMimeData *data = new SongMimeData;
|
||||
QList<QUrl> urls;
|
||||
QSet<int> song_ids;
|
||||
|
||||
data->backend = backend_;
|
||||
|
||||
for (const QModelIndex &index : indexes) {
|
||||
GetChildSongs(IndexToItem(index), &urls, &data->songs, &song_ids);
|
||||
}
|
||||
|
||||
data->setUrls(urls);
|
||||
data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs);
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
bool ContextAlbumsModel::CompareItems(const CollectionItem *a, const CollectionItem *b) const {
|
||||
|
||||
QVariant left(data(a, ContextAlbumsModel::Role_SortText));
|
||||
QVariant right(data(b, ContextAlbumsModel::Role_SortText));
|
||||
|
||||
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
|
||||
return left.toString() < right.toString();
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const {
|
||||
|
||||
switch (item->type) {
|
||||
case CollectionItem::Type_Container: {
|
||||
const_cast<ContextAlbumsModel*>(this)->LazyPopulate(item);
|
||||
|
||||
QList<CollectionItem*> children = item->children;
|
||||
qSort(children.begin(), children.end(), std::bind(&ContextAlbumsModel::CompareItems, this, _1, _2));
|
||||
|
||||
for (CollectionItem *child : children)
|
||||
GetChildSongs(child, urls, songs, song_ids);
|
||||
break;
|
||||
}
|
||||
|
||||
case CollectionItem::Type_Song:
|
||||
urls->append(item->metadata.url());
|
||||
if (!song_ids->contains(item->metadata.id())) {
|
||||
songs->append(item->metadata);
|
||||
song_ids->insert(item->metadata.id());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SongList ContextAlbumsModel::GetChildSongs(const QModelIndexList &indexes) const {
|
||||
|
||||
QList<QUrl> dontcare;
|
||||
SongList ret;
|
||||
QSet<int> song_ids;
|
||||
|
||||
for (const QModelIndex &index : indexes) {
|
||||
GetChildSongs(IndexToItem(index), &dontcare, &ret, &song_ids);
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
SongList ContextAlbumsModel::GetChildSongs(const QModelIndex &index) const {
|
||||
return GetChildSongs(QModelIndexList() << index);
|
||||
}
|
||||
|
||||
bool ContextAlbumsModel::canFetchMore(const QModelIndex &parent) const {
|
||||
|
||||
if (!parent.isValid()) return false;
|
||||
|
||||
CollectionItem *item = IndexToItem(parent);
|
||||
return !item->lazy_loaded;
|
||||
|
||||
}
|
||||
143
src/context/contextalbumsmodel.h
Normal file
143
src/context/contextalbumsmodel.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This code was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONTEXTALBUMSMODEL_H
|
||||
#define CONTEXTALBUMSMODEL_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QMimeData>
|
||||
#include <QImage>
|
||||
#include <QIcon>
|
||||
#include <QPixmap>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QSettings>
|
||||
|
||||
#include "core/simpletreemodel.h"
|
||||
#include "core/song.h"
|
||||
#include "collection/collectionquery.h"
|
||||
#include "collection/collectionitem.h"
|
||||
#include "collection/sqlrow.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
|
||||
class Application;
|
||||
class CollectionBackend;
|
||||
class CollectionItem;
|
||||
|
||||
class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent = nullptr);
|
||||
~ContextAlbumsModel();
|
||||
|
||||
static const int kPrettyCoverSize;
|
||||
static const qint64 kIconCacheSize;
|
||||
|
||||
enum Role {
|
||||
Role_Type = Qt::UserRole + 1,
|
||||
Role_ContainerType,
|
||||
Role_SortText,
|
||||
Role_Key,
|
||||
Role_Artist,
|
||||
Role_IsDivider,
|
||||
Role_Editable,
|
||||
LastRole
|
||||
};
|
||||
|
||||
struct QueryResult {
|
||||
QueryResult() {}
|
||||
SqlRowList rows;
|
||||
};
|
||||
|
||||
CollectionBackend *backend() const { return backend_; }
|
||||
|
||||
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
|
||||
SongList GetChildSongs(const QModelIndex &index) const;
|
||||
SongList GetChildSongs(const QModelIndexList &indexes) const;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
QStringList mimeTypes() const;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const;
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
|
||||
void set_pretty_covers(bool use_pretty_covers);
|
||||
bool use_pretty_covers() const { return use_pretty_covers_; }
|
||||
|
||||
static QString TextOrUnknown(const QString &text);
|
||||
static QString SortText(QString text);
|
||||
static QString SortTextForArtist(QString artist);
|
||||
static QString SortTextForSong(const Song &song);
|
||||
|
||||
void Reset();
|
||||
void AddSongs(const SongList &songs);
|
||||
|
||||
protected:
|
||||
void LazyPopulate(CollectionItem *item) { LazyPopulate(item, true); }
|
||||
void LazyPopulate(CollectionItem *item, bool signal);
|
||||
|
||||
private slots:
|
||||
void AlbumArtLoaded(quint64 id, const QImage &image);
|
||||
|
||||
private:
|
||||
QueryResult RunQuery(CollectionItem *parent);
|
||||
void PostQuery(CollectionItem *parent, const QueryResult &result, bool signal);
|
||||
CollectionItem *ItemFromSong(CollectionItem::Type item_type, bool signal, CollectionItem *parent, const Song &s, int container_level);
|
||||
|
||||
QString AlbumIconPixmapCacheKey(const QModelIndex &index) const;
|
||||
QVariant AlbumIcon(const QModelIndex &index);
|
||||
QVariant data(const CollectionItem *item, int role) const;
|
||||
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
|
||||
|
||||
private:
|
||||
CollectionBackend *backend_;
|
||||
Application *app_;
|
||||
QueryOptions query_options_;
|
||||
QMap<int, CollectionItem*> song_nodes_;
|
||||
QMap<QString, CollectionItem*> container_nodes_;
|
||||
QIcon artist_icon_;
|
||||
QIcon album_icon_;
|
||||
QPixmap no_cover_icon_;
|
||||
QIcon playlists_dir_icon_;
|
||||
QIcon playlist_icon_;
|
||||
QNetworkDiskCache *icon_cache_;
|
||||
bool use_pretty_covers_;
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
typedef QPair<CollectionItem*, QString> ItemAndCacheKey;
|
||||
QMap<quint64, ItemAndCacheKey> pending_art_;
|
||||
QSet<QString> pending_cache_keys_;
|
||||
};
|
||||
|
||||
#endif // CONTEXTALBUMSMODEL_H
|
||||
532
src/context/contextalbumsview.cpp
Normal file
532
src/context/contextalbumsview.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This code was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <qcoreevent.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QWidget>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QAction>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QList>
|
||||
#include <QLocale>
|
||||
#include <QMap>
|
||||
#include <QMessageBox>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QPen>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QSet>
|
||||
#include <QSize>
|
||||
#include <QToolTip>
|
||||
#include <QTreeView>
|
||||
#include <QHeaderView>
|
||||
#include <QWhatsThis>
|
||||
#include <QBrush>
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
#include <QLinearGradient>
|
||||
#include <QSettings>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "core/utilities.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
#include "collection/collectiondirectorymodel.h"
|
||||
#include "collection/collectionitem.h"
|
||||
#include "device/devicemanager.h"
|
||||
#include "device/devicestatefiltermodel.h"
|
||||
#include "dialogs/edittagdialog.h"
|
||||
#ifdef HAVE_GSTREAMER
|
||||
#include "dialogs/organisedialog.h"
|
||||
#endif
|
||||
#include "settings/collectionsettingspage.h"
|
||||
|
||||
#include "contextview.h"
|
||||
#include "contextalbumsmodel.h"
|
||||
#include "contextalbumsview.h"
|
||||
|
||||
ContextItemDelegate::ContextItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
|
||||
|
||||
void ContextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const {
|
||||
|
||||
const bool is_divider = index.data(ContextAlbumsModel::Role_IsDivider).toBool();
|
||||
|
||||
if (is_divider) {
|
||||
QString text(index.data().toString());
|
||||
|
||||
painter->save();
|
||||
|
||||
QRect text_rect(opt.rect);
|
||||
|
||||
// Does this item have an icon?
|
||||
QPixmap pixmap;
|
||||
QVariant decoration = index.data(Qt::DecorationRole);
|
||||
if (!decoration.isNull()) {
|
||||
if (decoration.canConvert<QPixmap>()) {
|
||||
pixmap = decoration.value<QPixmap>();
|
||||
}
|
||||
else if (decoration.canConvert<QIcon>()) {
|
||||
pixmap = decoration.value<QIcon>().pixmap(opt.decorationSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the icon at the left of the text rectangle
|
||||
if (!pixmap.isNull()) {
|
||||
QRect icon_rect(text_rect.topLeft(), opt.decorationSize);
|
||||
const int padding = (text_rect.height() - icon_rect.height()) / 2;
|
||||
icon_rect.adjust(padding, padding, padding, padding);
|
||||
text_rect.moveLeft(icon_rect.right() + padding + 6);
|
||||
|
||||
if (pixmap.size() != opt.decorationSize) {
|
||||
pixmap = pixmap.scaled(opt.decorationSize, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
painter->drawPixmap(icon_rect, pixmap);
|
||||
}
|
||||
else {
|
||||
text_rect.setLeft(text_rect.left() + 30);
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
QFont bold_font(opt.font);
|
||||
bold_font.setBold(true);
|
||||
|
||||
painter->setPen(opt.palette.color(QPalette::Text));
|
||||
painter->setFont(bold_font);
|
||||
painter->drawText(text_rect, text);
|
||||
|
||||
// Draw the line under the item
|
||||
QColor line_color = opt.palette.color(QPalette::Text);
|
||||
QLinearGradient grad_color(opt.rect.bottomLeft(), opt.rect.bottomRight());
|
||||
const double fade_start_end = (opt.rect.width()/3.0)/opt.rect.width();
|
||||
line_color.setAlphaF(0.0);
|
||||
grad_color.setColorAt(0, line_color);
|
||||
line_color.setAlphaF(0.5);
|
||||
grad_color.setColorAt(fade_start_end, line_color);
|
||||
grad_color.setColorAt(1.0 - fade_start_end, line_color);
|
||||
line_color.setAlphaF(0.0);
|
||||
grad_color.setColorAt(1, line_color);
|
||||
painter->setPen(QPen(grad_color, 1));
|
||||
painter->drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight());
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
else {
|
||||
if (!is_divider) QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ContextItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) {
|
||||
|
||||
return true;
|
||||
|
||||
Q_UNUSED(option);
|
||||
|
||||
if (!event || !view) return false;
|
||||
|
||||
QHelpEvent *he = static_cast<QHelpEvent*>(event);
|
||||
QString text = displayText(index.data(), QLocale::system());
|
||||
|
||||
if (text.isEmpty() || !he) return false;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::ToolTip: {
|
||||
QRect displayed_text;
|
||||
QSize real_text;
|
||||
bool is_elided = false;
|
||||
|
||||
real_text = sizeHint(option, index);
|
||||
displayed_text = view->visualRect(index);
|
||||
is_elided = displayed_text.width() < real_text.width();
|
||||
|
||||
if (is_elided) {
|
||||
QToolTip::showText(he->globalPos(), text, view);
|
||||
}
|
||||
else if (index.data(Qt::ToolTipRole).isValid()) {
|
||||
// If the item has a tooltip text, display it
|
||||
QString tooltip_text = index.data(Qt::ToolTipRole).toString();
|
||||
QToolTip::showText(he->globalPos(), tooltip_text, view);
|
||||
}
|
||||
else {
|
||||
// in case that another text was previously displayed
|
||||
QToolTip::hideText();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::QueryWhatsThis:
|
||||
return true;
|
||||
|
||||
case QEvent::WhatsThis:
|
||||
QWhatsThis::showText(he->globalPos(), text, view);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
ContextAlbumsView::ContextAlbumsView(QWidget *parent)
|
||||
: AutoExpandingTreeView(parent),
|
||||
app_(nullptr),
|
||||
context_menu_(nullptr),
|
||||
is_in_keyboard_search_(false),
|
||||
model_(nullptr)
|
||||
{
|
||||
|
||||
setStyleSheet("border: none;");
|
||||
|
||||
setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
|
||||
setItemDelegate(new ContextItemDelegate(this));
|
||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
setHeaderHidden(true);
|
||||
setAllColumnsShowFocus(true);
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DragOnly);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
SetAddOnDoubleClick(false);
|
||||
|
||||
}
|
||||
|
||||
ContextAlbumsView::~ContextAlbumsView() {}
|
||||
|
||||
void ContextAlbumsView::SaveFocus() {
|
||||
|
||||
QModelIndex current = currentIndex();
|
||||
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
|
||||
if (!type.isValid() || !(type.toInt() == CollectionItem::Type_Song || type.toInt() == CollectionItem::Type_Container || type.toInt() == CollectionItem::Type_Divider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_selected_path_.clear();
|
||||
last_selected_song_ = Song();
|
||||
last_selected_container_ = QString();
|
||||
|
||||
switch (type.toInt()) {
|
||||
case CollectionItem::Type_Song: {
|
||||
QModelIndex index = current;
|
||||
SongList songs = model_->GetChildSongs(index);
|
||||
if (!songs.isEmpty()) {
|
||||
last_selected_song_ = songs.last();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CollectionItem::Type_Container:
|
||||
case CollectionItem::Type_Divider: {
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
SaveContainerPath(current);
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::SaveContainerPath(const QModelIndex &child) {
|
||||
|
||||
|
||||
// return;
|
||||
|
||||
QModelIndex current = model()->parent(child);
|
||||
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
|
||||
if (!type.isValid() || !(type.toInt() == CollectionItem::Type_Container || type.toInt() == CollectionItem::Type_Divider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = model()->data(current, ContextAlbumsModel::Role_SortText).toString();
|
||||
last_selected_path_ << text;
|
||||
SaveContainerPath(current);
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::RestoreFocus() {
|
||||
|
||||
if (last_selected_container_.isEmpty() && last_selected_song_.url().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
RestoreLevelFocus();
|
||||
|
||||
}
|
||||
|
||||
bool ContextAlbumsView::RestoreLevelFocus(const QModelIndex &parent) {
|
||||
|
||||
if (model()->canFetchMore(parent)) {
|
||||
model()->fetchMore(parent);
|
||||
}
|
||||
int rows = model()->rowCount(parent);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
QModelIndex current = model()->index(i, 0, parent);
|
||||
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
|
||||
switch (type.toInt()) {
|
||||
case CollectionItem::Type_Song:
|
||||
if (!last_selected_song_.url().isEmpty()) {
|
||||
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
|
||||
SongList songs = model_->GetChildSongs(index);
|
||||
for (const Song &song : songs) {
|
||||
if (song == last_selected_song_) {
|
||||
setCurrentIndex(current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::ReloadSettings() {
|
||||
|
||||
QSettings settings;
|
||||
|
||||
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
||||
SetAutoOpen(settings.value("auto_open", true).toBool());
|
||||
|
||||
if (app_ && model_) {
|
||||
model_->set_pretty_covers(settings.value("pretty_covers", true).toBool());
|
||||
}
|
||||
|
||||
settings.endGroup();
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::SetApplication(Application *app) {
|
||||
|
||||
app_ = app;
|
||||
|
||||
model_ = new ContextAlbumsModel(app_->collection_backend(), app_, this);
|
||||
model_->Reset();
|
||||
|
||||
setModel(model_);
|
||||
|
||||
connect(model_, SIGNAL(modelAboutToBeReset()), this, SLOT(SaveFocus()));
|
||||
connect(model_, SIGNAL(modelReset()), this, SLOT(RestoreFocus()));
|
||||
|
||||
ReloadSettings();
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::paintEvent(QPaintEvent *event) {
|
||||
QTreeView::paintEvent(event);
|
||||
}
|
||||
|
||||
void ContextAlbumsView::mouseReleaseEvent(QMouseEvent *e) {
|
||||
QTreeView::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
|
||||
if (!context_menu_) {
|
||||
context_menu_ = new QMenu(this);
|
||||
//context_menu_->setStyleSheet("background-color: #3DADE8;");
|
||||
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenInNewPlaylist()));
|
||||
|
||||
context_menu_->addSeparator();
|
||||
add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue track"), this, SLOT(AddToPlaylistEnqueue()));
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
context_menu_->addSeparator();
|
||||
organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise()));
|
||||
copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice()));
|
||||
#endif
|
||||
|
||||
context_menu_->addSeparator();
|
||||
edit_track_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit track information..."), this, SLOT(EditTracks()));
|
||||
edit_tracks_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit tracks information..."), this, SLOT(EditTracks()));
|
||||
show_in_browser_ = context_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, SLOT(ShowInBrowser()));
|
||||
|
||||
context_menu_->addSeparator();
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
|
||||
connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool)));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
context_menu_index_ = indexAt(e->pos());
|
||||
if (!context_menu_index_.isValid()) return;
|
||||
QModelIndexList selected_indexes = selectionModel()->selectedRows();
|
||||
|
||||
int regular_elements = 0;
|
||||
int regular_editable = 0;
|
||||
|
||||
for (const QModelIndex &index : selected_indexes) {
|
||||
regular_elements++;
|
||||
if(model_->data(index, ContextAlbumsModel::Role_Editable).toBool()) {
|
||||
regular_editable++;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check if custom plugin actions should be enabled / visible
|
||||
const int songs_selected = regular_elements;
|
||||
const bool regular_elements_only = songs_selected == regular_elements && regular_elements > 0;
|
||||
|
||||
// in all modes
|
||||
load_->setEnabled(songs_selected);
|
||||
add_to_playlist_->setEnabled(songs_selected);
|
||||
open_in_new_playlist_->setEnabled(songs_selected);
|
||||
add_to_playlist_enqueue_->setEnabled(songs_selected);
|
||||
|
||||
// if neither edit_track not edit_tracks are available, we show disabled edit_track element
|
||||
edit_track_->setVisible(regular_editable <= 1);
|
||||
edit_track_->setEnabled(regular_editable == 1);
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
organise_->setVisible(regular_elements_only);
|
||||
copy_to_device_->setVisible(regular_elements_only);
|
||||
#endif
|
||||
|
||||
// only when all selected items are editable
|
||||
#ifdef HAVE_GSTREAMER
|
||||
organise_->setEnabled(regular_elements == regular_editable);
|
||||
copy_to_device_->setEnabled(regular_elements == regular_editable);
|
||||
#endif
|
||||
|
||||
context_menu_->popup(e->globalPos());
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::Load() {
|
||||
|
||||
QMimeData *data = model()->mimeData(selectedIndexes());
|
||||
if (MimeData *mime_data = qobject_cast<MimeData*>(data)) {
|
||||
mime_data->clear_first_ = true;
|
||||
}
|
||||
emit AddToPlaylistSignal(data);
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::AddToPlaylist() {
|
||||
|
||||
emit AddToPlaylistSignal(model()->mimeData(selectedIndexes()));
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::AddToPlaylistEnqueue() {
|
||||
|
||||
QMimeData *data = model()->mimeData(selectedIndexes());
|
||||
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
|
||||
mime_data->enqueue_now_ = true;
|
||||
}
|
||||
emit AddToPlaylistSignal(data);
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::OpenInNewPlaylist() {
|
||||
|
||||
QMimeData *data = model()->mimeData(selectedIndexes());
|
||||
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
|
||||
mime_data->open_in_new_playlist_ = true;
|
||||
}
|
||||
emit AddToPlaylistSignal(data);
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::scrollTo(const QModelIndex &index, ScrollHint hint) {
|
||||
|
||||
if (is_in_keyboard_search_)
|
||||
QTreeView::scrollTo(index, QAbstractItemView::PositionAtTop);
|
||||
else
|
||||
QTreeView::scrollTo(index, hint);
|
||||
|
||||
}
|
||||
|
||||
SongList ContextAlbumsView::GetSelectedSongs() const {
|
||||
QModelIndexList selected_indexes = selectionModel()->selectedRows();
|
||||
return model_->GetChildSongs(selected_indexes);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void ContextAlbumsView::Organise() {
|
||||
|
||||
if (!organise_dialog_)
|
||||
organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
|
||||
|
||||
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
|
||||
organise_dialog_->SetCopy(false);
|
||||
if (organise_dialog_->SetSongs(GetSelectedSongs()))
|
||||
organise_dialog_->show();
|
||||
else {
|
||||
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ContextAlbumsView::EditTracks() {
|
||||
|
||||
if (!edit_tag_dialog_) {
|
||||
edit_tag_dialog_.reset(new EditTagDialog(app_, this));
|
||||
}
|
||||
edit_tag_dialog_->SetSongs(GetSelectedSongs());
|
||||
edit_tag_dialog_->show();
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void ContextAlbumsView::CopyToDevice() {
|
||||
|
||||
if (!organise_dialog_)
|
||||
organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
|
||||
|
||||
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
|
||||
organise_dialog_->SetCopy(true);
|
||||
organise_dialog_->SetSongs(GetSelectedSongs());
|
||||
organise_dialog_->show();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void ContextAlbumsView::ShowInBrowser() {
|
||||
|
||||
QList<QUrl> urls;
|
||||
for (const Song &song : GetSelectedSongs()) {
|
||||
urls << song.url();
|
||||
}
|
||||
|
||||
Utilities::OpenInFileBrowser(urls);
|
||||
}
|
||||
151
src/context/contextalbumsview.h
Normal file
151
src/context/contextalbumsview.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This code was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONTEXTALBUMSVIEW_H
|
||||
#define CONTEXTALBUMSVIEW_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QSet>
|
||||
#include <QStyleOption>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "widgets/autoexpandingtreeview.h"
|
||||
|
||||
class Application;
|
||||
class EditTagDialog;
|
||||
#ifdef HAVE_GSTREAMER
|
||||
class OrganiseDialog;
|
||||
#endif
|
||||
class ContextAlbumsModel;
|
||||
|
||||
class ContextItemDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ContextItemDelegate(QObject *parent);
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
public slots:
|
||||
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index);
|
||||
};
|
||||
|
||||
class ContextAlbumsView : public AutoExpandingTreeView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ContextAlbumsView(QWidget *parent = nullptr);
|
||||
~ContextAlbumsView();
|
||||
|
||||
// Returns Songs currently selected in the collection view.
|
||||
// Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs.
|
||||
SongList GetSelectedSongs() const;
|
||||
|
||||
void SetApplication(Application *app);
|
||||
|
||||
// QTreeView
|
||||
void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
|
||||
|
||||
ContextAlbumsModel *albums_model() { return model_; }
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
|
||||
void SaveFocus();
|
||||
void RestoreFocus();
|
||||
|
||||
signals:
|
||||
void ShowConfigDialog();
|
||||
|
||||
protected:
|
||||
// QWidget
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
||||
private slots:
|
||||
void Load();
|
||||
void AddToPlaylist();
|
||||
void AddToPlaylistEnqueue();
|
||||
void OpenInNewPlaylist();
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void Organise();
|
||||
void CopyToDevice();
|
||||
#endif
|
||||
void EditTracks();
|
||||
void ShowInBrowser();
|
||||
|
||||
private:
|
||||
void RecheckIsEmpty();
|
||||
bool RestoreLevelFocus(const QModelIndex &parent = QModelIndex());
|
||||
void SaveContainerPath(const QModelIndex &child);
|
||||
|
||||
private:
|
||||
Application *app_;
|
||||
|
||||
QMenu *context_menu_;
|
||||
QModelIndex context_menu_index_;
|
||||
QAction *load_;
|
||||
QAction *add_to_playlist_;
|
||||
QAction *add_to_playlist_enqueue_;
|
||||
QAction *open_in_new_playlist_;
|
||||
#ifdef HAVE_GSTREAMER
|
||||
QAction *organise_;
|
||||
QAction *copy_to_device_;
|
||||
#endif
|
||||
QAction *delete_;
|
||||
QAction *edit_track_;
|
||||
QAction *edit_tracks_;
|
||||
QAction *show_in_browser_;
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
std::unique_ptr<OrganiseDialog> organise_dialog_;
|
||||
#endif
|
||||
std::unique_ptr<EditTagDialog> edit_tag_dialog_;
|
||||
|
||||
bool is_in_keyboard_search_;
|
||||
|
||||
// Save focus
|
||||
Song last_selected_song_;
|
||||
QString last_selected_container_;
|
||||
QSet<QString> last_selected_path_;
|
||||
|
||||
ContextAlbumsModel *model_;
|
||||
|
||||
};
|
||||
|
||||
#endif // CONTEXTALBUMSVIEW_H
|
||||
|
||||
654
src/context/contextview.cpp
Normal file
654
src/context/contextview.cpp
Normal file
@@ -0,0 +1,654 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QIODevice>
|
||||
#include <QFile>
|
||||
#include <QByteArray>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QMovie>
|
||||
#include <QAction>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QBrush>
|
||||
#include <QMenu>
|
||||
#include <QSettings>
|
||||
#include <QSizePolicy>
|
||||
#include <QTimeLine>
|
||||
#include <QtEvents>
|
||||
#include <QFontDatabase>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/player.h"
|
||||
#include "core/song.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "engine/engine_fwd.h"
|
||||
#include "engine/enginebase.h"
|
||||
#include "engine/enginetype.h"
|
||||
#include "engine/enginedevice.h"
|
||||
#include "engine/devicefinder.h"
|
||||
#include "collection/collection.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
#include "collection/collectionquery.h"
|
||||
#include "collection/collectionmodel.h"
|
||||
#include "collection/collectionview.h"
|
||||
#include "covermanager/albumcoverchoicecontroller.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
#include "covermanager/currentartloader.h"
|
||||
#include "lyrics/lyricsfetcher.h"
|
||||
|
||||
#include "contextview.h"
|
||||
#include "contextalbumsmodel.h"
|
||||
#include "ui_contextviewcontainer.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
const char *ContextView::kSettingsGroup = "ContextView";
|
||||
|
||||
ContextView::ContextView(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
app_(nullptr),
|
||||
ui_(new Ui_ContextViewContainer),
|
||||
collectionview_(nullptr),
|
||||
menu_(new QMenu(this)),
|
||||
timeline_fade_(new QTimeLine(1000, this)),
|
||||
image_strawberry_(":/pictures/strawberry.png"),
|
||||
album_cover_choice_controller_(new AlbumCoverChoiceController(this)),
|
||||
lyrics_fetcher_(nullptr),
|
||||
active_(false),
|
||||
downloading_covers_(false)
|
||||
{
|
||||
|
||||
ui_->setupUi(this);
|
||||
ui_->widget_stacked->setCurrentWidget(ui_->widget_stop);
|
||||
ui_->label_play_album->installEventFilter(this);
|
||||
|
||||
QFontDatabase::addApplicationFont(":/fonts/HumongousofEternitySt.ttf");
|
||||
|
||||
connect(timeline_fade_, SIGNAL(valueChanged(qreal)), SLOT(FadePreviousTrack(qreal)));
|
||||
timeline_fade_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0
|
||||
|
||||
cover_loader_options_.desired_height_ = 300;
|
||||
cover_loader_options_.pad_output_image_ = true;
|
||||
cover_loader_options_.scale_output_image_ = true;
|
||||
pixmap_current_ = QPixmap::fromImage(AlbumCoverLoader::ScaleAndPad(cover_loader_options_, image_strawberry_));
|
||||
|
||||
AddActions();
|
||||
LoadSettings();
|
||||
|
||||
}
|
||||
|
||||
ContextView::~ContextView() { delete ui_; }
|
||||
|
||||
void ContextView::LoadSettings() {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
action_show_data_->setChecked(s.value("show_data", true).toBool());
|
||||
action_show_output_->setChecked(s.value("show_output", true).toBool());
|
||||
action_show_albums_->setChecked(s.value("show_albums", true).toBool());
|
||||
action_show_lyrics_->setChecked(s.value("show_lyrics", false).toBool());
|
||||
album_cover_choice_controller_->search_cover_auto_action()->setChecked(s.value("search_for_cover_auto", true).toBool());
|
||||
|
||||
s.endGroup();
|
||||
|
||||
}
|
||||
|
||||
void ContextView::SetApplication(Application *app) {
|
||||
|
||||
app_ = app;
|
||||
|
||||
connect(app_->current_art_loader(), SIGNAL(ArtLoaded(Song, QString, QImage)), SLOT(AlbumArtLoaded(Song, QString, QImage)));
|
||||
|
||||
album_cover_choice_controller_->SetApplication(app_);
|
||||
connect(album_cover_choice_controller_, SIGNAL(AutomaticCoverSearchDone()), this, SLOT(AutomaticCoverSearchDone()));
|
||||
connect(album_cover_choice_controller_->cover_from_file_action(), SIGNAL(triggered()), this, SLOT(LoadCoverFromFile()));
|
||||
connect(album_cover_choice_controller_->cover_to_file_action(), SIGNAL(triggered()), this, SLOT(SaveCoverToFile()));
|
||||
connect(album_cover_choice_controller_->cover_from_url_action(), SIGNAL(triggered()), this, SLOT(LoadCoverFromURL()));
|
||||
connect(album_cover_choice_controller_->search_for_cover_action(), SIGNAL(triggered()), this, SLOT(SearchForCover()));
|
||||
connect(album_cover_choice_controller_->unset_cover_action(), SIGNAL(triggered()), this, SLOT(UnsetCover()));
|
||||
connect(album_cover_choice_controller_->show_cover_action(), SIGNAL(triggered()), this, SLOT(ShowCover()));
|
||||
connect(album_cover_choice_controller_->search_cover_auto_action(), SIGNAL(triggered()), this, SLOT(SearchCoverAutomatically()));
|
||||
|
||||
ui_->widget_play_albums->SetApplication(app_);
|
||||
|
||||
lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this);
|
||||
connect(lyrics_fetcher_, SIGNAL(LyricsFetched(quint64, const QString)), this, SLOT(UpdateLyrics(quint64, const QString)));
|
||||
|
||||
}
|
||||
|
||||
void ContextView::SetCollectionView(CollectionView *collectionview) {
|
||||
collectionview_ = collectionview;
|
||||
connect(collectionview_, SIGNAL(TotalSongCountUpdated_()), this, SLOT(UpdateNoSong()));
|
||||
connect(collectionview_, SIGNAL(TotalArtistCountUpdated_()), this, SLOT(UpdateNoSong()));
|
||||
connect(collectionview_, SIGNAL(TotalAlbumCountUpdated_()), this, SLOT(UpdateNoSong()));
|
||||
}
|
||||
|
||||
void ContextView::AddActions() {
|
||||
|
||||
action_show_data_ = new QAction(tr("Show song technical data"), this);
|
||||
action_show_data_->setCheckable(true);
|
||||
action_show_data_->setChecked(true);
|
||||
|
||||
action_show_output_ = new QAction(tr("Show engine and device"), this);
|
||||
action_show_output_->setCheckable(true);
|
||||
action_show_output_->setChecked(true);
|
||||
|
||||
action_show_albums_ = new QAction(tr("Show albums by artist"), this);
|
||||
action_show_albums_->setCheckable(true);
|
||||
action_show_albums_->setChecked(true);
|
||||
|
||||
action_show_lyrics_ = new QAction(tr("Show song lyrics"), this);
|
||||
action_show_lyrics_->setCheckable(true);
|
||||
action_show_lyrics_->setChecked(false);
|
||||
|
||||
menu_->addAction(action_show_data_);
|
||||
menu_->addAction(action_show_output_);
|
||||
menu_->addAction(action_show_albums_);
|
||||
menu_->addAction(action_show_lyrics_);
|
||||
menu_->addSeparator();
|
||||
|
||||
connect(action_show_data_, SIGNAL(triggered()), this, SLOT(ActionShowData()));
|
||||
connect(action_show_output_, SIGNAL(triggered()), this, SLOT(ActionShowOutput()));
|
||||
connect(action_show_albums_, SIGNAL(triggered()), this, SLOT(ActionShowAlbums()));
|
||||
connect(action_show_lyrics_, SIGNAL(triggered()), this, SLOT(ActionShowLyrics()));
|
||||
|
||||
QList<QAction*> cover_actions = album_cover_choice_controller_->GetAllActions();
|
||||
cover_actions.append(album_cover_choice_controller_->search_cover_auto_action());
|
||||
menu_->addActions(cover_actions);
|
||||
menu_->addSeparator();
|
||||
|
||||
}
|
||||
|
||||
void ContextView::Playing() {
|
||||
}
|
||||
|
||||
void ContextView::Stopped() {
|
||||
|
||||
active_ = false;
|
||||
song_ = song_empty_;
|
||||
downloading_covers_ = false;
|
||||
prev_artist_ = QString();
|
||||
lyrics_ = QString();
|
||||
SetImage(image_strawberry_);
|
||||
|
||||
}
|
||||
|
||||
void ContextView::Error() {
|
||||
}
|
||||
|
||||
void ContextView::UpdateNoSong() {
|
||||
NoSong();
|
||||
}
|
||||
|
||||
void ContextView::SongChanged(const Song &song) {
|
||||
|
||||
image_previous_ = image_original_;
|
||||
prev_artist_ = song_.artist();
|
||||
lyrics_ = QString();
|
||||
song_ = song;
|
||||
UpdateSong();
|
||||
update();
|
||||
if (action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
|
||||
|
||||
}
|
||||
|
||||
void ContextView::SetText(QLabel *label, int value, const QString &suffix, const QString &def) {
|
||||
label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
|
||||
}
|
||||
|
||||
void ContextView::NoSong() {
|
||||
|
||||
ui_->label_stop_top->setStyleSheet(
|
||||
"font: 20pt \"Humongous of Eternity St\";"
|
||||
"font-weight: Regular;"
|
||||
);
|
||||
|
||||
ui_->label_stop_top->setText("No song playing");
|
||||
|
||||
QString html = QString(
|
||||
"%1 songs<br />\n"
|
||||
"%2 artists<br />\n"
|
||||
"%3 albums<br />\n"
|
||||
)
|
||||
.arg(collectionview_->TotalSongs())
|
||||
.arg(collectionview_->TotalArtists())
|
||||
.arg(collectionview_->TotalAlbums())
|
||||
;
|
||||
|
||||
ui_->label_stop_summary->setStyleSheet(
|
||||
"font: 12pt;"
|
||||
"font-weight: regular;"
|
||||
);
|
||||
ui_->label_stop_summary->setText(html);
|
||||
|
||||
}
|
||||
|
||||
void ContextView::UpdateSong() {
|
||||
|
||||
QList <QLabel *> labels_play_data;
|
||||
|
||||
labels_play_data << ui_->label_filetype
|
||||
<< ui_->filetype
|
||||
<< ui_->label_length
|
||||
<< ui_->length
|
||||
<< ui_->label_samplerate
|
||||
<< ui_->samplerate
|
||||
<< ui_->label_bitdepth
|
||||
<< ui_->bitdepth
|
||||
<< ui_->label_bitrate
|
||||
<< ui_->bitrate;
|
||||
|
||||
ui_->label_play_top->setStyleSheet(
|
||||
"font: 11pt;"
|
||||
"font-weight: regular;"
|
||||
);
|
||||
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song_.PrettyTitle().toHtmlEscaped(), song_.artist().toHtmlEscaped(), song_.album().toHtmlEscaped()));
|
||||
|
||||
if (action_show_data_->isChecked()) {
|
||||
for (QLabel *l : labels_play_data) {
|
||||
l->setEnabled(true);
|
||||
l->setVisible(true);
|
||||
l->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
}
|
||||
ui_->layout_play_data->setEnabled(true);
|
||||
ui_->filetype->setText(song_.TextForFiletype());
|
||||
ui_->length->setText(Utilities::PrettyTimeNanosec(song_.length_nanosec()));
|
||||
SetText(ui_->samplerate, song_.samplerate(), "Hz");
|
||||
SetText(ui_->bitdepth, song_.bitdepth(), "Bit");
|
||||
SetText(ui_->bitrate, song_.bitrate(), tr("kbps"));
|
||||
ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed);
|
||||
}
|
||||
else {
|
||||
for (QLabel *l : labels_play_data) {
|
||||
l->setEnabled(false);
|
||||
l->setVisible(false);
|
||||
l->setMaximumSize(0, 0);
|
||||
}
|
||||
ui_->layout_play_data->setEnabled(false);
|
||||
ui_->filetype->clear();
|
||||
ui_->length->clear();
|
||||
ui_->samplerate->clear();
|
||||
ui_->bitdepth->clear();
|
||||
ui_->bitrate->clear();
|
||||
ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
if (action_show_output_->isChecked()) {
|
||||
Engine::EngineType enginetype(Engine::None);
|
||||
if (app_->player()->engine()) enginetype = app_->player()->engine()->type();
|
||||
QIcon icon_engine = IconLoader::Load(EngineName(enginetype), 32);
|
||||
|
||||
ui_->label_engine->setVisible(true);
|
||||
ui_->label_engine->setMaximumSize(60, QWIDGETSIZE_MAX);
|
||||
ui_->label_engine_icon->setVisible(true);
|
||||
ui_->label_engine_icon->setPixmap(icon_engine.pixmap(icon_engine.availableSizes().last()));
|
||||
ui_->label_engine_icon->setMinimumSize(32, 32);
|
||||
ui_->label_engine_icon->setMaximumSize(32, 32);
|
||||
ui_->engine->setVisible(true);
|
||||
ui_->engine->setText(EngineDescription(enginetype));
|
||||
ui_->engine->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
ui_->spacer_play_output->changeSize(20, 20, QSizePolicy::Fixed);
|
||||
|
||||
DeviceFinder::Device device;
|
||||
for (DeviceFinder *f : app_->enginedevice()->device_finders_) {
|
||||
for (const DeviceFinder::Device &d : f->ListDevices()) {
|
||||
if (d.value != app_->player()->engine()->device()) continue;
|
||||
device = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (device.value.isValid()) {
|
||||
ui_->label_device->setVisible(true);
|
||||
ui_->label_device->setMaximumSize(60, QWIDGETSIZE_MAX);
|
||||
ui_->label_device_icon->setVisible(true);
|
||||
ui_->label_device_icon->setMinimumSize(32, 32);
|
||||
ui_->label_device_icon->setMaximumSize(32, 32);
|
||||
ui_->device->setVisible(true);
|
||||
ui_->device->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
QIcon icon_device = IconLoader::Load(device.iconname, 32);
|
||||
ui_->label_device_icon->setPixmap(icon_device.pixmap(icon_device.availableSizes().last()));
|
||||
ui_->device->setText(device.description);
|
||||
}
|
||||
else {
|
||||
ui_->label_device->setVisible(false);
|
||||
ui_->label_device->setMaximumSize(0, 0);
|
||||
ui_->label_device_icon->setVisible(false);
|
||||
ui_->label_device_icon->setMinimumSize(0, 0);
|
||||
ui_->label_device_icon->setMaximumSize(0, 0);
|
||||
ui_->label_device_icon->clear();
|
||||
ui_->device->clear();
|
||||
ui_->device->setVisible(false);
|
||||
ui_->device->setMaximumSize(0, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ui_->label_engine->setVisible(false);
|
||||
ui_->label_engine->setMaximumSize(0, 0);
|
||||
ui_->label_engine_icon->clear();
|
||||
ui_->label_engine_icon->setVisible(false);
|
||||
ui_->label_engine_icon->setMinimumSize(0, 0);
|
||||
ui_->label_engine_icon->setMaximumSize(0, 0);
|
||||
ui_->engine->clear();
|
||||
ui_->engine->setVisible(false);
|
||||
ui_->engine->setMaximumSize(0, 0);
|
||||
ui_->spacer_play_output->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
ui_->label_device->setVisible(false);
|
||||
ui_->label_device->setMaximumSize(0, 0);
|
||||
ui_->label_device_icon->setVisible(false);
|
||||
ui_->label_device_icon->setMinimumSize(0, 0);
|
||||
ui_->label_device_icon->setMaximumSize(0, 0);
|
||||
ui_->label_device_icon->clear();
|
||||
ui_->device->clear();
|
||||
ui_->device->setVisible(false);
|
||||
ui_->device->setMaximumSize(0, 0);
|
||||
}
|
||||
|
||||
if (action_show_albums_->isChecked() && prev_artist_ != song_.artist()) {
|
||||
const QueryOptions opt;
|
||||
CollectionBackend::AlbumList albumlist;
|
||||
ui_->widget_play_albums->albums_model()->Reset();
|
||||
albumlist = app_->collection_backend()->GetAlbumsByArtist(song_.artist(), opt);
|
||||
if (albumlist.count() > 1) {
|
||||
ui_->label_play_albums->setVisible(true);
|
||||
ui_->label_play_albums->setMinimumSize(0, 20);
|
||||
ui_->label_play_albums->setText(QString("Albums by %1").arg( song_.artist().toHtmlEscaped()));
|
||||
ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
|
||||
for (CollectionBackend::Album album : albumlist) {
|
||||
SongList songs = app_->collection_backend()->GetSongs(song_.artist(), album.album_name, opt);
|
||||
ui_->widget_play_albums->albums_model()->AddSongs(songs);
|
||||
}
|
||||
ui_->widget_play_albums->setEnabled(true);
|
||||
ui_->widget_play_albums->setVisible(true);
|
||||
ui_->spacer_play_albums1->changeSize(20, 10, QSizePolicy::Fixed);
|
||||
ui_->spacer_play_albums2->changeSize(20, 20, QSizePolicy::Fixed);
|
||||
}
|
||||
else {
|
||||
ui_->label_play_albums->clear();
|
||||
ui_->label_play_albums->setVisible(false);
|
||||
ui_->label_play_albums->setMinimumSize(0, 0);
|
||||
ui_->widget_play_albums->setEnabled(false);
|
||||
ui_->widget_play_albums->setVisible(false);
|
||||
ui_->spacer_play_albums1->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
ui_->spacer_play_albums2->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
}
|
||||
}
|
||||
else if (!action_show_albums_->isChecked()) {
|
||||
ui_->label_play_albums->clear();
|
||||
ui_->label_play_albums->setVisible(false);
|
||||
ui_->label_play_albums->setMinimumSize(0, 0);
|
||||
ui_->widget_play_albums->albums_model()->Reset();
|
||||
ui_->widget_play_albums->setEnabled(false);
|
||||
ui_->widget_play_albums->setVisible(false);
|
||||
ui_->spacer_play_albums1->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
ui_->spacer_play_albums2->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
if (action_show_lyrics_->isChecked()) {
|
||||
ui_->label_play_lyrics->setText(lyrics_);
|
||||
}
|
||||
else {
|
||||
ui_->label_play_lyrics->clear();
|
||||
}
|
||||
|
||||
ui_->widget_stacked->setCurrentWidget(ui_->widget_play);
|
||||
|
||||
}
|
||||
|
||||
void ContextView::UpdateLyrics(quint64 id, const QString lyrics) {
|
||||
|
||||
lyrics_ = lyrics;
|
||||
if (action_show_lyrics_->isChecked()) {
|
||||
ui_->label_play_lyrics->setText(lyrics);
|
||||
}
|
||||
else ui_->label_play_lyrics->clear();
|
||||
|
||||
}
|
||||
|
||||
bool ContextView::eventFilter(QObject *object, QEvent *event) {
|
||||
|
||||
switch(event->type()) {
|
||||
case QEvent::Paint:{
|
||||
handlePaintEvent(object, event);
|
||||
}
|
||||
default:{
|
||||
return QObject::eventFilter(object, event);
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
|
||||
}
|
||||
|
||||
void ContextView::handlePaintEvent(QObject *object, QEvent *event) {
|
||||
|
||||
if (object == ui_->label_play_album) {
|
||||
PaintEventAlbum(event);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void ContextView::PaintEventAlbum(QEvent *event) {
|
||||
|
||||
QPainter p(ui_->label_play_album);
|
||||
|
||||
DrawImage(&p);
|
||||
|
||||
// Draw the previous track's image if we're fading
|
||||
if (!pixmap_previous_.isNull()) {
|
||||
p.setOpacity(pixmap_previous_opacity_);
|
||||
p.drawPixmap(0, 0, pixmap_previous_);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextView::DrawImage(QPainter *p) {
|
||||
|
||||
p->drawPixmap(0, 0, 300, 300, pixmap_current_);
|
||||
if ((downloading_covers_) && (spinner_animation_)) {
|
||||
p->drawPixmap(50, 50, 16, 16, spinner_animation_->currentPixmap());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ContextView::FadePreviousTrack(qreal value) {
|
||||
|
||||
pixmap_previous_opacity_ = value;
|
||||
if (qFuzzyCompare(pixmap_previous_opacity_, qreal(0.0))) {
|
||||
image_previous_ = QImage();
|
||||
pixmap_previous_ = QPixmap();
|
||||
}
|
||||
update();
|
||||
|
||||
if (value == 0 && !active_) {
|
||||
ui_->widget_stacked->setCurrentWidget(ui_->widget_stop);
|
||||
NoSong();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ContextView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
if (menu_ && ui_->widget_stacked->currentWidget() == ui_->widget_play) menu_->popup(mapToGlobal(e->pos()));
|
||||
}
|
||||
|
||||
void ContextView::mouseReleaseEvent(QMouseEvent *) {
|
||||
}
|
||||
|
||||
void ContextView::dragEnterEvent(QDragEnterEvent *e) {
|
||||
QWidget::dragEnterEvent(e);
|
||||
}
|
||||
|
||||
void ContextView::dropEvent(QDropEvent *e) {
|
||||
QWidget::dropEvent(e);
|
||||
}
|
||||
|
||||
void ContextView::ScaleCover() {
|
||||
|
||||
pixmap_current_ = QPixmap::fromImage(AlbumCoverLoader::ScaleAndPad(cover_loader_options_, image_original_));
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) {
|
||||
|
||||
if (song == song_) {}
|
||||
else {
|
||||
qLog(Error) << __PRETTY_FUNCTION__ << "Ignoring" << song.title() << "because current song is" << song_.title();
|
||||
return;
|
||||
}
|
||||
|
||||
active_ = true;
|
||||
downloading_covers_ = false;
|
||||
SetImage(image);
|
||||
GetCoverAutomatically();
|
||||
|
||||
}
|
||||
|
||||
void ContextView::SetImage(const QImage &image) {
|
||||
|
||||
// Cache the current pixmap so we can fade between them
|
||||
pixmap_previous_ = QPixmap(size());
|
||||
pixmap_previous_.fill(palette().background().color());
|
||||
pixmap_previous_opacity_ = 1.0;
|
||||
|
||||
QPainter p(&pixmap_previous_);
|
||||
DrawImage(&p);
|
||||
p.end();
|
||||
|
||||
image_original_ = image;
|
||||
|
||||
ScaleCover();
|
||||
|
||||
// Were we waiting for this cover to load before we started fading?
|
||||
if (!pixmap_previous_.isNull() && timeline_fade_) {
|
||||
timeline_fade_->stop();
|
||||
timeline_fade_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0
|
||||
timeline_fade_->start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ContextView::GetCoverAutomatically() {
|
||||
|
||||
// Search for cover automatically?
|
||||
bool search = !song_.has_manually_unset_cover() && song_.art_automatic().isEmpty() && song_.art_manual().isEmpty() && !song_.artist().isEmpty() && !song_.album().isEmpty();
|
||||
|
||||
if (search) {
|
||||
downloading_covers_ = true;
|
||||
album_cover_choice_controller_->SearchCoverAutomatically(song_);
|
||||
|
||||
// Show a spinner animation
|
||||
spinner_animation_.reset(new QMovie(":/pictures/spinner.gif", QByteArray(), this));
|
||||
connect(spinner_animation_.get(), SIGNAL(updated(const QRect&)), SLOT(update()));
|
||||
spinner_animation_->start();
|
||||
update();
|
||||
}
|
||||
|
||||
return search;
|
||||
|
||||
}
|
||||
|
||||
void ContextView::AutomaticCoverSearchDone() {
|
||||
|
||||
downloading_covers_ = false;
|
||||
spinner_animation_.reset();
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
void ContextView::ActionShowData() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_data", action_show_data_->isChecked());
|
||||
s.endGroup();
|
||||
UpdateSong();
|
||||
}
|
||||
|
||||
void ContextView::ActionShowOutput() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_output", action_show_output_->isChecked());
|
||||
s.endGroup();
|
||||
UpdateSong();
|
||||
}
|
||||
|
||||
void ContextView::ActionShowAlbums() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_albums", action_show_albums_->isChecked());
|
||||
s.endGroup();
|
||||
prev_artist_ = QString();
|
||||
UpdateSong();
|
||||
}
|
||||
|
||||
void ContextView::ActionShowLyrics() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_lyrics", action_show_lyrics_->isChecked());
|
||||
s.endGroup();
|
||||
UpdateSong();
|
||||
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
|
||||
}
|
||||
|
||||
void ContextView::LoadCoverFromFile() {
|
||||
album_cover_choice_controller_->LoadCoverFromFile(&song_);
|
||||
}
|
||||
|
||||
void ContextView::LoadCoverFromURL() {
|
||||
album_cover_choice_controller_->LoadCoverFromURL(&song_);
|
||||
}
|
||||
|
||||
void ContextView::SearchForCover() {
|
||||
album_cover_choice_controller_->SearchForCover(&song_);
|
||||
}
|
||||
|
||||
void ContextView::SaveCoverToFile() {
|
||||
album_cover_choice_controller_->SaveCoverToFile(song_, image_original_);
|
||||
}
|
||||
|
||||
void ContextView::UnsetCover() {
|
||||
album_cover_choice_controller_->UnsetCover(&song_);
|
||||
}
|
||||
|
||||
void ContextView::ShowCover() {
|
||||
album_cover_choice_controller_->ShowCover(song_);
|
||||
}
|
||||
|
||||
void ContextView::SearchCoverAutomatically() {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("search_for_cover_auto", album_cover_choice_controller_->search_cover_auto_action()->isChecked());
|
||||
s.endGroup();
|
||||
|
||||
GetCoverAutomatically();
|
||||
|
||||
}
|
||||
164
src/context/contextview.h
Normal file
164
src/context/contextview.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONTEXTVIEW_H
|
||||
#define CONTEXTVIEW_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QMovie>
|
||||
#include <QTimeLine>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QLabel>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
|
||||
#include "ui_contextviewcontainer.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
class Application;
|
||||
class CollectionView;
|
||||
class CollectionModel;
|
||||
class AlbumCoverChoiceController;
|
||||
class Ui_ContextViewContainer;
|
||||
class ContextAlbumsView;
|
||||
class LyricsFetcher;
|
||||
|
||||
class ContextView : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ContextView(QWidget *parent = nullptr);
|
||||
~ContextView();
|
||||
|
||||
void SetApplication(Application *app);
|
||||
void SetCollectionView(CollectionView *collectionview);
|
||||
|
||||
ContextAlbumsView *albums() { return ui_->widget_play_albums; }
|
||||
|
||||
public slots:
|
||||
void UpdateNoSong();
|
||||
void Playing();
|
||||
void Stopped();
|
||||
void Error();
|
||||
void SongChanged(const Song &song);
|
||||
void AlbumArtLoaded(const Song &song, const QString &uri, const QImage &image);
|
||||
|
||||
void LoadCoverFromFile();
|
||||
void SaveCoverToFile();
|
||||
void LoadCoverFromURL();
|
||||
void SearchForCover();
|
||||
void UnsetCover();
|
||||
void ShowCover();
|
||||
void SearchCoverAutomatically();
|
||||
void AutomaticCoverSearchDone();
|
||||
void UpdateLyrics(quint64 id, const QString lyrics);
|
||||
|
||||
private:
|
||||
|
||||
enum WidgetState {
|
||||
//State_None = 0,
|
||||
State_Playing,
|
||||
State_Stopped
|
||||
};
|
||||
|
||||
static const char *kSettingsGroup;
|
||||
static const int kPadding;
|
||||
static const int kGradientHead;
|
||||
static const int kGradientTail;
|
||||
static const int kMaxCoverSize;
|
||||
static const int kBottomOffset;
|
||||
static const int kTopBorder;
|
||||
|
||||
Application *app_;
|
||||
Ui_ContextViewContainer *ui_;
|
||||
CollectionView *collectionview_;
|
||||
WidgetState widgetstate_;
|
||||
QMenu *menu_;
|
||||
QTimeLine *timeline_fade_;
|
||||
QImage image_strawberry_;
|
||||
AlbumCoverChoiceController *album_cover_choice_controller_;
|
||||
LyricsFetcher *lyrics_fetcher_;
|
||||
bool active_;
|
||||
bool downloading_covers_;
|
||||
|
||||
QAction *action_show_data_;
|
||||
QAction *action_show_output_;
|
||||
QAction *action_show_albums_;
|
||||
QAction *action_show_lyrics_;
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
Song song_;
|
||||
Song song_empty_;
|
||||
Song song_prev_;
|
||||
QImage image_original_;
|
||||
QImage image_previous_;
|
||||
QPixmap *pixmap_album_;
|
||||
QPixmap pixmap_current_;
|
||||
QPixmap pixmap_previous_;
|
||||
QPainter *painter_album_;
|
||||
qreal pixmap_previous_opacity_;
|
||||
std::unique_ptr<QMovie> spinner_animation_;
|
||||
|
||||
QString prev_artist_;
|
||||
QString lyrics_;
|
||||
|
||||
void LoadSettings();
|
||||
void AddActions();
|
||||
void SetText(QLabel *label, int value, const QString &suffix, const QString &def = QString());
|
||||
void NoSong();
|
||||
void UpdateSong();
|
||||
void SetImage(const QImage &image);
|
||||
void DrawImage(QPainter *p);
|
||||
void ScaleCover();
|
||||
bool GetCoverAutomatically();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *, QEvent *);
|
||||
void handlePaintEvent(QObject *object, QEvent *event);
|
||||
void PaintEventAlbum(QEvent *event);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *);
|
||||
void dragEnterEvent(QDragEnterEvent *e);
|
||||
void dropEvent(QDropEvent *e);
|
||||
|
||||
private slots:
|
||||
void ActionShowData();
|
||||
void ActionShowOutput();
|
||||
void ActionShowAlbums();
|
||||
void ActionShowLyrics();
|
||||
void FadePreviousTrack(qreal value);
|
||||
|
||||
};
|
||||
|
||||
#endif // CONTEXTVIEW_H
|
||||
|
||||
567
src/context/contextviewcontainer.ui
Normal file
567
src/context/contextviewcontainer.ui
Normal file
@@ -0,0 +1,567 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ContextViewContainer</class>
|
||||
<widget class="QWidget" name="ContextViewContainer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>927</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="layout_container">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="widget_stacked">
|
||||
<widget class="QWidget" name="widget_stop">
|
||||
<layout class="QVBoxLayout" name="widget_layout_stop">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="layout_stop">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollarea_stop">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_stop">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>923</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_stop_top">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No song playing</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_stop_logo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../data/data.qrc">:/pictures/strawberry.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_stop_summary">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_stop_bottom">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>53</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="widget_play">
|
||||
<layout class="QVBoxLayout" name="widget_layout_play">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="layout_play">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollarea_play">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_play">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>923</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_play_top">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_play_album">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_play">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="layout_play_data">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_bitdepth">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bit depth</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="samplerate">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="length">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_length">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="filetype">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_samplerate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Samplerate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="bitdepth">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_filetype">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Filetype</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_bitrate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="bitrate">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_play_data">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="layout_play_grid">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_device_icon">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="device">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="engine">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_engine_icon">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_engine">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Engine</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_device">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_play_output">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_play_albums">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>6</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_play_albums1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ContextAlbumsView" name="widget_play_albums" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_play_albums2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_play_lyrics">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_play_bottom">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ContextAlbumsView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>context/contextalbumsview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user