409 lines
13 KiB
C++
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"
|