Album cover art manager. So far this only displays cover art that was loaded from disk.
This commit is contained in:
parent
f9ad923f3a
commit
12273256e5
@ -62,5 +62,7 @@
|
|||||||
<file>schema-1.sql</file>
|
<file>schema-1.sql</file>
|
||||||
<file>schema-2.sql</file>
|
<file>schema-2.sql</file>
|
||||||
<file>nocover.png</file>
|
<file>nocover.png</file>
|
||||||
|
<file>view-choose.png</file>
|
||||||
|
<file>download.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
BIN
data/download.png
Normal file
BIN
data/download.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
data/view-choose.png
Normal file
BIN
data/view-choose.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 409 B |
@ -52,6 +52,8 @@ set(CLEMENTINE-SOURCES
|
|||||||
savedradio.cpp
|
savedradio.cpp
|
||||||
stylesheetloader.cpp
|
stylesheetloader.cpp
|
||||||
shortcutsdialog.cpp
|
shortcutsdialog.cpp
|
||||||
|
albumcovermanager.cpp
|
||||||
|
albumcoverloader.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header files that have Q_OBJECT in
|
# Header files that have Q_OBJECT in
|
||||||
@ -96,6 +98,8 @@ set(CLEMENTINE-MOC-HEADERS
|
|||||||
addstreamdialog.h
|
addstreamdialog.h
|
||||||
savedradio.h
|
savedradio.h
|
||||||
shortcutsdialog.h
|
shortcutsdialog.h
|
||||||
|
albumcovermanager.h
|
||||||
|
albumcoverloader.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# UI files
|
# UI files
|
||||||
@ -114,6 +118,7 @@ set(CLEMENTINE-UI
|
|||||||
about.ui
|
about.ui
|
||||||
addstreamdialog.ui
|
addstreamdialog.ui
|
||||||
shortcutsdialog.ui
|
shortcutsdialog.ui
|
||||||
|
albumcovermanager.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
# Resource files
|
# Resource files
|
||||||
|
70
src/albumcoverloader.cpp
Normal file
70
src/albumcoverloader.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include "albumcoverloader.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
AlbumCoverLoader::AlbumCoverLoader(QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
height_(120),
|
||||||
|
next_id_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverLoader::Clear() {
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
tasks_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 AlbumCoverLoader::LoadImageAsync(const QString& art_automatic,
|
||||||
|
const QString& art_manual) {
|
||||||
|
Task task;
|
||||||
|
task.art_automatic = art_automatic;
|
||||||
|
task.art_manual = art_manual;
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
task.id = next_id_ ++;
|
||||||
|
tasks_.enqueue(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
metaObject()->invokeMethod(this, "ProcessTasks", Qt::QueuedConnection);
|
||||||
|
|
||||||
|
return task.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverLoader::ProcessTasks() {
|
||||||
|
forever {
|
||||||
|
// Get the next task
|
||||||
|
Task task;
|
||||||
|
{
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
if (tasks_.isEmpty())
|
||||||
|
return;
|
||||||
|
task = tasks_.dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load the image
|
||||||
|
QImage image;
|
||||||
|
if (!task.art_manual.isEmpty())
|
||||||
|
image.load(task.art_manual);
|
||||||
|
if (!task.art_automatic.isEmpty() && image.isNull())
|
||||||
|
image.load(task.art_automatic);
|
||||||
|
|
||||||
|
if (!image.isNull()) {
|
||||||
|
// Scale the image down
|
||||||
|
image = image.scaled(QSize(height_, height_), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
// Pad the image to height_ x height_
|
||||||
|
QImage bigger_image(height_, height_, QImage::Format_ARGB32);
|
||||||
|
bigger_image.fill(0);
|
||||||
|
|
||||||
|
QPainter p(&bigger_image);
|
||||||
|
p.drawImage((height_ - image.width()) / 2, (height_ - image.height()) / 2,
|
||||||
|
image);
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
image = bigger_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit ImageLoaded(task.id, image);
|
||||||
|
}
|
||||||
|
}
|
42
src/albumcoverloader.h
Normal file
42
src/albumcoverloader.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef ALBUMCOVERLOADER_H
|
||||||
|
#define ALBUMCOVERLOADER_H
|
||||||
|
|
||||||
|
#include "backgroundthread.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
class AlbumCoverLoader : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AlbumCoverLoader(QObject* parent = 0);
|
||||||
|
|
||||||
|
void SetDesiredHeight(int height) { height_ = height; }
|
||||||
|
quint64 LoadImageAsync(const QString& art_automatic, const QString& art_manual);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ImageLoaded(quint64 id, const QImage& image);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ProcessTasks();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Task {
|
||||||
|
quint64 id;
|
||||||
|
QString art_automatic;
|
||||||
|
QString art_manual;
|
||||||
|
};
|
||||||
|
|
||||||
|
int height_;
|
||||||
|
|
||||||
|
QMutex mutex_;
|
||||||
|
QQueue<Task> tasks_;
|
||||||
|
quint64 next_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALBUMCOVERLOADER_H
|
160
src/albumcovermanager.cpp
Normal file
160
src/albumcovermanager.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include "albumcovermanager.h"
|
||||||
|
#include "librarybackend.h"
|
||||||
|
#include "libraryquery.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QActionGroup>
|
||||||
|
|
||||||
|
const char* AlbumCoverManager::kSettingsGroup = "CoverManager";
|
||||||
|
|
||||||
|
AlbumCoverManager::AlbumCoverManager(QWidget *parent)
|
||||||
|
: QDialog(parent),
|
||||||
|
cover_loader_(new BackgroundThread<AlbumCoverLoader>(this)),
|
||||||
|
artist_icon_(":/artist.png"),
|
||||||
|
all_artists_icon_(":/album.png")
|
||||||
|
{
|
||||||
|
ui_.setupUi(this);
|
||||||
|
|
||||||
|
// Get a square version of nocover.png
|
||||||
|
QImage nocover(":/nocover.png");
|
||||||
|
nocover = nocover.scaled(120, 120, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
QImage square_nocover(120, 120, QImage::Format_ARGB32);
|
||||||
|
square_nocover.fill(0);
|
||||||
|
QPainter p(&square_nocover);
|
||||||
|
p.drawImage((120 - nocover.width()) / 2, (120 - nocover.height()) / 2, nocover);
|
||||||
|
p.end();
|
||||||
|
no_cover_icon_ = QPixmap::fromImage(square_nocover);
|
||||||
|
|
||||||
|
// View menu
|
||||||
|
QActionGroup* filter_group = new QActionGroup(this);
|
||||||
|
filter_all_ = filter_group->addAction("All albums");
|
||||||
|
filter_with_covers_ = filter_group->addAction("Albums with covers");
|
||||||
|
filter_without_covers_ = filter_group->addAction("Albums without covers");
|
||||||
|
filter_all_->setCheckable(true);
|
||||||
|
filter_with_covers_->setCheckable(true);
|
||||||
|
filter_without_covers_->setCheckable(true);
|
||||||
|
filter_group->setExclusive(true);
|
||||||
|
filter_all_->setChecked(true);
|
||||||
|
|
||||||
|
QMenu* view_menu = new QMenu(this);
|
||||||
|
view_menu->addActions(filter_group->actions());
|
||||||
|
|
||||||
|
ui_.view->setMenu(view_menu);
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
connect(cover_loader_, SIGNAL(Initialised()), SLOT(CoverLoaderInitialised()));
|
||||||
|
connect(ui_.artists, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
||||||
|
SLOT(ArtistChanged(QListWidgetItem*)));
|
||||||
|
connect(ui_.filter, SIGNAL(textChanged(QString)), SLOT(UpdateFilter()));
|
||||||
|
connect(filter_group, SIGNAL(triggered(QAction*)), SLOT(UpdateFilter()));
|
||||||
|
connect(ui_.view, SIGNAL(clicked()), ui_.view, SLOT(showMenu()));
|
||||||
|
|
||||||
|
// Restore settings
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
|
restoreGeometry(s.value("geometry").toByteArray());
|
||||||
|
if (!ui_.splitter->restoreState(s.value("splitter_state").toByteArray())) {
|
||||||
|
// Sensible default size for the artists view
|
||||||
|
ui_.splitter->setSizes(QList<int>() << 200 << width() - 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
cover_loader_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::CoverLoaderInitialised() {
|
||||||
|
connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)),
|
||||||
|
SLOT(CoverImageLoaded(quint64,QImage)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::SetBackend(boost::shared_ptr<LibraryBackend> backend) {
|
||||||
|
backend_ = backend;
|
||||||
|
|
||||||
|
if (isVisible())
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::showEvent(QShowEvent *) {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::closeEvent(QCloseEvent *) {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
|
s.setValue("geometry", saveGeometry());
|
||||||
|
s.setValue("splitter_state", ui_.splitter->saveState());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::Reset() {
|
||||||
|
if (!backend_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ui_.artists->clear();
|
||||||
|
new QListWidgetItem(all_artists_icon_, "All artists", ui_.artists, All_Artists);
|
||||||
|
|
||||||
|
foreach (const QString& artist, backend_->GetAllArtists()) {
|
||||||
|
if (artist.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
new QListWidgetItem(artist_icon_, artist, ui_.artists, Specific_Artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::ArtistChanged(QListWidgetItem* current) {
|
||||||
|
if (!backend_ || !cover_loader_->Worker())
|
||||||
|
return;
|
||||||
|
if (!current)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString artist;
|
||||||
|
if (current->type() == Specific_Artist)
|
||||||
|
artist = current->text();
|
||||||
|
|
||||||
|
ui_.albums->clear();
|
||||||
|
cover_loading_tasks_.clear();
|
||||||
|
cover_loader_->Worker()->Clear();
|
||||||
|
|
||||||
|
foreach (const LibraryBackend::AlbumArtInfo& info, backend_->GetAlbumArtInfo(artist)) {
|
||||||
|
QListWidgetItem* item = new QListWidgetItem(no_cover_icon_, info.album_name, ui_.albums);
|
||||||
|
|
||||||
|
if (!info.art_automatic.isEmpty() || !info.art_manual.isEmpty()) {
|
||||||
|
quint64 id = cover_loader_->Worker()->LoadImageAsync(
|
||||||
|
info.art_automatic, info.art_manual);
|
||||||
|
cover_loading_tasks_[id] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::CoverImageLoaded(quint64 id, const QImage &image) {
|
||||||
|
if (!cover_loading_tasks_.contains(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QListWidgetItem* item = cover_loading_tasks_.take(id);
|
||||||
|
|
||||||
|
if (image.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
item->setIcon(QPixmap::fromImage(image));
|
||||||
|
UpdateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlbumCoverManager::UpdateFilter() {
|
||||||
|
const QString filter = ui_.filter->text().toLower();
|
||||||
|
const bool hide_with_covers = filter_without_covers_->isChecked();
|
||||||
|
const bool hide_without_covers = filter_with_covers_->isChecked();
|
||||||
|
|
||||||
|
for (int i=0 ; i<ui_.albums->count() ; ++i) {
|
||||||
|
QListWidgetItem* item = ui_.albums->item(i);
|
||||||
|
QString text = item->text();
|
||||||
|
bool has_cover = item->icon().cacheKey() != no_cover_icon_.cacheKey();
|
||||||
|
|
||||||
|
item->setHidden((!filter.isEmpty() && !text.toLower().contains(filter)) ||
|
||||||
|
(has_cover && hide_with_covers) ||
|
||||||
|
(!has_cover && hide_without_covers));
|
||||||
|
}
|
||||||
|
}
|
59
src/albumcovermanager.h
Normal file
59
src/albumcovermanager.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef ALBUMCOVERMANAGER_H
|
||||||
|
#define ALBUMCOVERMANAGER_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "ui_covermanager.h"
|
||||||
|
#include "backgroundthread.h"
|
||||||
|
#include "albumcoverloader.h"
|
||||||
|
|
||||||
|
class LibraryBackend;
|
||||||
|
|
||||||
|
class AlbumCoverManager : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AlbumCoverManager(QWidget *parent = 0);
|
||||||
|
|
||||||
|
static const char* kSettingsGroup;
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void SetBackend(boost::shared_ptr<LibraryBackend> backend);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *);
|
||||||
|
void closeEvent(QCloseEvent *);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ArtistChanged(QListWidgetItem* current);
|
||||||
|
void CoverLoaderInitialised();
|
||||||
|
void CoverImageLoaded(quint64 id, const QImage& image);
|
||||||
|
void UpdateFilter();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum ArtistItemType {
|
||||||
|
All_Artists,
|
||||||
|
Specific_Artist,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::CoverManager ui_;
|
||||||
|
boost::shared_ptr<LibraryBackend> backend_;
|
||||||
|
|
||||||
|
QAction* filter_all_;
|
||||||
|
QAction* filter_with_covers_;
|
||||||
|
QAction* filter_without_covers_;
|
||||||
|
|
||||||
|
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||||
|
QMap<quint64, QListWidgetItem*> cover_loading_tasks_;
|
||||||
|
|
||||||
|
QIcon artist_icon_;
|
||||||
|
QIcon all_artists_icon_;
|
||||||
|
QIcon no_cover_icon_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALBUMCOVERMANAGER_H
|
175
src/albumcovermanager.ui
Normal file
175
src/albumcovermanager.ui
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>CoverManager</class>
|
||||||
|
<widget class="QDialog" name="CoverManager">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>827</width>
|
||||||
|
<height>662</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Cover Manager</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QListWidget" name="artists">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="layoutWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="clear">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../data/data.qrc">
|
||||||
|
<normaloff>:/clear.png</normaloff>:/clear.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="autoRaise">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="LineEdit" name="filter">
|
||||||
|
<property name="hint" stdset="0">
|
||||||
|
<string>Enter search terms here</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="view">
|
||||||
|
<property name="text">
|
||||||
|
<string>View</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../data/data.qrc">
|
||||||
|
<normaloff>:/view-choose.png</normaloff>:/view-choose.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="popupMode">
|
||||||
|
<enum>QToolButton::MenuButtonPopup</enum>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="autoRaise">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="fetch">
|
||||||
|
<property name="text">
|
||||||
|
<string>Fetch Missing Covers</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../data/data.qrc">
|
||||||
|
<normaloff>:/download.png</normaloff>:/download.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="albums">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>120</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="verticalScrollMode">
|
||||||
|
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="flow">
|
||||||
|
<enum>QListView::LeftToRight</enum>
|
||||||
|
</property>
|
||||||
|
<property name="isWrapping" stdset="0">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="resizeMode">
|
||||||
|
<enum>QListView::Adjust</enum>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="viewMode">
|
||||||
|
<enum>QListView::IconMode</enum>
|
||||||
|
</property>
|
||||||
|
<property name="uniformItemSizes">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>LineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>lineedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources>
|
||||||
|
<include location="../data/data.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>clear</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>filter</receiver>
|
||||||
|
<slot>clear()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>329</x>
|
||||||
|
<y>13</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>367</x>
|
||||||
|
<y>14</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>clear</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>filter</receiver>
|
||||||
|
<slot>setFocus()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>334</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>401</x>
|
||||||
|
<y>13</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -567,6 +567,10 @@ p, li { white-space: pre-wrap; }
|
|||||||
<source>&Hide tray icon</source>
|
<source>&Hide tray icon</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Configure &Global Shortcuts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MultiLoadingIndicator</name>
|
<name>MultiLoadingIndicator</name>
|
||||||
@ -768,6 +772,109 @@ p, li { white-space: pre-wrap; }
|
|||||||
<source>Show a notification when I change the volume</source>
|
<source>Show a notification when I change the volume</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Include album art in the notification</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ShortcutsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Configure Shortcuts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Play</source>
|
||||||
|
<translation type="unfinished">Αναπαραγωγή</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pause</source>
|
||||||
|
<translation type="unfinished">Παύση</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Play/Pause</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stop</source>
|
||||||
|
<translation type="unfinished">Σταμάτημα</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stop Playing After Current Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Next Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Previous Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Increase Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Decrease Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mute Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seek Forwards</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seek Backwards</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shortcut</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Alternate</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Defaults</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&OK</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Cancel</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shortcut for Selected Action</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>De&fault</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Custom</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Non&e</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Default key:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SomaFMService</name>
|
<name>SomaFMService</name>
|
||||||
|
@ -550,6 +550,10 @@ p, li { white-space: pre-wrap; }
|
|||||||
<source>&Hide tray icon</source>
|
<source>&Hide tray icon</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Configure &Global Shortcuts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MultiLoadingIndicator</name>
|
<name>MultiLoadingIndicator</name>
|
||||||
@ -751,6 +755,109 @@ p, li { white-space: pre-wrap; }
|
|||||||
<source>Show a notification when I change the volume</source>
|
<source>Show a notification when I change the volume</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Include album art in the notification</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ShortcutsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Configure Shortcuts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Play</source>
|
||||||
|
<translation type="unfinished">Reproducir</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pause</source>
|
||||||
|
<translation type="unfinished">Pausa</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Play/Pause</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stop</source>
|
||||||
|
<translation type="unfinished">Detener</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stop Playing After Current Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Next Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Previous Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Increase Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Decrease Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mute Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seek Forwards</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seek Backwards</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shortcut</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Alternate</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Defaults</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&OK</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Cancel</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shortcut for Selected Action</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>De&fault</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Custom</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Non&e</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Default key:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SomaFMService</name>
|
<name>SomaFMService</name>
|
||||||
|
@ -545,6 +545,10 @@ p, li { white-space: pre-wrap; }
|
|||||||
<source>&Hide tray icon</source>
|
<source>&Hide tray icon</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Configure &Global Shortcuts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MultiLoadingIndicator</name>
|
<name>MultiLoadingIndicator</name>
|
||||||
@ -746,6 +750,109 @@ p, li { white-space: pre-wrap; }
|
|||||||
<source>Show a notification when I change the volume</source>
|
<source>Show a notification when I change the volume</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Include album art in the notification</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ShortcutsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Configure Shortcuts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Play</source>
|
||||||
|
<translation type="unfinished">Воспроизвести</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pause</source>
|
||||||
|
<translation type="unfinished">Пауза</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Play/Pause</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stop</source>
|
||||||
|
<translation type="unfinished">Стоп</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stop Playing After Current Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Next Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Previous Track</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Increase Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Decrease Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mute Volume</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seek Forwards</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Seek Backwards</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shortcut</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Alternate</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Defaults</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&OK</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Cancel</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shortcut for Selected Action</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>De&fault</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Custom</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Non&e</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Default key:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SomaFMService</name>
|
<name>SomaFMService</name>
|
||||||
|
@ -45,6 +45,8 @@ void Library::BackendInitialised() {
|
|||||||
|
|
||||||
dir_model_->SetBackend(backend_->Worker());
|
dir_model_->SetBackend(backend_->Worker());
|
||||||
|
|
||||||
|
emit BackendReady(backend_->Worker());
|
||||||
|
|
||||||
if (--waiting_for_threads_ == 0)
|
if (--waiting_for_threads_ == 0)
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
@ -339,17 +341,17 @@ void Library::LazyPopulate(LibraryItem* item) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryItem::Type_CompilationAlbum:
|
case LibraryItem::Type_CompilationAlbum:
|
||||||
foreach (const Song& song, backend_->Worker()->GetCompilationSongs(query_options_, item->key))
|
foreach (const Song& song, backend_->Worker()->GetCompilationSongs(item->key, query_options_))
|
||||||
CreateSongNode(false, song, item);
|
CreateSongNode(false, song, item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryItem::Type_Artist:
|
case LibraryItem::Type_Artist:
|
||||||
foreach (const QString& album, backend_->Worker()->GetAlbumsByArtist(query_options_, item->key))
|
foreach (const QString& album, backend_->Worker()->GetAlbumsByArtist(item->key, query_options_))
|
||||||
CreateAlbumNode(false, album, item, false);
|
CreateAlbumNode(false, album, item, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryItem::Type_Album:
|
case LibraryItem::Type_Album:
|
||||||
foreach (const Song& song, backend_->Worker()->GetSongs(query_options_, item->parent->key, item->key))
|
foreach (const Song& song, backend_->Worker()->GetSongs(item->parent->key, item->key, query_options_))
|
||||||
CreateSongNode(false, song, item);
|
CreateSongNode(false, song, item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ class Library : public SimpleTreeModel<LibraryItem> {
|
|||||||
void ScanStarted();
|
void ScanStarted();
|
||||||
void ScanFinished();
|
void ScanFinished();
|
||||||
|
|
||||||
|
void BackendReady(boost::shared_ptr<LibraryBackend> backend);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetFilterAge(int age);
|
void SetFilterAge(int age);
|
||||||
void SetFilterText(const QString& text);
|
void SetFilterText(const QString& text);
|
||||||
|
@ -321,11 +321,17 @@ QStringList LibraryBackend::GetAllArtists(const QueryOptions& opt) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList LibraryBackend::GetAlbumsByArtist(const QueryOptions& opt, const QString& artist) {
|
QStringList LibraryBackend::GetAllAlbums(const QueryOptions &opt) {
|
||||||
|
return GetAlbumsByArtist(QString(), opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LibraryBackend::GetAlbumsByArtist(const QString& artist, const QueryOptions& opt) {
|
||||||
LibraryQuery query(opt);
|
LibraryQuery query(opt);
|
||||||
query.SetColumnSpec("DISTINCT album");
|
query.SetColumnSpec("DISTINCT album");
|
||||||
query.AddCompilationRequirement(false);
|
query.AddCompilationRequirement(false);
|
||||||
query.AddWhere("artist", artist);
|
|
||||||
|
if (!artist.isNull())
|
||||||
|
query.AddWhere("artist", artist);
|
||||||
|
|
||||||
QSqlQuery q(query.Query(Connect()));
|
QSqlQuery q(query.Query(Connect()));
|
||||||
q.exec();
|
q.exec();
|
||||||
@ -338,7 +344,7 @@ QStringList LibraryBackend::GetAlbumsByArtist(const QueryOptions& opt, const QSt
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList LibraryBackend::GetSongs(const QueryOptions& opt, const QString& artist, const QString& album) {
|
SongList LibraryBackend::GetSongs(const QString& artist, const QString& album, const QueryOptions& opt) {
|
||||||
LibraryQuery query(opt);
|
LibraryQuery query(opt);
|
||||||
query.SetColumnSpec("ROWID, " + QString(Song::kColumnSpec));
|
query.SetColumnSpec("ROWID, " + QString(Song::kColumnSpec));
|
||||||
query.AddCompilationRequirement(false);
|
query.AddCompilationRequirement(false);
|
||||||
@ -402,7 +408,7 @@ QStringList LibraryBackend::GetCompilationAlbums(const QueryOptions& opt) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SongList LibraryBackend::GetCompilationSongs(const QueryOptions& opt, const QString& album) {
|
SongList LibraryBackend::GetCompilationSongs(const QString& album, const QueryOptions& opt) {
|
||||||
LibraryQuery query(opt);
|
LibraryQuery query(opt);
|
||||||
query.SetColumnSpec("ROWID, " + QString(Song::kColumnSpec));
|
query.SetColumnSpec("ROWID, " + QString(Song::kColumnSpec));
|
||||||
query.AddCompilationRequirement(true);
|
query.AddCompilationRequirement(true);
|
||||||
@ -515,4 +521,33 @@ void LibraryBackend::UpdateCompilations(QSqlQuery& find_songs, QSqlQuery& update
|
|||||||
CheckErrors(update.lastError());
|
CheckErrors(update.lastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<LibraryBackend::AlbumArtInfo>
|
||||||
|
LibraryBackend::GetAlbumArtInfo(const QString& artist,
|
||||||
|
const QueryOptions& opt) {
|
||||||
|
QList<AlbumArtInfo> ret;
|
||||||
|
LibraryQuery query(opt);
|
||||||
|
query.SetColumnSpec("album, art_automatic, art_manual");
|
||||||
|
query.SetOrderBy("album");
|
||||||
|
|
||||||
|
if (!artist.isNull())
|
||||||
|
query.AddWhere("artist", artist);
|
||||||
|
|
||||||
|
QSqlQuery q(query.Query(Connect()));
|
||||||
|
q.exec();
|
||||||
|
if (CheckErrors(q.lastError())) return ret;
|
||||||
|
|
||||||
|
QString last_album;
|
||||||
|
while (q.next()) {
|
||||||
|
if (q.value(0).toString() == last_album)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AlbumArtInfo info;
|
||||||
|
info.album_name = q.value(0).toString();
|
||||||
|
info.art_automatic = q.value(1).toString();
|
||||||
|
info.art_manual = q.value(2).toString();
|
||||||
|
ret << info;
|
||||||
|
|
||||||
|
last_album = info.album_name;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
#include "directory.h"
|
#include "directory.h"
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
#include "libraryquery.h"
|
||||||
struct QueryOptions;
|
|
||||||
|
|
||||||
class LibraryBackend : public QObject {
|
class LibraryBackend : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -18,6 +17,12 @@ class LibraryBackend : public QObject {
|
|||||||
public:
|
public:
|
||||||
LibraryBackend(QObject* parent = 0);
|
LibraryBackend(QObject* parent = 0);
|
||||||
|
|
||||||
|
struct AlbumArtInfo {
|
||||||
|
QString album_name;
|
||||||
|
QString art_automatic;
|
||||||
|
QString art_manual;
|
||||||
|
};
|
||||||
|
|
||||||
// This actually refers to the location of the sqlite database
|
// This actually refers to the location of the sqlite database
|
||||||
static QString DefaultDirectory();
|
static QString DefaultDirectory();
|
||||||
|
|
||||||
@ -29,13 +34,16 @@ class LibraryBackend : public QObject {
|
|||||||
|
|
||||||
SongList FindSongsInDirectory(int id);
|
SongList FindSongsInDirectory(int id);
|
||||||
|
|
||||||
QStringList GetAllArtists(const QueryOptions& opt);
|
QStringList GetAllArtists(const QueryOptions& opt = QueryOptions());
|
||||||
QStringList GetAlbumsByArtist(const QueryOptions& opt, const QString& artist);
|
QStringList GetAllAlbums(const QueryOptions& opt = QueryOptions());
|
||||||
SongList GetSongs(const QueryOptions& opt, const QString& artist, const QString& album);
|
QStringList GetAlbumsByArtist(const QString& artist, const QueryOptions& opt = QueryOptions());
|
||||||
|
SongList GetSongs(const QString& artist, const QString& album, const QueryOptions& opt = QueryOptions());
|
||||||
|
|
||||||
bool HasCompilations(const QueryOptions& opt);
|
bool HasCompilations(const QueryOptions& opt = QueryOptions());
|
||||||
QStringList GetCompilationAlbums(const QueryOptions& opt);
|
QStringList GetCompilationAlbums(const QueryOptions& opt = QueryOptions());
|
||||||
SongList GetCompilationSongs(const QueryOptions& opt, const QString& album);
|
SongList GetCompilationSongs(const QString& album, const QueryOptions& opt = QueryOptions());
|
||||||
|
|
||||||
|
QList<AlbumArtInfo> GetAlbumArtInfo(const QString& artist = QString(), const QueryOptions& opt = QueryOptions());
|
||||||
|
|
||||||
Song GetSongById(int id);
|
Song GetSongById(int id);
|
||||||
|
|
||||||
|
@ -53,6 +53,9 @@ QSqlQuery LibraryQuery::Query(QSqlDatabase db) const {
|
|||||||
if (!where_clauses_.isEmpty())
|
if (!where_clauses_.isEmpty())
|
||||||
sql += " WHERE " + where_clauses_.join(" AND ");
|
sql += " WHERE " + where_clauses_.join(" AND ");
|
||||||
|
|
||||||
|
if (!order_by_.isEmpty())
|
||||||
|
sql += " ORDER BY " + order_by_;
|
||||||
|
|
||||||
QSqlQuery q(sql, db);
|
QSqlQuery q(sql, db);
|
||||||
|
|
||||||
// Bind values
|
// Bind values
|
||||||
|
@ -24,6 +24,7 @@ class LibraryQuery {
|
|||||||
LibraryQuery(const QueryOptions& options);
|
LibraryQuery(const QueryOptions& options);
|
||||||
|
|
||||||
void SetColumnSpec(const QString& spec) { column_spec_ = spec; }
|
void SetColumnSpec(const QString& spec) { column_spec_ = spec; }
|
||||||
|
void SetOrderBy(const QString& order_by) { order_by_ = order_by; }
|
||||||
void AddWhere(const QString& column, const QVariant& value);
|
void AddWhere(const QString& column, const QVariant& value);
|
||||||
void AddCompilationRequirement(bool compilation);
|
void AddCompilationRequirement(bool compilation);
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ class LibraryQuery {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QString column_spec_;
|
QString column_spec_;
|
||||||
|
QString order_by_;
|
||||||
QStringList where_clauses_;
|
QStringList where_clauses_;
|
||||||
QVariantList bound_values_;
|
QVariantList bound_values_;
|
||||||
};
|
};
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "about.h"
|
#include "about.h"
|
||||||
#include "addstreamdialog.h"
|
#include "addstreamdialog.h"
|
||||||
#include "stylesheetloader.h"
|
#include "stylesheetloader.h"
|
||||||
|
#include "albumcovermanager.h"
|
||||||
|
|
||||||
#include "qxtglobalshortcut.h"
|
#include "qxtglobalshortcut.h"
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
settings_dialog_(new SettingsDialog(this)),
|
settings_dialog_(new SettingsDialog(this)),
|
||||||
add_stream_dialog_(new AddStreamDialog(this)),
|
add_stream_dialog_(new AddStreamDialog(this)),
|
||||||
shortcuts_dialog_(new ShortcutsDialog(this)),
|
shortcuts_dialog_(new ShortcutsDialog(this)),
|
||||||
|
cover_manager_(new AlbumCoverManager(this)),
|
||||||
playlist_menu_(new QMenu(this)),
|
playlist_menu_(new QMenu(this)),
|
||||||
library_sort_model_(new QSortFilterProxyModel(this)),
|
library_sort_model_(new QSortFilterProxyModel(this)),
|
||||||
track_position_timer_(new QTimer(this))
|
track_position_timer_(new QTimer(this))
|
||||||
@ -114,6 +116,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
connect(ui_.action_add_stream, SIGNAL(triggered()), SLOT(AddStream()));
|
connect(ui_.action_add_stream, SIGNAL(triggered()), SLOT(AddStream()));
|
||||||
connect(ui_.action_hide_tray_icon, SIGNAL(triggered()), SLOT(HideShowTrayIcon()));
|
connect(ui_.action_hide_tray_icon, SIGNAL(triggered()), SLOT(HideShowTrayIcon()));
|
||||||
connect(ui_.action_global_shortcuts, SIGNAL(triggered()), shortcuts_dialog_, SLOT(show()));
|
connect(ui_.action_global_shortcuts, SIGNAL(triggered()), shortcuts_dialog_, SLOT(show()));
|
||||||
|
connect(ui_.action_cover_manager, SIGNAL(triggered()), cover_manager_, SLOT(show()));
|
||||||
|
|
||||||
// Give actions to buttons
|
// Give actions to buttons
|
||||||
ui_.forward_button->setDefaultAction(ui_.action_next_track);
|
ui_.forward_button->setDefaultAction(ui_.action_next_track);
|
||||||
@ -165,6 +168,8 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
connect(library_, SIGNAL(TotalSongCountUpdated(int)), ui_.library_view, SLOT(TotalSongCountUpdated(int)));
|
connect(library_, SIGNAL(TotalSongCountUpdated(int)), ui_.library_view, SLOT(TotalSongCountUpdated(int)));
|
||||||
connect(library_, SIGNAL(ScanStarted()), SLOT(LibraryScanStarted()));
|
connect(library_, SIGNAL(ScanStarted()), SLOT(LibraryScanStarted()));
|
||||||
connect(library_, SIGNAL(ScanFinished()), SLOT(LibraryScanFinished()));
|
connect(library_, SIGNAL(ScanFinished()), SLOT(LibraryScanFinished()));
|
||||||
|
connect(library_, SIGNAL(BackendReady(boost::shared_ptr<LibraryBackend>)),
|
||||||
|
cover_manager_, SLOT(SetBackend(boost::shared_ptr<LibraryBackend>)));
|
||||||
|
|
||||||
// Age filters
|
// Age filters
|
||||||
QActionGroup* filter_age_group = new QActionGroup(this);
|
QActionGroup* filter_age_group = new QActionGroup(this);
|
||||||
|
@ -22,6 +22,7 @@ class SettingsDialog;
|
|||||||
class About;
|
class About;
|
||||||
class AddStreamDialog;
|
class AddStreamDialog;
|
||||||
class ShortcutsDialog;
|
class ShortcutsDialog;
|
||||||
|
class AlbumCoverManager;
|
||||||
|
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
class SystemTrayIcon;
|
class SystemTrayIcon;
|
||||||
@ -104,6 +105,7 @@ class MainWindow : public QMainWindow {
|
|||||||
SettingsDialog* settings_dialog_;
|
SettingsDialog* settings_dialog_;
|
||||||
AddStreamDialog* add_stream_dialog_;
|
AddStreamDialog* add_stream_dialog_;
|
||||||
ShortcutsDialog* shortcuts_dialog_;
|
ShortcutsDialog* shortcuts_dialog_;
|
||||||
|
AlbumCoverManager* cover_manager_;
|
||||||
|
|
||||||
QMenu* playlist_menu_;
|
QMenu* playlist_menu_;
|
||||||
QAction* playlist_play_pause_;
|
QAction* playlist_play_pause_;
|
||||||
|
@ -309,7 +309,11 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="library_filter"/>
|
<widget class="LineEdit" name="library_filter">
|
||||||
|
<property name="hint" stdset="0">
|
||||||
|
<string>Enter search terms here</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="library_options">
|
<widget class="QToolButton" name="library_options">
|
||||||
@ -353,6 +357,9 @@
|
|||||||
<attribute name="headerVisible">
|
<attribute name="headerVisible">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -434,7 +441,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>804</width>
|
<width>804</width>
|
||||||
<height>24</height>
|
<height>21</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuMusic">
|
<widget class="QMenu" name="menuMusic">
|
||||||
@ -478,8 +485,15 @@
|
|||||||
</property>
|
</property>
|
||||||
<addaction name="action_about"/>
|
<addaction name="action_about"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuTools">
|
||||||
|
<property name="title">
|
||||||
|
<string>Tools</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="action_cover_manager"/>
|
||||||
|
</widget>
|
||||||
<addaction name="menuMusic"/>
|
<addaction name="menuMusic"/>
|
||||||
<addaction name="menuPlaylist"/>
|
<addaction name="menuPlaylist"/>
|
||||||
|
<addaction name="menuTools"/>
|
||||||
<addaction name="menuSettings"/>
|
<addaction name="menuSettings"/>
|
||||||
<addaction name="menuHelp"/>
|
<addaction name="menuHelp"/>
|
||||||
</widget>
|
</widget>
|
||||||
@ -706,9 +720,23 @@
|
|||||||
<string>Configure &Global Shortcuts...</string>
|
<string>Configure &Global Shortcuts...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_cover_manager">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../data/data.qrc">
|
||||||
|
<normaloff>:/download.png</normaloff>:/download.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cover Manager</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>LineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>lineedit.h</header>
|
||||||
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>BlockAnalyzer</class>
|
<class>BlockAnalyzer</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
|
11
src/src.pro
11
src/src.pro
@ -56,7 +56,9 @@ SOURCES += main.cpp \
|
|||||||
addstreamdialog.cpp \
|
addstreamdialog.cpp \
|
||||||
savedradio.cpp \
|
savedradio.cpp \
|
||||||
stylesheetloader.cpp \
|
stylesheetloader.cpp \
|
||||||
shortcutsdialog.cpp
|
shortcutsdialog.cpp \
|
||||||
|
covermanager.cpp \
|
||||||
|
coverloader.cpp
|
||||||
HEADERS += mainwindow.h \
|
HEADERS += mainwindow.h \
|
||||||
player.h \
|
player.h \
|
||||||
library.h \
|
library.h \
|
||||||
@ -113,7 +115,9 @@ HEADERS += mainwindow.h \
|
|||||||
addstreamdialog.h \
|
addstreamdialog.h \
|
||||||
savedradio.h \
|
savedradio.h \
|
||||||
stylesheetloader.h \
|
stylesheetloader.h \
|
||||||
shortcutsdialog.h
|
shortcutsdialog.h \
|
||||||
|
covermanager.h \
|
||||||
|
coverloader.h
|
||||||
FORMS += mainwindow.ui \
|
FORMS += mainwindow.ui \
|
||||||
libraryconfig.ui \
|
libraryconfig.ui \
|
||||||
fileview.ui \
|
fileview.ui \
|
||||||
@ -127,7 +131,8 @@ FORMS += mainwindow.ui \
|
|||||||
lastfmconfigdialog.ui \
|
lastfmconfigdialog.ui \
|
||||||
about.ui \
|
about.ui \
|
||||||
addstreamdialog.ui \
|
addstreamdialog.ui \
|
||||||
shortcutsdialog.ui
|
shortcutsdialog.ui \
|
||||||
|
covermanager.ui
|
||||||
RESOURCES += ../data/data.qrc \
|
RESOURCES += ../data/data.qrc \
|
||||||
translations.qrc
|
translations.qrc
|
||||||
OTHER_FILES += ../data/schema.sql \
|
OTHER_FILES += ../data/schema.sql \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user