808 lines
26 KiB
C++
808 lines
26 KiB
C++
/*
|
|
* Cantata
|
|
*
|
|
* Copyright (c) 2011-2012 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 details.
|
|
*
|
|
* 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 "tageditor.h"
|
|
#include "tags.h"
|
|
#include "musiclibrarymodel.h"
|
|
#include "mpdconnection.h"
|
|
#include "settings.h"
|
|
#include "messagebox.h"
|
|
#include "inputdialog.h"
|
|
#include "localize.h"
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
#include <KDE/KStandardGuiItem>
|
|
#endif
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
#include "devicesmodel.h"
|
|
#include "device.h"
|
|
#endif
|
|
#include <QtGui/QMenu>
|
|
#include <QtGui/QCloseEvent>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QEventLoop>
|
|
#include <QtCore/QDir>
|
|
|
|
static bool equalTags(const Song &a, const Song &b, bool compareCommon)
|
|
{
|
|
return (compareCommon || a.track==b.track) && a.year==b.year && a.disc==b.disc &&
|
|
a.artist==b.artist && a.genre==b.genre && a.album==b.album &&
|
|
a.albumartist==b.albumartist && (compareCommon || a.title==b.title);
|
|
}
|
|
|
|
static void setString(QString &str, const QString &v, bool skipEmpty) {
|
|
if (!skipEmpty || !v.isEmpty()) {
|
|
str=v;
|
|
}
|
|
}
|
|
|
|
static int iCount=0;
|
|
|
|
int TagEditor::instanceCount()
|
|
{
|
|
return iCount;
|
|
}
|
|
|
|
TagEditor::TagEditor(QWidget *parent, const QList<Song> &songs,
|
|
const QSet<QString> &existingArtists, const QSet<QString> &existingAlbumArtists,
|
|
const QSet<QString> &existingAlbums, const QSet<QString> &existingGenres
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
, const QString &udi
|
|
#endif
|
|
)
|
|
: Dialog(parent)
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
, deviceUdi(udi)
|
|
#endif
|
|
, currentSongIndex(-1)
|
|
, updating(false)
|
|
, haveArtists(false)
|
|
, haveAlbumArtists(false)
|
|
, haveAlbums(false)
|
|
, haveGenres(false)
|
|
, saving(false)
|
|
{
|
|
iCount++;
|
|
original=songs;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (deviceUdi.isEmpty()) {
|
|
baseDir=MPDConnection::self()->getDetails().dir;
|
|
} else {
|
|
Device *dev=getDevice(udi, parentWidget());
|
|
|
|
if (!dev) {
|
|
deleteLater();
|
|
return;
|
|
}
|
|
|
|
baseDir=dev->path();
|
|
}
|
|
#else
|
|
baseDir=MPDConnection::self()->getDetails().dir;
|
|
#endif
|
|
qSort(original);
|
|
|
|
QWidget *mainWidet = new QWidget(this);
|
|
setupUi(mainWidet);
|
|
track->setAllowEmpty();
|
|
disc->setAllowEmpty();
|
|
year->setAllowEmpty();
|
|
setMainWidget(mainWidet);
|
|
ButtonCodes buttons=Ok|Cancel|Reset|User3;
|
|
if (songs.count()>1) {
|
|
buttons|=User2|User1;
|
|
}
|
|
setButtons(buttons);
|
|
setCaption(i18n("Tags"));
|
|
progress->setVisible(false);
|
|
if (songs.count()>1) {
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
setButtonGuiItem(User2, KStandardGuiItem::back(KStandardGuiItem::UseRTL));
|
|
setButtonText(User2, i18n("Previous"));
|
|
setButtonGuiItem(User1, KStandardGuiItem::forward(KStandardGuiItem::UseRTL));
|
|
setButtonText(User1, i18n("Next"));
|
|
#else
|
|
setButtonGuiItem(User2, GuiItem(i18n("Previous"), "go-previous"));
|
|
setButtonGuiItem(User1, GuiItem(i18n("Next"), "go-next"));
|
|
#endif
|
|
enableButton(User1, false);
|
|
enableButton(User2, false);
|
|
}
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
setButtonGuiItem(Ok, KStandardGuiItem::save());
|
|
setButtonGuiItem(User3, GuiItem(i18n("Tools"), "tools-wizard"));
|
|
#else
|
|
setButtonGuiItem(Ok, GuiItem(i18n("Save"), "document-save"));
|
|
setButtonGuiItem(User3, GuiItem(i18n("Tools"), "tools-wizard"));
|
|
#endif
|
|
QMenu *toolsMenu=new QMenu(this);
|
|
toolsMenu->addAction(i18n("Apply \"Various Artists\" Workaround"), this, SLOT(applyVa()));
|
|
toolsMenu->addAction(i18n("Revert \"Various Artists\" Workaround"), this, SLOT(revertVa()));
|
|
toolsMenu->addAction(i18n("Set 'Album Artist' from 'Artist'"), this, SLOT(setAlbumArtistFromArtist()));
|
|
toolsMenu->addAction(i18n("Capitalize"), this, SLOT(capitalise()));
|
|
toolsMenu->addAction(i18n("Adjust Track Numbers"), this, SLOT(adjustTrackNumbers()));
|
|
setButtonMenu(User3, toolsMenu, InstantPopup);
|
|
enableButton(Ok, false);
|
|
enableButton(Reset, false);
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
QStringList strings=existingArtists.toList();
|
|
strings.sort();
|
|
artist->clear();
|
|
artist->insertItems(0, strings);
|
|
|
|
strings=existingAlbumArtists.toList();
|
|
strings.sort();
|
|
albumArtist->clear();
|
|
albumArtist->insertItems(0, strings);
|
|
|
|
strings=existingAlbums.toList();
|
|
strings.sort();
|
|
album->clear();
|
|
album->insertItems(0, strings);
|
|
|
|
strings=existingGenres.toList();
|
|
strings.sort();
|
|
genre->clear();
|
|
genre->insertItems(0, strings);
|
|
|
|
trackName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
trackName->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
|
|
trackName->view()->setTextElideMode(Qt::ElideLeft);
|
|
resize(500, 200);
|
|
if (original.count()>1) {
|
|
QSet<QString> songArtists;
|
|
QSet<QString> songAlbumArtists;
|
|
QSet<QString> songAlbums;
|
|
QSet<QString> songGenres;
|
|
QSet<int> songYears;
|
|
QSet<int> songDiscs;
|
|
|
|
foreach (const Song &s, songs) {
|
|
if (!s.artist.isEmpty()) {
|
|
songArtists.insert(s.artist);
|
|
}
|
|
if (!s.albumartist.isEmpty()) {
|
|
songAlbumArtists.insert(s.albumartist);
|
|
}
|
|
if (!s.album.isEmpty()) {
|
|
songAlbums.insert(s.album);
|
|
}
|
|
if (!s.genre.isEmpty()) {
|
|
songGenres.insert(s.genre);
|
|
}
|
|
songYears.insert(s.year);
|
|
songDiscs.insert(s.disc);
|
|
if (songArtists.count()>1 && songAlbumArtists.count()>1 && songAlbums.count()>1 && songGenres.count()>1 && songYears.count()>1 && songDiscs.count()>1) {
|
|
break;
|
|
}
|
|
}
|
|
Song all;
|
|
all.file.clear();
|
|
all.title.clear();
|
|
all.track=0;
|
|
all.artist=1==songArtists.count() ? *(songArtists.begin()) : QString();
|
|
all.albumartist=1==songAlbumArtists.count() ? *(songAlbumArtists.begin()) : QString();
|
|
all.album=1==songAlbums.count() ? *(songAlbums.begin()) : QString();
|
|
all.genre=1==songGenres.count() ? *(songGenres.begin()) : QString();
|
|
all.year=1==songYears.count() ? *(songYears.begin()) : 0;
|
|
all.disc=1==songDiscs.count() ? *(songDiscs.begin()) : 0;
|
|
original.prepend(all);
|
|
artist->setFocus();
|
|
haveArtists=!songArtists.isEmpty();
|
|
haveAlbumArtists=!songAlbumArtists.isEmpty();
|
|
haveAlbums=!songAlbums.isEmpty();
|
|
haveGenres=!songGenres.isEmpty();
|
|
} else {
|
|
title->setFocus();
|
|
}
|
|
edited=original;
|
|
setIndex(0);
|
|
bool first=original.count()>1;
|
|
foreach (const Song &s, original) {
|
|
if (first) {
|
|
trackName->insertItem(trackName->count(), i18n("All tracks"));
|
|
first=false;
|
|
} else {
|
|
trackName->insertItem(trackName->count(), s.file);
|
|
}
|
|
}
|
|
connect(title, SIGNAL(textChanged(const QString &)), SLOT(checkChanged()));
|
|
connect(artist, SIGNAL(activated(int)), SLOT(checkChanged()));
|
|
connect(artist, SIGNAL(editTextChanged(const QString &)), SLOT(checkChanged()));
|
|
connect(albumArtist, SIGNAL(activated(int)), SLOT(checkChanged()));
|
|
connect(albumArtist, SIGNAL(editTextChanged(const QString &)), SLOT(checkChanged()));
|
|
connect(album, SIGNAL(activated(int)), SLOT(checkChanged()));
|
|
connect(album, SIGNAL(editTextChanged(const QString &)), SLOT(checkChanged()));
|
|
connect(genre, SIGNAL(activated(int)), SLOT(checkChanged()));
|
|
connect(track, SIGNAL(valueChanged(int)), SLOT(checkChanged()));
|
|
connect(disc, SIGNAL(valueChanged(int)), SLOT(checkChanged()));
|
|
connect(genre, SIGNAL(editTextChanged(const QString &)), SLOT(checkChanged()));
|
|
connect(year, SIGNAL(valueChanged(int)), SLOT(checkChanged()));
|
|
connect(trackName, SIGNAL(activated(int)), SLOT(setIndex(int)));
|
|
connect(this, SIGNAL(update()), MPDConnection::self(), SLOT(update()));
|
|
|
|
if (original.count()>0) {
|
|
saveable=QDir(baseDir).isReadable();
|
|
} else {
|
|
saveable=false;
|
|
}
|
|
}
|
|
|
|
TagEditor::~TagEditor()
|
|
{
|
|
iCount--;
|
|
}
|
|
|
|
void TagEditor::fillSong(Song &s, bool isAll, bool skipEmpty) const
|
|
{
|
|
Song all=original.at(0);
|
|
bool haveAll=original.count()>1;
|
|
|
|
if (!isAll) {
|
|
setString(s.title, title->text().trimmed(), skipEmpty);
|
|
}
|
|
setString(s.artist, artist->text().trimmed(), skipEmpty && (!haveAll || all.artist.isEmpty()));
|
|
setString(s.album, album->text().trimmed(), skipEmpty && (!haveAll || all.album.isEmpty()));
|
|
setString(s.albumartist, albumArtist->text().trimmed(), skipEmpty && (!haveAll || all.albumartist.isEmpty()));
|
|
if (!isAll) {
|
|
s.track=track->value();
|
|
}
|
|
s.disc=disc->value();
|
|
setString(s.genre, genre->text().trimmed(), skipEmpty && (!haveAll || all.genre.isEmpty()));
|
|
s.year=year->value();
|
|
}
|
|
|
|
void TagEditor::setPlaceholderTexts()
|
|
{
|
|
QString various=i18n("(Various)");
|
|
|
|
if(0==currentSongIndex && original.count()>1) {
|
|
Song all=original.at(0);
|
|
artist->setPlaceholderText(all.artist.isEmpty() && haveArtists ? various : QString());
|
|
album->setPlaceholderText(all.album.isEmpty() && haveAlbums ? various : QString());
|
|
albumArtist->setPlaceholderText(all.albumartist.isEmpty() && haveAlbumArtists ? various : QString());
|
|
genre->setPlaceholderText(all.genre.isEmpty() && haveGenres ? various : QString());
|
|
}
|
|
}
|
|
|
|
void TagEditor::enableOkButton()
|
|
{
|
|
if (!saveable) {
|
|
return;
|
|
}
|
|
|
|
enableButton(Ok, (editedIndexes.count()>1) ||
|
|
(1==original.count() && 1==editedIndexes.count()) ||
|
|
(1==editedIndexes.count() && !editedIndexes.contains(0)) );
|
|
enableButton(Reset, isButtonEnabled(Ok));
|
|
}
|
|
|
|
void TagEditor::setLabelStates()
|
|
{
|
|
Song o=original.at(currentSongIndex);
|
|
Song e=edited.at(currentSongIndex);
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
|
|
titleLabel->setOn(!isAll && o.title!=e.title);
|
|
artistLabel->setOn(o.artist!=e.artist);
|
|
albumArtistLabel->setOn(o.albumartist!=e.albumartist);
|
|
albumLabel->setOn(o.album!=e.album);
|
|
trackLabel->setOn(!isAll && o.track!=e.track);
|
|
discLabel->setOn(o.disc!=e.disc);
|
|
genreLabel->setOn(o.genre!=e.genre);
|
|
yearLabel->setOn(o.year!=e.year);
|
|
}
|
|
|
|
void TagEditor::applyVa()
|
|
{
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
|
|
if (MessageBox::No==MessageBox::questionYesNo(this, (isAll ? i18n("Apply \"Various Artists\" workaround to <b>all</b> tracks?")
|
|
: i18n("Apply \"Various Artists\" workaround?"))+
|
|
QLatin1String("<br/><hr/><br/>")+
|
|
i18n("<i>This will set 'Album artist' and 'Artist' to "
|
|
"\"Various Artists\", and set 'Title' to "
|
|
"\"TrackArtist - TrackTitle\"</i>"))) {
|
|
return;
|
|
}
|
|
|
|
if (isAll) {
|
|
updating=true;
|
|
for (int i=0; i<edited.count(); ++i) {
|
|
Song s=edited.at(i);
|
|
if (s.fixVariousArtists()) {
|
|
if (0==i && QLatin1String(" - ")==s.title) {
|
|
s.title=QString();
|
|
}
|
|
edited.replace(i, s);
|
|
updateEditedStatus(i);
|
|
if (i==currentSongIndex) {
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
updating=false;
|
|
setLabelStates();
|
|
enableOkButton();
|
|
} else {
|
|
Song s=edited.at(currentSongIndex);
|
|
if (s.fixVariousArtists()) {
|
|
edited.replace(currentSongIndex, s);
|
|
updateEditedStatus(currentSongIndex);
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TagEditor::revertVa()
|
|
{
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
|
|
if (MessageBox::No==MessageBox::questionYesNo(this, (isAll ? i18n("Revert \"Various Artists\" workaround on <b>all</b> tracks?")
|
|
: i18n("Revert \"Various Artists\" workaround"))+
|
|
QLatin1String("<br/><hr/><br/>")+
|
|
i18n("<i>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. <br/><br/>"
|
|
"If 'Title' is \"Wibble - Wobble\", then 'Artist' will be set to "
|
|
"\"Wibble\" and 'Title' will be set to \"Wobble\"</i>"))) {
|
|
return;
|
|
}
|
|
|
|
if (isAll) {
|
|
updating=true;
|
|
QSet<QString> artists;
|
|
for (int i=1; i<edited.count(); ++i) {
|
|
Song s=edited.at(i);
|
|
if (s.revertVariousArtists()) {
|
|
artists.insert(s.artist);
|
|
edited.replace(i, s);
|
|
updateEditedStatus(i);
|
|
if (i==currentSongIndex) {
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
Song s=edited.at(0);
|
|
s.artist=artists.count()!=1 ? QString() : *artists.constBegin();
|
|
edited.replace(0, s);
|
|
updateEditedStatus(0);
|
|
setSong(s);
|
|
updating=false;
|
|
setLabelStates();
|
|
enableOkButton();
|
|
} else {
|
|
Song s=edited.at(currentSongIndex);
|
|
if (s.revertVariousArtists()) {
|
|
edited.replace(currentSongIndex, s);
|
|
updateEditedStatus(currentSongIndex);
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TagEditor::setAlbumArtistFromArtist()
|
|
{
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
|
|
if (MessageBox::No==MessageBox::questionYesNo(this, isAll ? i18n("Set 'Album Artist' from 'Artist' for <b>all</b> tracks?")
|
|
: i18n("Set 'Album Artist' from 'Artist'?"))) {
|
|
return;
|
|
}
|
|
|
|
if (isAll) {
|
|
for (int i=0; i<edited.count(); ++i) {
|
|
Song s=edited.at(i);
|
|
if (s.setAlbumArtist()) {
|
|
edited.replace(i, s);
|
|
updateEditedStatus(i);
|
|
if (i==currentSongIndex) {
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Song s=edited.at(currentSongIndex);
|
|
if (s.setAlbumArtist()) {
|
|
edited.replace(currentSongIndex, s);
|
|
updateEditedStatus(currentSongIndex);
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TagEditor::capitalise()
|
|
{
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
|
|
if (MessageBox::No==MessageBox::questionYesNo(this, isAll ? i18n("Capitalize the first letter of 'Title', 'Artist', 'Album artist', and "
|
|
"'Album' of <b>all</b> tracks?")
|
|
: i18n("Capitalize the first letter of 'Title', 'Artist', 'Album artist', and "
|
|
"'Album'"))) {
|
|
return;
|
|
}
|
|
|
|
if (isAll) {
|
|
for (int i=0; i<edited.count(); ++i) {
|
|
Song s=edited.at(i);
|
|
if (s.capitalise()) {
|
|
edited.replace(i, s);
|
|
updateEditedStatus(i);
|
|
if (i==currentSongIndex) {
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Song s=edited.at(currentSongIndex);
|
|
if (s.capitalise()) {
|
|
edited.replace(currentSongIndex, s);
|
|
updateEditedStatus(currentSongIndex);
|
|
setSong(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TagEditor::adjustTrackNumbers()
|
|
{
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
bool ok=false;
|
|
int inc=InputDialog::getInteger(i18n("Adjust Track Numbers"), isAll ? i18n("Increment the value of each track number by:")
|
|
: i18n("Increment track number by:"),
|
|
0, 1, 500, 1, 10, &ok, this);
|
|
|
|
if (!ok || inc<=0) {
|
|
return;
|
|
}
|
|
|
|
if (isAll) {
|
|
for (int i=0; i<edited.count(); ++i) {
|
|
Song s=edited.at(i);
|
|
s.track+=inc;
|
|
edited.replace(i, s);
|
|
updateEditedStatus(i);
|
|
if (i==currentSongIndex) {
|
|
setSong(s);
|
|
}
|
|
}
|
|
} else {
|
|
Song s=edited.at(currentSongIndex);
|
|
s.track+=inc;
|
|
edited.replace(currentSongIndex, s);
|
|
updateEditedStatus(currentSongIndex);
|
|
setSong(s);
|
|
}
|
|
}
|
|
|
|
void TagEditor::checkChanged()
|
|
{
|
|
if (updating) {
|
|
return;
|
|
}
|
|
|
|
bool allWasEdited=editedIndexes.contains(0);
|
|
|
|
updateEdited();
|
|
|
|
bool allEdited=editedIndexes.contains(0);
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
|
|
if (isAll && (allEdited || allWasEdited)) {
|
|
int save=currentSongIndex;
|
|
for (int i=1; i<edited.count(); ++i) {
|
|
currentSongIndex=i;
|
|
updateEdited(true);
|
|
}
|
|
currentSongIndex=save;
|
|
}
|
|
enableOkButton();
|
|
setLabelStates();
|
|
}
|
|
|
|
void TagEditor::updateTrackName(int index, bool edited)
|
|
{
|
|
bool isAll=0==index && original.count()>1;
|
|
|
|
if (edited) {
|
|
if (isAll) {
|
|
trackName->setItemText(index, i18n("All tracks [modified]"));
|
|
} else {
|
|
trackName->setItemText(index, i18n("%1 [modified]").arg(original.at(index).file));
|
|
}
|
|
} else {
|
|
if (isAll) {
|
|
trackName->setItemText(index, i18n("All tracks"));
|
|
} else {
|
|
trackName->setItemText(index, original.at(index).file);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TagEditor::updateEditedStatus(int index)
|
|
{
|
|
bool isAll=0==index && original.count()>1;
|
|
Song s=edited.at(index);
|
|
if (equalTags(s, original.at(index), isAll)) {
|
|
if (editedIndexes.contains(index)) {
|
|
editedIndexes.remove(index);
|
|
updateTrackName(index, false);
|
|
edited.replace(index, s);
|
|
}
|
|
} else {
|
|
if (!editedIndexes.contains(index)) {
|
|
editedIndexes.insert(index);
|
|
updateTrackName(index, true);
|
|
}
|
|
edited.replace(index, s);
|
|
}
|
|
}
|
|
|
|
void TagEditor::updateEdited(bool isFromAll)
|
|
{
|
|
Song s=edited.at(currentSongIndex);
|
|
bool isAll=0==currentSongIndex && original.count()>1;
|
|
fillSong(s, isFromAll || isAll, /*isFromAll*/false);
|
|
|
|
if (!isAll && isFromAll && original.count()>1) {
|
|
Song all=original.at(0);
|
|
Song o=original.at(currentSongIndex);
|
|
if (all.artist.isEmpty() && s.artist.isEmpty() && !o.artist.isEmpty()) {
|
|
s.artist=o.artist;
|
|
}
|
|
if (all.albumartist.isEmpty() && s.albumartist.isEmpty() && !o.albumartist.isEmpty()) {
|
|
s.albumartist=o.albumartist;
|
|
}
|
|
if (all.album.isEmpty() && s.album.isEmpty() && !o.album.isEmpty()) {
|
|
s.album=o.album;
|
|
}
|
|
if (all.genre.isEmpty() && s.genre.isEmpty() && !o.genre.isEmpty()) {
|
|
s.genre=o.genre;
|
|
}
|
|
}
|
|
|
|
if (equalTags(s, original.at(currentSongIndex), isFromAll || isAll)) {
|
|
if (editedIndexes.contains(currentSongIndex)) {
|
|
editedIndexes.remove(currentSongIndex);
|
|
updateTrackName(currentSongIndex, false);
|
|
edited.replace(currentSongIndex, s);
|
|
}
|
|
} else {
|
|
if (!editedIndexes.contains(currentSongIndex)) {
|
|
editedIndexes.insert(currentSongIndex);
|
|
updateTrackName(currentSongIndex, true);
|
|
}
|
|
edited.replace(currentSongIndex, s);
|
|
}
|
|
}
|
|
|
|
void TagEditor::setSong(const Song &s)
|
|
{
|
|
title->setText(s.title);
|
|
artist->setText(s.artist);
|
|
albumArtist->setText(s.albumartist);
|
|
album->setText(s.album);
|
|
track->setValue(s.track);
|
|
disc->setValue(s.disc);
|
|
genre->setText(s.genre);
|
|
year->setValue(s.year);
|
|
}
|
|
|
|
void TagEditor::setIndex(int idx)
|
|
{
|
|
if (currentSongIndex==idx || idx>(original.count()-1)) {
|
|
return;
|
|
}
|
|
updating=true;
|
|
|
|
bool haveMultiple=original.count()>1;
|
|
|
|
if (haveMultiple && currentSongIndex>=0) {
|
|
updateEdited();
|
|
}
|
|
Song s=edited.at(!haveMultiple || idx==0 ? 0 : idx);
|
|
setSong(s);
|
|
currentSongIndex=idx;
|
|
|
|
bool isMultiple=haveMultiple && 0==idx;
|
|
title->setEnabled(!isMultiple);
|
|
titleLabel->setEnabled(!isMultiple);
|
|
track->setEnabled(!isMultiple);
|
|
trackLabel->setEnabled(!isMultiple);
|
|
if (isMultiple) {
|
|
title->setText(QString());
|
|
track->setValue(0);
|
|
}
|
|
|
|
if (original.count()>1) {
|
|
enableButton(User1, !isMultiple && idx<(original.count()-1)); // Next
|
|
enableButton(User2, !isMultiple && idx>1); // Prev
|
|
}
|
|
setPlaceholderTexts();
|
|
enableOkButton();
|
|
trackName->setCurrentIndex(idx);
|
|
setLabelStates();
|
|
updating=false;
|
|
}
|
|
|
|
void TagEditor::applyUpdates()
|
|
{
|
|
bool updated=false;
|
|
bool skipFirst=original.count()>1;
|
|
QStringList failed;
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
Device * dev=0;
|
|
if (!deviceUdi.isEmpty()) {
|
|
dev=getDevice(deviceUdi, this);
|
|
if (!dev) {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int toSave=editedIndexes.count();
|
|
|
|
saving=true;
|
|
enableButton(Ok, false);
|
|
enableButton(Cancel, false);
|
|
enableButton(Reset, false);
|
|
enableButton(User1, false);
|
|
enableButton(User2, false);
|
|
enableButton(User3, false);
|
|
progress->setVisible(true);
|
|
progress->setRange(0, toSave);
|
|
|
|
int count=0;
|
|
foreach (int idx, editedIndexes) {
|
|
progress->setValue(progress->value()+1);
|
|
if (0==count++%10) {
|
|
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
|
}
|
|
|
|
if (skipFirst && 0==idx) {
|
|
continue;
|
|
}
|
|
Song orig=original.at(idx);
|
|
Song edit=edited.at(idx);
|
|
if (equalTags(orig, edit, false)) {
|
|
continue;
|
|
}
|
|
|
|
switch(Tags::update(baseDir+orig.file, orig, edit)) {
|
|
case Tags::Update_Modified:
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (!deviceUdi.isEmpty()) {
|
|
if (!dev->updateSong(orig, edit)) {
|
|
dev->removeSongFromList(orig);
|
|
dev->addSongToList(edit);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (!MusicLibraryModel::self()->updateSong(orig, edit)) {
|
|
MusicLibraryModel::self()->removeSongFromList(orig);
|
|
MusicLibraryModel::self()->addSongToList(edit);
|
|
}
|
|
}
|
|
updated=true;
|
|
break;
|
|
case Tags::Update_Failed:
|
|
failed.append(orig.file);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
saving=false;
|
|
|
|
if (failed.count()) {
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
MessageBox::errorList(this, i18n("Failed to update the tags of the following tracks:"), failed);
|
|
#else
|
|
MessageBox::errorList(this, QObject::tr("Failed to update the tags of some tracks."), failed);
|
|
#endif
|
|
}
|
|
|
|
if (updated) {
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
if (!deviceUdi.isEmpty()) {
|
|
dev->saveCache();
|
|
} else
|
|
#endif
|
|
{
|
|
// MusicLibraryModel::self()->removeCache();
|
|
emit update();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TagEditor::slotButtonClicked(int button)
|
|
{
|
|
switch (button) {
|
|
case Ok: {
|
|
applyUpdates();
|
|
accept();
|
|
break;
|
|
}
|
|
case Reset: // Reset
|
|
if (0==currentSongIndex && original.count()>1) {
|
|
for (int i=0; i<original.count(); ++i) {
|
|
edited.replace(i, original.at(i));
|
|
updateTrackName(i, false);
|
|
}
|
|
editedIndexes.clear();
|
|
setSong(original.at(currentSongIndex));
|
|
} else {
|
|
setSong(original.at(currentSongIndex));
|
|
}
|
|
enableOkButton();
|
|
break;
|
|
case User1: // Next
|
|
setIndex(currentSongIndex+1);
|
|
break;
|
|
case User2: // Prev
|
|
setIndex(currentSongIndex-1);
|
|
break;
|
|
case Cancel:
|
|
reject();
|
|
// Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!!
|
|
Dialog::slotButtonClicked(button);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_DEVICES_SUPPORT
|
|
Device * TagEditor::getDevice(const QString &udi, QWidget *p)
|
|
{
|
|
Device *dev=DevicesModel::self()->device(udi);
|
|
if (!dev) {
|
|
MessageBox::error(p ? p : this, i18n("Device has been removed!"));
|
|
reject();
|
|
return 0;
|
|
}
|
|
if (!dev->isConnected()) {
|
|
MessageBox::error(p ? p : this, i18n("Device is not connected."));
|
|
reject();
|
|
return 0;
|
|
}
|
|
if (!dev->isIdle()) {
|
|
MessageBox::error(p ? p : this, i18n("Device is busy?"));
|
|
reject();
|
|
return 0;
|
|
}
|
|
return dev;
|
|
}
|
|
#endif
|
|
|
|
void TagEditor::closeEvent(QCloseEvent *event)
|
|
{
|
|
if (saving) {
|
|
event->ignore();
|
|
} else {
|
|
Dialog::closeEvent(event);
|
|
}
|
|
}
|