Allow changing the cover art from the edit tag dialog

This commit is contained in:
David Sansome 2010-12-20 15:46:38 +00:00
parent 4e2d82f3fb
commit 2a63a746fe
6 changed files with 175 additions and 21 deletions

View File

@ -44,6 +44,10 @@
#include <QTimer>
const char* AlbumCoverManager::kSettingsGroup = "CoverManager";
const char* AlbumCoverManager::kImageFileFilter =
QT_TR_NOOP("Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm *.tiff)");
const char* AlbumCoverManager::kAllFilesFilter =
QT_TR_NOOP("All files (*)");
AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
QNetworkAccessManager* network)
@ -53,7 +57,7 @@ AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
backend_(backend),
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
cover_fetcher_(new AlbumCoverFetcher(this, network)),
cover_searcher_(new AlbumCoverSearcher(this)),
cover_searcher_(NULL),
artist_icon_(IconLoader::Load("x-clementine-artist")),
all_artists_icon_(IconLoader::Load("x-clementine-album")),
context_menu_(new QMenu(this)),
@ -87,6 +91,8 @@ AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent,
p.end();
no_cover_icon_ = QPixmap::fromImage(square_nocover);
cover_searcher_ = new AlbumCoverSearcher(no_cover_icon_, this);
// Set up the status bar
statusBar()->addPermanentWidget(progress_bar_);
progress_bar_->hide();
@ -387,10 +393,8 @@ void AlbumCoverManager::AlbumCoverFetched(quint64 id, const QImage &image) {
UpdateStatusText();
}
void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &image) {
const QString artist = item->data(Role_ArtistName).toString();
const QString album = item->data(Role_AlbumName).toString();
QString AlbumCoverManager::SaveCoverInCache(
const QString& artist, const QString& album, const QImage& image) {
// Hash the artist and album into a filename for the image
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(artist.toLower().toUtf8().constData());
@ -406,6 +410,15 @@ void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &ima
// Save the image to disk
image.save(path, "JPG");
return path;
}
void AlbumCoverManager::SaveAndSetCover(QListWidgetItem *item, const QImage &image) {
const QString artist = item->data(Role_ArtistName).toString();
const QString album = item->data(Role_AlbumName).toString();
QString path = SaveCoverInCache(artist, album, image);
// Save the image in the database
backend_->UpdateManualAlbumArtAsync(artist, album, path);
@ -514,8 +527,7 @@ void AlbumCoverManager::ChooseManualCover() {
QString cover = QFileDialog::getOpenFileName(
this, tr("Choose manual cover"), dir,
tr("Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm *.tiff)") + ";;" +
tr("All files (*)"));
tr(kImageFileFilter) + ";;" + tr(kAllFilesFilter));
if (cover.isNull())
return;
@ -536,10 +548,10 @@ void AlbumCoverManager::ChooseManualCover() {
}
QString AlbumCoverManager::InitialPathForOpenCoverDialog(const QString& path_automatic, const QString& first_file_name) const {
if(!path_automatic.isEmpty() && path_automatic != "embedded") {
if(!path_automatic.isEmpty() && path_automatic != AlbumCoverLoader::kEmbeddedCover) {
return path_automatic;
} else if (!first_file_name.isEmpty() && first_file_name.contains('/')) {
// we get rid of the filename because it's extension is screwing with the dialog's
// we get rid of the filename because its extension is screwing with the dialog's
// filters
return first_file_name.section('/', 0, -2);
} else {

View File

@ -47,6 +47,8 @@ class AlbumCoverManager : public QMainWindow {
~AlbumCoverManager();
static const char* kSettingsGroup;
static const char* kImageFileFilter;
static const char* kAllFilesFilter;
LibraryBackend* backend() const { return backend_; }
QIcon no_cover_icon() const { return no_cover_icon_; }
@ -57,6 +59,9 @@ class AlbumCoverManager : public QMainWindow {
SongList GetSongsInAlbum(const QModelIndex& index) const;
SongList GetSongsInAlbums(const QModelIndexList& indexes) const;
static QString SaveCoverInCache(
const QString& artist, const QString& album, const QImage& image);
signals:
void AddSongsToPlaylist(const SongList& songs);
void LoadSongsToPlaylist(const SongList& songs);

View File

@ -15,7 +15,6 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "albumcovermanager.h"
#include "albumcoversearcher.h"
#include "ui_albumcoversearcher.h"
#include "core/albumcoverfetcher.h"
@ -24,10 +23,10 @@
#include <QKeyEvent>
#include <QListWidgetItem>
AlbumCoverSearcher::AlbumCoverSearcher(AlbumCoverManager* parent)
AlbumCoverSearcher::AlbumCoverSearcher(const QIcon& no_cover_icon, QWidget* parent)
: QDialog(parent),
ui_(new Ui_AlbumCoverSearcher),
manager_(parent),
no_cover_icon_(no_cover_icon),
fetcher_(NULL),
id_(0)
{
@ -62,7 +61,7 @@ QImage AlbumCoverSearcher::Exec(const QString &query) {
return QImage();
QIcon icon = ui_->covers->currentItem()->icon();
if (icon.cacheKey() == manager_->no_cover_icon().cacheKey())
if (icon.cacheKey() == no_cover_icon_.cacheKey())
return QImage();
return icon.pixmap(icon.availableSizes()[0]).toImage();
@ -95,7 +94,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const AlbumCoverFetcher::Sea
quint64 id = loader_->LoadImageAsync(result.image_url, QString());
QListWidgetItem* item = new QListWidgetItem(ui_->covers);
item->setIcon(manager_->no_cover_icon());
item->setIcon(no_cover_icon_);
item->setText(result.artist + " - " + result.album);
item->setData(Role_ImageURL, result.image_url);
item->setData(Role_ImageRequestId, id);

View File

@ -21,11 +21,11 @@
#include "core/albumcoverfetcher.h"
#include <QDialog>
#include <QIcon>
#include <boost/shared_ptr.hpp>
class AlbumCoverLoader;
class AlbumCoverManager;
class Ui_AlbumCoverSearcher;
class QListWidgetItem;
@ -35,7 +35,7 @@ class AlbumCoverSearcher : public QDialog {
Q_OBJECT
public:
AlbumCoverSearcher(AlbumCoverManager* parent);
AlbumCoverSearcher(const QIcon& no_cover_icon, QWidget* parent);
~AlbumCoverSearcher();
enum Role {
@ -61,7 +61,7 @@ private slots:
private:
Ui_AlbumCoverSearcher* ui_;
AlbumCoverManager* manager_;
QIcon no_cover_icon_;
boost::shared_ptr<AlbumCoverLoader> loader_;
AlbumCoverFetcher* fetcher_;

View File

@ -15,15 +15,21 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "albumcovermanager.h"
#include "albumcoversearcher.h"
#include "edittagdialog.h"
#include "ui_edittagdialog.h"
#include "core/albumcoverfetcher.h"
#include "core/albumcoverloader.h"
#include "core/utilities.h"
#include "library/library.h"
#include "library/librarybackend.h"
#include "playlist/playlistdelegates.h"
#include <QDateTime>
#include <QFileDialog>
#include <QLabel>
#include <QMenu>
#include <QtDebug>
const char* EditTagDialog::kHintText = QT_TR_NOOP("(different across multiple songs)");
@ -31,14 +37,19 @@ const char* EditTagDialog::kHintText = QT_TR_NOOP("(different across multiple so
EditTagDialog::EditTagDialog(QWidget* parent)
: QDialog(parent),
ui_(new Ui_EditTagDialog),
backend_(NULL),
ignore_edits_(false),
cover_searcher_(new AlbumCoverSearcher(QIcon(":/nocover.png"), this)),
cover_fetcher_(new AlbumCoverFetcher(this)),
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
cover_art_id_(0),
ignore_edits_(false)
cover_art_is_set_(false)
{
cover_loader_->Start(true);
cover_loader_->Worker()->SetDefaultOutputImage(QImage(":nocover.png"));
connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)),
SLOT(ArtLoaded(quint64,QImage)));
cover_searcher_->Init(cover_loader_->Worker(), cover_fetcher_);
ui_->setupUi(this);
ui_->splitter->setSizes(QList<int>() << 200 << width() - 200);
@ -70,6 +81,22 @@ EditTagDialog::EditTagDialog(QWidget* parent)
connect(ui_->song_list->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
SLOT(SelectionChanged()));
// Set up the album cover menu
cover_menu_ = new QMenu(this);
choose_cover_ = cover_menu_->addAction(
IconLoader::Load("document-open"), tr("Load cover from disk..."),
this, SLOT(LoadCoverFromFile()));
download_cover_ = cover_menu_->addAction(
IconLoader::Load("download"), tr("Search for album covers..."),
this, SLOT(SearchCover()));
unset_cover_ = cover_menu_->addAction(
IconLoader::Load("list-remove"), tr("Unset cover"),
this, SLOT(UnsetCover()));
show_cover_ = cover_menu_->addAction(
IconLoader::Load("zoom-in"), tr("Show fullsize..."),
this, SLOT(ZoomCover()));
ui_->summary_art_button->setMenu(cover_menu_);
}
EditTagDialog::~EditTagDialog() {
@ -109,6 +136,7 @@ bool EditTagDialog::SetSongs(const SongList& s) {
}
void EditTagDialog::SetTagCompleter(LibraryBackend* backend) {
backend_ = backend;
new TagCompleter(backend, Playlist::Column_Artist, ui_->artist);
new TagCompleter(backend, Playlist::Column_Album, ui_->album);
}
@ -248,8 +276,10 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) {
QString summary = "<b>" + Qt::escape(song.PrettyTitleWithArtist()) + "</b><br/>";
bool art_is_set = true;
if (song.art_manual() == AlbumCoverLoader::kManuallyUnsetCover) {
summary += Qt::escape(tr("Cover art manually unset"));
art_is_set = false;
} else if (!song.art_manual().isEmpty()) {
summary += Qt::escape(tr("Cover art set from %1").arg(song.art_manual()));
} else if (song.art_automatic() == AlbumCoverLoader::kEmbeddedCover) {
@ -258,10 +288,15 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) {
summary += Qt::escape(tr("Cover art loaded automatically from %1").arg(song.art_manual()));
} else {
summary += Qt::escape(tr("Cover art not set"));
art_is_set = false;
}
ui_->summary->setText(summary);
unset_cover_->setEnabled(art_is_set);
show_cover_->setEnabled(art_is_set);
ui_->summary_art_button->setEnabled(song.id() != -1);
ui_->length->setText(Utilities::PrettyTime(song.length()));
SetText(ui_->bpm, song.bpm(), tr("bpm"));
SetText(ui_->samplerate, song.samplerate(), "Hz");
@ -326,3 +361,87 @@ void EditTagDialog::ResetField() {
}
}
}
void EditTagDialog::LoadCoverFromFile() {
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
const Song& song = data_[sel.first().row()].original_;
// Figure out the initial path. Logic copied from
// AlbumCoverManager::InitialPathForOpenCoverDialog
QString dir;
if (!song.art_automatic().isEmpty() && song.art_automatic() != AlbumCoverLoader::kEmbeddedCover) {
dir = song.art_automatic();
} else {
dir = song.filename().section('/', 0, -1);
}
QString cover = QFileDialog::getOpenFileName(
this, tr("Choose manual cover"), dir,
tr(AlbumCoverManager::kImageFileFilter) + ";;" + tr(AlbumCoverManager::kAllFilesFilter));
if (cover.isNull())
return;
// Can we load the image?
QImage image(cover);
if (image.isNull())
return;
// Update database
SetAlbumArt(cover);
}
void EditTagDialog::SearchCover() {
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
const Song& song = data_[sel.first().row()].original_;
// Get something sensible to stick in the search box
QString query = song.artist();
if (!query.isEmpty())
query += " ";
query += song.album();
QImage image = cover_searcher_->Exec(query);
if (image.isNull())
return;
SetAlbumArt(AlbumCoverManager::SaveCoverInCache(song.artist(), song.album(), image));
}
void EditTagDialog::UnsetCover() {
SetAlbumArt(AlbumCoverLoader::kManuallyUnsetCover);
}
void EditTagDialog::ZoomCover() {
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
const Song& song = data_[sel.first().row()].original_;
QDialog* dialog = new QDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
dialog->setWindowTitle(song.title());
QLabel* label = new QLabel(dialog);
label->setPixmap(AlbumCoverLoader::TryLoadPixmap(
song.art_automatic(), song.art_manual()));
dialog->resize(label->pixmap()->size());
dialog->show();
}
void EditTagDialog::SetAlbumArt(const QString& path) {
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
Song& song = data_[sel.first().row()].original_;
if (!song.is_valid() || song.id() == -1)
return;
song.set_art_manual(path);
backend_->UpdateManualAlbumArtAsync(song.artist(), song.album(), path);
UpdateSummaryTab(song);
}

View File

@ -25,7 +25,9 @@
#include "core/song.h"
#include "widgets/lineedit.h"
class AlbumCoverFetcher;
class AlbumCoverLoader;
class AlbumCoverSearcher;
class LibraryBackend;
class Ui_EditTagDialog;
@ -51,6 +53,11 @@ private slots:
void ArtLoaded(quint64 id, const QImage& image);
void LoadCoverFromFile();
void SearchCover();
void UnsetCover();
void ZoomCover();
private:
struct Data {
Data(const Song& song = Song()) : original_(song), current_(song) {}
@ -85,16 +92,28 @@ private:
void UpdateSummaryTab(const Song& song);
void UpdateStatisticsTab(const Song& song);
void SetAlbumArt(const QString& path);
private:
Ui_EditTagDialog* ui_;
BackgroundThread<AlbumCoverLoader>* cover_loader_;
quint64 cover_art_id_;
LibraryBackend* backend_;
QList<Data> data_;
QList<FieldData> fields_;
bool ignore_edits_;
AlbumCoverSearcher* cover_searcher_;
AlbumCoverFetcher* cover_fetcher_;
BackgroundThread<AlbumCoverLoader>* cover_loader_;
quint64 cover_art_id_;
bool cover_art_is_set_;
QMenu* cover_menu_;
QAction* choose_cover_;
QAction* download_cover_;
QAction* unset_cover_;
QAction* show_cover_;
};
#endif // EDITTAGDIALOG_H