Files
cantata/devices/albumdetailsdialog.cpp
Craig Drummond b6bd94c236 Update (c) year
2022-01-08 21:24:07 +00:00

409 lines
13 KiB
C++

/*
* Cantata
*
* Copyright (c) 2011-2022 Craig Drummond <craig.p.drummond@gmail.com>
*
* ----
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 detailexampleSong.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "albumdetailsdialog.h"
#include "audiocddevice.h"
#include "models/musiclibraryitemsong.h"
#include "support/messagebox.h"
#include "support/inputdialog.h"
#include "models/devicesmodel.h"
#include "models/mpdlibrarymodel.h"
#include "cdalbum.h"
#include "widgets/icons.h"
#include "gui/coverdialog.h"
#include "widgets/basicitemdelegate.h"
#include "support/lineedit.h"
#include <QMenu>
#include <QStyledItemDelegate>
#include <QMouseEvent>
#include <QSpinBox>
#include <algorithm>
enum Columns {
COL_TRACK,
COL_ARTIST,
COL_TITLE
};
class EditorDelegate : public BasicItemDelegate
{
public:
EditorDelegate(QObject *parent=0) : BasicItemDelegate(parent) { }
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Q_UNUSED(option)
if (COL_TRACK==index.column()) {
QSpinBox *editor = new QSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(500);
return editor;
} else {
parent->setProperty("cantata-delegate", true);
return new LineEdit(parent);
}
}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
return QStyledItemDelegate::sizeHint(option, index)+QSize(0, 4);
}
void setEditorData(QWidget *editor, const QModelIndex &index) const {
if (COL_TRACK==index.column()) {
static_cast<QSpinBox*>(editor)->setValue(index.model()->data(index, Qt::EditRole).toInt());
} else {
static_cast<LineEdit*>(editor)->setText(index.model()->data(index, Qt::EditRole).toString());
}
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
if (COL_TRACK==index.column()) {
model->setData(index, static_cast<QSpinBox*>(editor)->value(), Qt::EditRole);
} else {
model->setData(index, static_cast<LineEdit*>(editor)->text().trimmed(), Qt::EditRole);
}
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Q_UNUSED(index)
editor->setGeometry(option.rect);
}
};
static int iCount=0;
int AlbumDetailsDialog::instanceCount()
{
return iCount;
}
AlbumDetailsDialog::AlbumDetailsDialog(QWidget *parent)
: Dialog(parent, "AlbumDetailsDialog")
, pressed(false)
{
iCount++;
setButtons(User1|Ok|Cancel);
setCaption(tr("Audio CD"));
setAttribute(Qt::WA_DeleteOnClose);
QWidget *mainWidet = new QWidget(this);
setupUi(mainWidet);
setMainWidget(mainWidet);
QSet<QString> artists;
QSet<QString> albumArtists;
QSet<QString> composers;
QSet<QString> albums;
QSet<QString> genres;
MpdLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres);
QStringList strings=albumArtists.values();
strings.sort();
artist->clear();
artist->insertItems(0, strings);
strings=composers.values();
strings.sort();
composer->clear();
composer->insertItems(0, strings);
strings=albums.values();
strings.sort();
title->clear();
title->insertItems(0, strings);
strings=genres.values();
strings.sort();
genre->clear();
genre->insertItems(0, strings);
QMenu *toolsMenu=new QMenu(this);
toolsMenu->addAction(tr("Apply \"Various Artists\" Workaround"), this, SLOT(applyVa()));
toolsMenu->addAction(tr("Revert \"Various Artists\" Workaround"), this, SLOT(revertVa()));
toolsMenu->addAction(tr("Capitalize"), this, SLOT(capitalise()));
toolsMenu->addAction(tr("Adjust Track Numbers"), this, SLOT(adjustTrackNumbers()));
setButtonMenu(User1, toolsMenu, InstantPopup);
setButtonGuiItem(User1, GuiItem(tr("Tools"), "tools-wizard"));
connect(singleArtist, SIGNAL(toggled(bool)), SLOT(hideArtistColumn(bool)));
resize(600, 600);
int size=fontMetrics().height()*5;
cover->setMinimumSize(size, size);
cover->setMaximumSize(size, size);
setCover();
cover->installEventFilter(this);
tracks->setItemDelegate(new EditorDelegate(tracks));
}
AlbumDetailsDialog::~AlbumDetailsDialog()
{
iCount--;
}
void AlbumDetailsDialog::show(AudioCdDevice *dev)
{
udi=dev->id();
artist->setText(dev->albumArtist());
composer->setText(dev->albumComposer());
title->setText(dev->albumName());
genre->setText(dev->albumGenre());
disc->setValue(dev->albumDisc());
year->setValue(dev->albumYear());
tracks->clear();
QSet<QString> artists;
artists.insert(dev->albumArtist());
QList<Song> songs;
for (const MusicLibraryItem *i: dev->childItems()) {
songs.append(static_cast<const MusicLibraryItemSong *>(i)->song());
}
std::sort(songs.begin(), songs.end());
for (const Song &s: songs) {
QTreeWidgetItem *item=new QTreeWidgetItem(tracks);
update(item, s);
artists.insert(s.artist);
item->setFlags(item->flags()|Qt::ItemIsEditable);
}
singleArtist->setChecked(1==artists.count());
coverImage=dev->cover();
if (!coverImage.img.isNull()) {
cover->setPixmap(QPixmap::fromImage(coverImage.img.scaled(cover->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
}
Dialog::show();
}
void AlbumDetailsDialog::slotButtonClicked(int button)
{
switch (button) {
case Ok: {
Device *dev=DevicesModel::self()->device(udi);
if (dev && Device::AudioCd==dev->devType()) {
CdAlbum cdAlbum=getAlbum();
for(int i=0; i<tracks->topLevelItemCount(); ++i) {
cdAlbum.tracks.append(toSong(tracks->topLevelItem(i), cdAlbum));
}
static_cast<AudioCdDevice *>(dev)->setDetails(cdAlbum);
static_cast<AudioCdDevice *>(dev)->setCover(coverImage);
}
accept();
break;
}
case Cancel:
reject();
break;
default:
break;
}
if (Ok==button) {
accept();
}
Dialog::slotButtonClicked(button);
}
void AlbumDetailsDialog::hideArtistColumn(bool hide)
{
tracks->header()->setSectionHidden(COL_ARTIST, hide);
}
void AlbumDetailsDialog::applyVa()
{
if (MessageBox::No==MessageBox::questionYesNo(this, tr("Apply \"Various Artists\" workaround?")+
QLatin1String("\n\n")+
tr("This will set 'Album artist' and 'Artist' to "
"\"Various Artists\", and set 'Title' to "
"\"TrackArtist - TrackTitle\""), tr("Apply \"Various Artists\" Workaround"),
StdGuiItem::apply(), StdGuiItem::cancel())) {
return;
}
CdAlbum album=getAlbum();
for(int i=0; i<tracks->topLevelItemCount(); ++i) {
QTreeWidgetItem *itm=tracks->topLevelItem(i);
Song s=toSong(itm, album);
if (s.fixVariousArtists()) {
update(itm, s);
}
}
}
void AlbumDetailsDialog::revertVa()
{
if (MessageBox::No==MessageBox::questionYesNo(this, tr("Revert \"Various Artists\" workaround?")+
QLatin1String("\n\n")+
tr("Where the 'Album artist' is the same as 'Artist' "
"and the 'Title' is of the format \"TrackArtist - TrackTitle\", "
"'Artist' will be taken from 'Title' and 'Title' itself will be "
"set to just the title. e.g. \n"
"If 'Title' is \"Wibble - Wobble\", then 'Artist' will be set to "
"\"Wibble\" and 'Title' will be set to \"Wobble\""), tr("Revert \"Various Artists\" Workaround"),
GuiItem(tr("Revert")), StdGuiItem::cancel())) {
return;
}
CdAlbum album=getAlbum();
for(int i=0; i<tracks->topLevelItemCount(); ++i) {
QTreeWidgetItem *itm=tracks->topLevelItem(i);
Song s=toSong(itm, album);
if (s.revertVariousArtists()) {
update(itm, s);
}
}
}
void AlbumDetailsDialog::capitalise()
{
if (MessageBox::No==MessageBox::questionYesNo(this, tr("Capitalize the first letter of 'Title', 'Artist', 'Album artist', and 'Album'?"),
tr("Capitalize"), GuiItem(tr("Capitalize")), StdGuiItem::cancel())) {
return;
}
CdAlbum album=getAlbum();
for(int i=0; i<tracks->topLevelItemCount(); ++i) {
QTreeWidgetItem *itm=tracks->topLevelItem(i);
Song s=toSong(itm, album);
if (s.capitalise()) {
update(itm, s);
}
}
}
void AlbumDetailsDialog::adjustTrackNumbers()
{
bool ok=false;
int adj=InputDialog::getInteger(tr("Adjust Track Numbers"), tr("Adjust track number by:"), 0, -500, 500, 1, 10, &ok, this);
if (!ok || 0==adj) {
return;
}
CdAlbum album=getAlbum();
for(int i=0; i<tracks->topLevelItemCount(); ++i) {
QTreeWidgetItem *itm=tracks->topLevelItem(i);
Song s=toSong(itm, album);
s.track+=adj;
update(itm, s);
}
}
enum Roles {
Role_Id = Qt::UserRole,
Role_File,
Role_Time
};
Song AlbumDetailsDialog::toSong(QTreeWidgetItem *i, const CdAlbum &album)
{
Song s;
s.albumartist=album.artist;
s.album=album.name;
s.genres[0]=album.genre;
s.year=album.year;
s.disc=album.disc;
s.artist=singleArtist->isChecked() ? s.albumartist : i->text(COL_ARTIST);
if (!album.composer.isEmpty()) {
s.setComposer(album.composer);
}
s.title=i->text(COL_TITLE);
s.track=i->text(COL_TRACK).toInt();
s.id=i->data(0, Role_Id).toInt();
s.file=i->data(0, Role_File).toString();
s.time=i->data(0, Role_Time).toInt();
s.fillEmptyFields();
return s;
}
CdAlbum AlbumDetailsDialog::getAlbum() const
{
CdAlbum cdAlbum;
cdAlbum.artist=artist->text().trimmed();
cdAlbum.composer=composer->text().trimmed();
cdAlbum.name=title->text().trimmed();
cdAlbum.disc=disc->value();
cdAlbum.year=year->value();
cdAlbum.genre=genre->text().trimmed();
if (cdAlbum.artist.isEmpty()) {
cdAlbum.artist=Song::unknown();
}
if (cdAlbum.name.isEmpty()) {
cdAlbum.name=Song::unknown();
}
return cdAlbum;
}
void AlbumDetailsDialog::update(QTreeWidgetItem *i, const Song &s)
{
i->setText(COL_TRACK, QString::number(s.track));
i->setText(COL_ARTIST, s.artist);
i->setText(COL_TITLE, s.title);
i->setData(0, Role_Id, s.id);
i->setData(0, Role_File, s.file);
i->setData(0, Role_Time, s.time);
}
void AlbumDetailsDialog::setCover()
{
int iconSize=cover->size().width()<=128 ? 128 : 256;
cover->setPixmap(Icons::self()->albumIcon(iconSize).pixmap(iconSize, iconSize).scaled(cover->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
bool AlbumDetailsDialog::eventFilter(QObject *object, QEvent *event)
{
switch(event->type()) {
case QEvent::MouseButtonPress:
if (Qt::LeftButton==static_cast<QMouseEvent *>(event)->button()) {
pressed=true;
}
break;
case QEvent::MouseButtonRelease:
if (pressed && Qt::LeftButton==static_cast<QMouseEvent *>(event)->button()) {
if (0==CoverDialog::instanceCount()) {
CoverDialog *dlg=new CoverDialog(this);
connect(dlg, SIGNAL(selectedCover(QImage, QString)), this, SLOT(coverSelected(QImage, QString)));
Song s;
s.file=AudioCdDevice::coverUrl(udi);
s.artist=artist->text().trimmed();
s.album=title->text().trimmed();
s.type=Song::Cdda;
dlg->show(s, coverImage);
}
}
pressed=false;
break;
default:
break;
}
return QObject::eventFilter(object, event);
}
void AlbumDetailsDialog::coverSelected(const QImage &img, const QString &fileName)
{
coverImage=Covers::Image(img, fileName);
if (!coverImage.img.isNull()) {
cover->setPixmap(QPixmap::fromImage(coverImage.img.scaled(cover->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
}
}
#include "moc_albumdetailsdialog.cpp"