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

437 lines
16 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 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 "devicepropertieswidget.h"
#include "filenameschemedialog.h"
#include "gui/covers.h"
#include "widgets/icons.h"
#include "support/utils.h"
#include <QValidator>
#include <QTabWidget>
#include <QTimer>
class CoverNameValidator : public QValidator
{
public:
CoverNameValidator(QObject *parent) : QValidator(parent) { }
State validate(QString &input, int &) const override
{
int dotCount(0);
for (int i=0; i<input.length(); ++i) {
if (QChar('.')==input[i]) {
if (++dotCount>1) {
return Invalid;
}
}
else if (!input[i].isLetterOrNumber() || input[i].isSpace()) {
return Invalid;
}
}
if (input.endsWith('.')) {
return Intermediate;
}
return Acceptable;
}
// void fixup(QString &input) const
// {
// QString out;
// int dotCount(0);
// for (int i=0; i<input.length(); ++i) {
// if (input[i].isLetterOrNumber() && !input[i].isSpace()) {
// out+=input[i];
// } else if (QChar('.')==input[i] && ++dotCount<1) {
// out+=input[i];
// }
// }
//
// if (!out.endsWith(".jpg") && !out.endsWith(".png")) {
// out.replace(".", "");
// out+=".jpg";
// }
// input=out;
// }
};
DevicePropertiesWidget::DevicePropertiesWidget(QWidget *parent)
: QWidget(parent)
, schemeDlg(nullptr)
, noCoverText(tr("Don't copy covers"))
, embedCoverText(tr("Embed cover within each file"))
, modified(false)
, saveable(false)
{
setupUi(this);
configFilename->setIcon(Icons::self()->configureIcon);
coverMaxSize->insertItems(0, QStringList() << tr("No maximum size") << tr("400 pixels") << tr("300 pixels") << tr("200 pixels") << tr("100 pixels"));
fixVariousArtists->setToolTip(tr("<p>When copying tracks to a device, and the 'Album Artist' is set to 'Various Artists', "
"then Cantata will set the 'Artist' tag of all tracks to 'Various Artists' and the "
"track 'Title' tag to 'TrackArtist - TrackTitle'.<hr/> When copying from a device, Cantata "
"will check if 'Album Artist' and 'Artist' are both set to 'Various Artists'. If so, it "
"will attempt to extract the real artist from the 'Title' tag, and remove the artist name "
"from the 'Title' tag.</p>"));
useCache->setToolTip(tr("<p>If you enable this, then Cantata will create a cache of the device's music library. "
"This will help to speed up subsequent library scans (as the cache file will be used instead of "
"having to read the tags of each file.)<hr/><b>NOTE:</b> If you use another application to update "
"the device's library, then this cache will become out-of-date. To rectify this, simply "
"click on the 'refresh' icon in the device list. This will cause the cache file to be removed, and "
"the contents of the device re-scanned.</p>"));
if (qobject_cast<QTabWidget *>(parent)) {
verticalLayout->setMargin(4);
}
}
#define REMOVE(w) \
w->setVisible(false); \
w->deleteLater(); \
w=0;
void DevicePropertiesWidget::update(const QString &path, const DeviceOptions &opts, const QList<DeviceStorage> &storage, int props, int disabledProps)
{
bool allowCovers=(props&Prop_CoversAll)||(props&Prop_CoversBasic);
albumCovers->clear();
if (allowCovers) {
if (props&Prop_CoversAll) {
albumCovers->insertItems(0, QStringList() << noCoverText << embedCoverText << Covers::standardNames());
} else {
albumCovers->insertItems(0, QStringList() << noCoverText << embedCoverText);
}
}
if (props&Prop_Name) {
name->setText(opts.name);
connect(name, SIGNAL(textChanged(const QString &)), SLOT(checkSaveable()));
} else {
REMOVE(name)
REMOVE(nameLabel)
}
if (props&Prop_FileName) {
filenameScheme->setText(opts.scheme);
vfatSafe->setChecked(opts.vfatSafe);
asciiOnly->setChecked(opts.asciiOnly);
ignoreThe->setChecked(opts.ignoreThe);
replaceSpaces->setChecked(opts.replaceSpaces);
} else {
REMOVE(filenamesGroupBox)
filenameScheme=nullptr;
vfatSafe=nullptr;
asciiOnly=nullptr;
ignoreThe=nullptr;
replaceSpaces=nullptr;
configFilename=nullptr;
}
origOpts=opts;
if (props&Prop_Folder) {
musicFolder->setText(Utils::convertPathForDisplay(path));
connect(musicFolder, SIGNAL(textChanged(const QString &)), this, SLOT(checkSaveable()));
if (disabledProps&Prop_Folder) {
musicFolder->setDisabled(true);
}
} else {
REMOVE(musicFolder);
REMOVE(musicFolderLabel);
}
if (allowCovers) {
albumCovers->setEditable(props&Prop_CoversAll);
if (origOpts.coverName==Device::constNoCover) {
origOpts.coverName=noCoverText;
albumCovers->setCurrentIndex(0);
}
if (origOpts.coverName==Device::constEmbedCover) {
origOpts.coverName=embedCoverText;
albumCovers->setCurrentIndex(1);
} else {
albumCovers->setCurrentIndex(0);
for (int i=1; i<albumCovers->count(); ++i) {
if (albumCovers->itemText(i)==origOpts.coverName) {
albumCovers->setCurrentIndex(i);
break;
}
}
}
if (0!=origOpts.coverMaxSize) {
int coverMax=origOpts.coverMaxSize/100;
if (coverMax<0 || coverMax>=coverMaxSize->count()) {
coverMax=0;
}
coverMaxSize->setCurrentIndex(0==coverMax ? 0 : (coverMaxSize->count()-coverMax));
} else {
coverMaxSize->setCurrentIndex(0);
}
albumCovers->setValidator(new CoverNameValidator(this));
connect(albumCovers, SIGNAL(editTextChanged(const QString &)), this, SLOT(albumCoversChanged()));
connect(coverMaxSize, SIGNAL(currentIndexChanged(int)), this, SLOT(checkSaveable()));
} else {
REMOVE(albumCovers);
REMOVE(albumCoversLabel);
REMOVE(coverMaxSize);
REMOVE(coverMaxSizeLabel);
}
if (props&Prop_Va) {
fixVariousArtists->setChecked(opts.fixVariousArtists);
connect(fixVariousArtists, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
} else {
REMOVE(fixVariousArtists);
}
if (props&Prop_Cache) {
useCache->setChecked(opts.useCache);
connect(useCache, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
} else {
REMOVE(useCache);
}
if (props&Prop_AutoScan) {
autoScan->setChecked(opts.autoScan);
connect(autoScan, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
} else {
REMOVE(autoScan);
}
if (props&Prop_Transcoder || props&Prop_Encoder) {
bool transcode=props&Prop_Transcoder;
transcoderName->clear();
if (transcode) {
transcoderName->addItem(tr("Do not transcode"), QString());
transcoderName->setCurrentIndex(0);
transcoderValue->setVisible(false);
transcoderWhen->addItem(tr("Always transcode"), DeviceOptions::TW_Always);
transcoderWhen->addItem(tr("Only transcode if source file is of a different format"), DeviceOptions::TW_IfDifferent);
transcoderWhen->addItem(tr("Only transcode if source is FLAC/WAV"), DeviceOptions::TW_IfLossess);
transcoderWhen->setVisible(false);
transcoderWhen->setCurrentIndex(0);
for (int i=0; i<transcoderWhen->count(); ++i) {
if (transcoderWhen->itemData(i).toInt()==opts.transcoderWhen) {
transcoderWhen->setCurrentIndex(i);
break;
}
}
connect(transcoderWhen, SIGNAL(currentIndexChanged(int)), this, SLOT(checkSaveable()));
} else {
transcoderFrame->setTitle(tr("Encoder"));
REMOVE(transcoderWhen);
}
QList<Encoders::Encoder> encs=Encoders::getAvailable();
if (encs.isEmpty()) {
transcoderFrame->setVisible(false);
} else {
for (const Encoders::Encoder &e: encs) {
if (!transcode || e.transcoder) {
QString name=e.name;
if (transcode && name.endsWith(QLatin1String(" (ffmpeg)"))) {
name=name.left(name.length()-9);
}
transcoderName->addItem(transcode ? tr("Transcode to %1").arg(name) : name, e.codec);
}
}
if (opts.transcoderCodec.isEmpty()) {
transcoderChanged();
} else {
Encoders::Encoder enc=Encoders::getEncoder(opts.transcoderCodec);
if (!enc.isNull()) {
for (int i=1; i<transcoderName->count(); ++i) {
if (transcoderName->itemData(i).toString()==opts.transcoderCodec) {
transcoderName->setCurrentIndex(i);
transcoderChanged();
transcoderValue->setValue(opts.transcoderValue);
break;
}
}
}
}
}
connect(transcoderName, SIGNAL(currentIndexChanged(int)), this, SLOT(transcoderChanged()));
connect(transcoderValue, SIGNAL(valueChanged(int)), this, SLOT(checkSaveable()));
} else {
REMOVE(transcoderFrame);
}
if (storage.count()<2) {
REMOVE(defaultVolume);
REMOVE(defaultVolumeLabel);
} else {
for (const DeviceStorage &ds: storage) {
defaultVolume->addItem(tr("%1 (%2 free)", "name (size free)")
.arg(ds.description).arg(Utils::formatByteSize(ds.size-ds.used)), ds.volumeIdentifier);
}
for (int i=0; i<defaultVolume->count(); ++i) {
if (defaultVolume->itemData(i).toString()==opts.volumeId) {
defaultVolume->setCurrentIndex(i);
break;
}
}
connect(defaultVolume,SIGNAL(currentIndexChanged(int)), this, SLOT(checkSaveable()));
}
origMusicFolder=Utils::fixPath(path);
if (props&Prop_FileName) {
connect(configFilename, SIGNAL(clicked()), SLOT(configureFilenameScheme()));
connect(filenameScheme, SIGNAL(textChanged(const QString &)), this, SLOT(checkSaveable()));
connect(vfatSafe, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
connect(asciiOnly, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
connect(ignoreThe, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
connect(replaceSpaces, SIGNAL(stateChanged(int)), this, SLOT(checkSaveable()));
}
if (albumCovers) {
albumCoversChanged();
}
QTimer::singleShot(0, this, SLOT(setSize()));
}
void DevicePropertiesWidget::transcoderChanged()
{
QString codec=transcoderName->itemData(transcoderName->currentIndex()).toString();
if (codec.isEmpty()) {
transcoderName->setToolTip(QString());
transcoderValue->setVisible(false);
if (transcoderWhen) {
transcoderWhen->setVisible(false);
}
} else {
Encoders::Encoder enc=Encoders::getEncoder(codec);
transcoderName->setToolTip(enc.description);
if (transcoderWhen) {
transcoderWhen->setVisible(true);
}
if (enc.values.count()) {
transcoderValue->setValues(enc);
transcoderValue->setVisible(true);
} else {
transcoderValue->setVisible(false);
}
}
Utils::resizeWindow(this, true, false);
checkSaveable();
}
void DevicePropertiesWidget::albumCoversChanged()
{
if (coverMaxSize) {
bool enableSize=albumCovers->currentText()!=noCoverText;
coverMaxSize->setEnabled(enableSize);
coverMaxSizeLabel->setEnabled(enableSize);
}
checkSaveable();
}
void DevicePropertiesWidget::checkSaveable()
{
DeviceOptions opts=settings();
bool checkFolder=musicFolder ? musicFolder->isEnabled() : false;
modified=opts!=origOpts;
if (!modified && checkFolder) {
modified=music()!=origMusicFolder;
}
saveable=!opts.scheme.isEmpty() && (!checkFolder || !music().isEmpty()) && !opts.coverName.isEmpty();
if (saveable &&
( (-1!=opts.coverName.indexOf(noCoverText) && opts.coverName!=noCoverText) ||
(-1!=opts.coverName.indexOf(embedCoverText) && opts.coverName!=embedCoverText) ) ) {
saveable=false;
}
emit updated();
}
void DevicePropertiesWidget::configureFilenameScheme()
{
if (!schemeDlg) {
schemeDlg=new FilenameSchemeDialog(this);
connect(schemeDlg, SIGNAL(scheme(const QString &)), filenameScheme, SLOT(setText(const QString &)));
}
schemeDlg->show(settings());
}
DeviceOptions DevicePropertiesWidget::settings()
{
DeviceOptions opts;
if (name && name->isEnabled()) {
opts.name=name->text().trimmed();
}
if (filenameScheme) {
opts.scheme=filenameScheme->text().trimmed();
}
if (vfatSafe) {
opts.vfatSafe=vfatSafe->isChecked();
}
if (asciiOnly) {
opts.asciiOnly=asciiOnly->isChecked();
}
if (ignoreThe) {
opts.ignoreThe=ignoreThe->isChecked();
}
if (replaceSpaces) {
opts.replaceSpaces=replaceSpaces->isChecked();
}
opts.fixVariousArtists=fixVariousArtists ? fixVariousArtists->isChecked() : false;
opts.useCache=useCache ? useCache->isChecked() : false;
opts.autoScan=autoScan ? autoScan->isChecked() : false;
opts.transcoderCodec=QString();
opts.transcoderValue=0;
opts.transcoderWhen=DeviceOptions::TW_Always;
opts.coverName=cover();
opts.coverMaxSize=(!coverMaxSize || 0==coverMaxSize->currentIndex()) ? 0 : ((coverMaxSize->count()-coverMaxSize->currentIndex())*100);
opts.volumeId=defaultVolume && defaultVolume->isVisible() ? defaultVolume->itemData(defaultVolume->currentIndex()).toString() : QString();
if (transcoderFrame && transcoderFrame->isVisible()) {
opts.transcoderCodec=transcoderName->itemData(transcoderName->currentIndex()).toString();
if (!opts.transcoderCodec.isEmpty()) {
Encoders::Encoder enc=Encoders::getEncoder(opts.transcoderCodec);
if (transcoderWhen) {
opts.transcoderWhen=(DeviceOptions::TranscodeWhen)transcoderWhen->itemData(transcoderWhen->currentIndex()).toUInt();
}
if (!enc.isNull() && transcoderValue->value()<enc.values.count()) {
opts.transcoderValue=enc.values.at(transcoderValue->value()).value;
}
}
}
return opts;
}
QString DevicePropertiesWidget::cover() const
{
QString coverName=albumCovers ? albumCovers->currentText().trimmed() : noCoverText;
return coverName==noCoverText
? Device::constNoCover
: coverName==embedCoverText
? Device::constEmbedCover
: coverName;
}
void DevicePropertiesWidget::setSize()
{
Utils::resizeWindow(this, true, false);
}
#include "moc_devicepropertieswidget.cpp"