Improve handling of targets

This commit is contained in:
Martchus 2016-05-26 02:15:41 +02:00
parent ae08e43e6a
commit c1269c7945
23 changed files with 1722 additions and 1391 deletions

View File

@ -14,6 +14,7 @@ set(META_VERSION_PATCH 1)
# add project files # add project files
set(HEADER_FILES set(HEADER_FILES
application/knownfieldmodel.h application/knownfieldmodel.h
application/targetlevelmodel.h
application/main.h application/main.h
application/settings.h application/settings.h
cli/mainfeatures.h cli/mainfeatures.h
@ -22,6 +23,7 @@ set(HEADER_FILES
) )
set(SRC_FILES set(SRC_FILES
application/knownfieldmodel.cpp application/knownfieldmodel.cpp
application/targetlevelmodel.cpp
application/main.cpp application/main.cpp
application/settings.cpp application/settings.cpp
cli/mainfeatures.cpp cli/mainfeatures.cpp
@ -95,6 +97,7 @@ set(WIDGETS_UI_FILES
gui/id3v2optionpage.ui gui/id3v2optionpage.ui
gui/id3v1optionpage.ui gui/id3v1optionpage.ui
gui/tagprocessinggeneraloptionpage.ui gui/tagprocessinggeneraloptionpage.ui
gui/tagprocessingtargetsoptionpage.ui
gui/editorgeneraloptionpage.ui gui/editorgeneraloptionpage.ui
gui/filebrowsergeneraloptionpage.ui gui/filebrowsergeneraloptionpage.ui
gui/mainwindow.ui gui/mainwindow.ui

View File

@ -61,6 +61,7 @@ KnownFieldModel::KnownFieldModel(QObject *parent, DefaultSelection defaultSelect
ChecklistModel(parent) ChecklistModel(parent)
{ {
QList<ChecklistItem> items; QList<ChecklistItem> items;
items.reserve(27);
Qt::CheckState defaultSelected = defaultSelection == DefaultSelection::CommonFields ? Qt::Checked : Qt::Unchecked; Qt::CheckState defaultSelected = defaultSelection == DefaultSelection::CommonFields ? Qt::Checked : Qt::Unchecked;
items << mkItem(KnownField::Title, defaultSelected); items << mkItem(KnownField::Title, defaultSelected);
items << mkItem(KnownField::Album, defaultSelected); items << mkItem(KnownField::Album, defaultSelected);

View File

@ -1,5 +1,5 @@
#ifndef KNOWNFIELDSELECTION_H #ifndef KNOWNFIELDMODEL_H
#define KNOWNFIELDSELECTION_H #define KNOWNFIELDMODEL_H
#include <qtutilities/models/checklistmodel.h> #include <qtutilities/models/checklistmodel.h>
@ -40,4 +40,4 @@ inline Models::ChecklistItem KnownFieldModel::mkItem(Media::KnownField field, Qt
} }
#endif // KNOWNFIELDSELECTION_H #endif // KNOWNFIELDMODEL_H

View File

@ -31,24 +31,24 @@ SetTagInfoArgs::SetTagInfoArgs(Argument &filesArg, Argument &verboseArg) :
filesArg(filesArg), filesArg(filesArg),
verboseArg(verboseArg), verboseArg(verboseArg),
docTitleArg("doc-title", "d", "specifies the document title (has no affect if not supported by the container)"), docTitleArg("doc-title", "d", "specifies the document title (has no affect if not supported by the container)"),
removeOtherFieldsArg("remove-other-fields", string(), "if present ALL unspecified tag fields will be removed (to remove a specific field use eg. \"album=\")"), removeOtherFieldsArg("remove-other-fields", nullptr, "if present ALL unspecified tag fields will be removed (to remove a specific field use eg. \"album=\")"),
treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", string(), "if present unknown files will be treatet as MP3 files"), treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", nullptr, "if present unknown files will be treatet as MP3 files"),
id3v1UsageArg("id3v1-usage", string(), "specifies the ID3v1 usage (only used when already present by default); only relevant when dealing with MP3 files (or files treated as such)"), id3v1UsageArg("id3v1-usage", nullptr, "specifies the ID3v1 usage (only used when already present by default); only relevant when dealing with MP3 files (or files treated as such)"),
id3v2UsageArg("id3v2-usage", string(), "specifies the ID3v2 usage (always used by default); only relevant when dealing with MP3 files (or files treated as such)"), id3v2UsageArg("id3v2-usage", nullptr, "specifies the ID3v2 usage (always used by default); only relevant when dealing with MP3 files (or files treated as such)"),
mergeMultipleSuccessiveTagsArg("merge-successive-tags", string(), "if present multiple successive ID3v2 tags will be merged"), mergeMultipleSuccessiveTagsArg("merge-successive-tags", nullptr, "if present multiple successive ID3v2 tags will be merged"),
id3v2VersionArg("id3v2-version", string(), "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used"), id3v2VersionArg("id3v2-version", nullptr, "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used"),
encodingArg("encoding", string(), "specifies the preferred encoding"), encodingArg("encoding", nullptr, "specifies the preferred encoding"),
removeTargetsArg("remove-targets", string(), "removes all tags with the specified targets (which must be separated by \",\")"), removeTargetsArg("remove-targets", nullptr, "removes all tags with the specified targets (which must be separated by \",\")"),
attachmentsArg("attachments", string(), "specifies attachments to be added/updated/removed (multiple attachments must be separated by \",\""), attachmentsArg("attachments", nullptr, "specifies attachments to be added/updated/removed (multiple attachments must be separated by \",\""),
removeExistingAttachmentsArg("remove-existing-attachments", "ra", "specifies names/IDs of existing attachments to be removed"), removeExistingAttachmentsArg("remove-existing-attachments", "ra", "specifies names/IDs of existing attachments to be removed"),
minPaddingArg("min-padding", string(), "specifies the minimum padding before the media data"), minPaddingArg("min-padding", nullptr, "specifies the minimum padding before the media data"),
maxPaddingArg("max-padding", string(), "specifies the maximum padding before the media data"), maxPaddingArg("max-padding", nullptr, "specifies the maximum padding before the media data"),
prefPaddingArg("preferred-padding", string(), "specifies the preferred padding before the media data"), prefPaddingArg("preferred-padding", nullptr, "specifies the preferred padding before the media data"),
tagPosArg("tag-pos", string(), "specifies the preferred tag position"), tagPosArg("tag-pos", nullptr, "specifies the preferred tag position"),
forceTagPosArg("force-tag-pos", string(), "forces the specified tag postion to be used even if it requires the file to be rewritten"), forceTagPosArg("force-tag-pos", nullptr, "forces the specified tag postion to be used even if it requires the file to be rewritten"),
indexPosArg("index-pos", string(), "specifies the preferred index position"), indexPosArg("index-pos", nullptr, "specifies the preferred index position"),
forceIndexPosArg("force-index-pos", string(), "forces the specified index postion to be used even if it requires the file to be rewritten"), forceIndexPosArg("force-index-pos", nullptr, "forces the specified index postion to be used even if it requires the file to be rewritten"),
forceRewriteArg("force-rewrite", string(), "forces the file to rewritten from the scratch"), forceRewriteArg("force-rewrite", nullptr, "forces the file to rewritten from the scratch"),
setTagInfoArg("set-tag-info", "set", "sets the values of all specified tag fields") setTagInfoArg("set-tag-info", "set", "sets the values of all specified tag fields")
{ {
docTitleArg.setCombinable(true); docTitleArg.setCombinable(true);
@ -135,7 +135,7 @@ int main(int argc, char *argv[])
outputFileArg.setRequired(true); outputFileArg.setRequired(true);
outputFileArg.setCombinable(true); outputFileArg.setCombinable(true);
// print field names // print field names
Argument printFieldNamesArg("print-field-names", string(), "prints available field names"); Argument printFieldNamesArg("print-field-names", nullptr, "prints available field names");
printFieldNamesArg.setCallback(Cli::printFieldNames); printFieldNamesArg.setCallback(Cli::printFieldNames);
// display general file info // display general file info
Argument displayFileInfoArg("display-file-info", "info", "displays general file information"); Argument displayFileInfoArg("display-file-info", "info", "displays general file information");
@ -162,19 +162,19 @@ int main(int argc, char *argv[])
Argument validateArg("validate", "c", "validates the file integrity as accurately as possible; the structure of the file will be parsed completely"); Argument validateArg("validate", "c", "validates the file integrity as accurately as possible; the structure of the file will be parsed completely");
validateArg.setDenotesOperation(true); validateArg.setDenotesOperation(true);
validateArg.setCombinable(true); validateArg.setCombinable(true);
Argument genInfoArg("html-info", string(), "generates technical information about the specified file as HTML document"); Argument genInfoArg("html-info", nullptr, "generates technical information about the specified file as HTML document");
genInfoArg.setDenotesOperation(true); genInfoArg.setDenotesOperation(true);
genInfoArg.setSecondaryArguments({&fileArg, &validateArg, &outputFileArg}); genInfoArg.setSecondaryArguments({&fileArg, &validateArg, &outputFileArg});
genInfoArg.setCallback(std::bind(Cli::generateFileInfo, _1, std::cref(fileArg), std::cref(outputFileArg), std::cref(validateArg))); genInfoArg.setCallback(std::bind(Cli::generateFileInfo, _1, std::cref(fileArg), std::cref(outputFileArg), std::cref(validateArg)));
// remove backup files // remove backup files
Argument remBackupFilesArg("remove-backup-files", string(), "removes all files with \".bak\" suffix in the given directory and in subdirectories if recursive option is present"); Argument remBackupFilesArg("remove-backup-files", nullptr, "removes all files with \".bak\" suffix in the given directory and in subdirectories if recursive option is present");
remBackupFilesArg.setDenotesOperation(true); remBackupFilesArg.setDenotesOperation(true);
remBackupFilesArg.setCallback(std::bind(Cli::removeBackupFiles, _1, std::cref(recursiveArg))); remBackupFilesArg.setCallback(std::bind(Cli::removeBackupFiles, _1, std::cref(recursiveArg)));
remBackupFilesArg.setValueNames({"directory"}); remBackupFilesArg.setValueNames({"directory"});
remBackupFilesArg.setRequiredValueCount(1); remBackupFilesArg.setRequiredValueCount(1);
remBackupFilesArg.setSecondaryArguments({&recursiveArg}); remBackupFilesArg.setSecondaryArguments({&recursiveArg});
// renaming utility // renaming utility
Argument renamingUtilityArg("renaming-utility", string(), "launches the renaming utility instead of the main GUI"); Argument renamingUtilityArg("renaming-utility", nullptr, "launches the renaming utility instead of the main GUI");
renamingUtilityArg.setCombinable(true); renamingUtilityArg.setCombinable(true);
// set arguments to parser // set arguments to parser
qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(&filesArg); qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(&filesArg);

View File

@ -1,5 +1,6 @@
#include "./settings.h" #include "./settings.h"
#include "./knownfieldmodel.h" #include "./knownfieldmodel.h"
#include "./targetlevelmodel.h"
#include <tagparser/mediafileinfo.h> #include <tagparser/mediafileinfo.h>
#include <tagparser/tag.h> #include <tagparser/tag.h>
@ -169,6 +170,13 @@ size_t &preferredPadding()
return v; return v;
} }
// targets
TargetLevelModel &defaultTargetsModel()
{
static TargetLevelModel model(nullptr, TargetLevelModel::DefaultSelection::MostUsefulTargets);
return model;
}
// fields // fields
KnownFieldModel &selectedFieldsModel() KnownFieldModel &selectedFieldsModel()
{ {
@ -387,6 +395,7 @@ void restore()
keepVersionOfExistingId3v2Tag() = settings.value(QStringLiteral("keepversionofexistingtag"), true).toBool(); keepVersionOfExistingId3v2Tag() = settings.value(QStringLiteral("keepversionofexistingtag"), true).toBool();
mergeMultipleSuccessiveId3v2Tags() = settings.value(QStringLiteral("mergemultiplesuccessivetags"), true).toBool(); mergeMultipleSuccessiveId3v2Tags() = settings.value(QStringLiteral("mergemultiplesuccessivetags"), true).toBool();
settings.endGroup(); settings.endGroup();
defaultTargetsModel().restore(settings, QStringLiteral("targets"));
settings.beginGroup(QStringLiteral("filelayout")); settings.beginGroup(QStringLiteral("filelayout"));
forceRewrite() = settings.value(QStringLiteral("forcerewrite"), true).toBool(); forceRewrite() = settings.value(QStringLiteral("forcerewrite"), true).toBool();
switch(settings.value(QStringLiteral("tagpos")).toInt()) { switch(settings.value(QStringLiteral("tagpos")).toInt()) {
@ -483,6 +492,7 @@ void save()
settings.setValue(QStringLiteral("keepversionofexistingtag"), keepVersionOfExistingId3v2Tag()); settings.setValue(QStringLiteral("keepversionofexistingtag"), keepVersionOfExistingId3v2Tag());
settings.setValue(QStringLiteral("mergemultiplesuccessivetags"), mergeMultipleSuccessiveId3v2Tags()); settings.setValue(QStringLiteral("mergemultiplesuccessivetags"), mergeMultipleSuccessiveId3v2Tags());
settings.endGroup(); settings.endGroup();
defaultTargetsModel().save(settings, QStringLiteral("targets"));
settings.beginGroup(QStringLiteral("filelayout")); settings.beginGroup(QStringLiteral("filelayout"));
settings.setValue(QStringLiteral("forcerewrite"), forceRewrite()); settings.setValue(QStringLiteral("forcerewrite"), forceRewrite());
settings.setValue(QStringLiteral("tagpos"), static_cast<int>(preferredTagPosition())); settings.setValue(QStringLiteral("tagpos"), static_cast<int>(preferredTagPosition()));

View File

@ -79,6 +79,10 @@ size_t &minPadding();
size_t &maxPadding(); size_t &maxPadding();
size_t &preferredPadding(); size_t &preferredPadding();
// targets
class TargetLevelModel;
TargetLevelModel &defaultTargetsModel();
// fields // fields
class KnownFieldModel; class KnownFieldModel;
KnownFieldModel &selectedFieldsModel(); KnownFieldModel &selectedFieldsModel();

View File

@ -0,0 +1,92 @@
#include "./targetlevelmodel.h"
#include <tagparser/tagtarget.h>
using namespace Models;
using namespace Media;
namespace Settings {
/*
TRANSLATOR Settings::TargetLevelModel
Necessary for lupdate.
*/
const char *TargetLevelModel::fieldName(TagTargetLevel targetLevel)
{
switch(targetLevel) {
case TagTargetLevel::Unspecified:
return QT_TR_NOOP("unspecified, everything");
case TagTargetLevel::Shot:
return QT_TR_NOOP("shot");
case TagTargetLevel::Subtrack:
return QT_TR_NOOP("subtrack, part, movement, scene");
case TagTargetLevel::Track:
return QT_TR_NOOP("track, song, chapter");
case TagTargetLevel::Part:
return QT_TR_NOOP("part, session");
case TagTargetLevel::Album:
return QT_TR_NOOP("album, opera, concert, movie, episode");
case TagTargetLevel::Edition:
return QT_TR_NOOP("edition, issue, volume, opus, season, sequel");
case TagTargetLevel::Collection:
return QT_TR_NOOP("collection");
default:
return QT_TR_NOOP("unknown");
}
}
QString TargetLevelModel::translatedFieldName(TagTargetLevel targetLevel)
{
return tr(fieldName(targetLevel));
}
QString TargetLevelModel::labelForId(const QVariant &id) const
{
return translatedFieldName(static_cast<TagTargetLevel>(id.toInt()));
}
TargetLevelModel::TargetLevelModel(QObject *parent, DefaultSelection defaultSelection) :
ChecklistModel(parent)
{
QList<ChecklistItem> items;
items.reserve(8);
Qt::CheckState defaultSelected = defaultSelection == DefaultSelection::MostUsefulTargets ? Qt::Checked : Qt::Unchecked;
items << mkItem(TagTargetLevel::Unspecified, defaultSelected);
items << mkItem(TagTargetLevel::Shot, Qt::Unchecked);
items << mkItem(TagTargetLevel::Subtrack, Qt::Unchecked);
items << mkItem(TagTargetLevel::Track, defaultSelected);
items << mkItem(TagTargetLevel::Part, Qt::Unchecked);
items << mkItem(TagTargetLevel::Album, Qt::Unchecked);
items << mkItem(TagTargetLevel::Edition, Qt::Unchecked);
items << mkItem(TagTargetLevel::Collection, Qt::Unchecked);
setItems(items);
}
TargetLevelModel::TargetLevelModel(const QList<Models::ChecklistItem> &items, QObject *parent) :
ChecklistModel(parent)
{
setItems(items);
}
QVariant TargetLevelModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch(orientation) {
case Qt::Horizontal:
switch(role) {
case Qt::DisplayRole:
switch(section) {
case 0:
return tr("Target level");
default:
;
}
}
break;
default:
;
}
return QVariant();
}
}

View File

@ -0,0 +1,43 @@
#ifndef TARGETLEVELMODEL_H
#define TARGETLEVELMODEL_H
#include <qtutilities/models/checklistmodel.h>
#include <QAbstractListModel>
#include <QList>
namespace Media {
DECLARE_ENUM(TagTargetLevel, unsigned char)
}
namespace Settings {
class TargetLevelModel : public Models::ChecklistModel
{
Q_OBJECT
public:
enum class DefaultSelection
{
None,
MostUsefulTargets
};
static const char *fieldName(Media::TagTargetLevel targetLevel);
static QString translatedFieldName(Media::TagTargetLevel targetLevel);
static Models::ChecklistItem mkItem(Media::TagTargetLevel targetLevel, Qt::CheckState checkState = Qt::Checked);
explicit TargetLevelModel(QObject *parent = nullptr, DefaultSelection defaultSelection = DefaultSelection::None);
explicit TargetLevelModel(const QList<Models::ChecklistItem> &items, QObject *parent = nullptr);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual QString labelForId(const QVariant &id) const;
};
inline Models::ChecklistItem TargetLevelModel::mkItem(Media::TagTargetLevel field, Qt::CheckState checkState)
{
return Models::ChecklistItem(static_cast<int>(field), translatedFieldName(field), checkState);
}
}
#endif // TARGETLEVELMODEL_H

View File

@ -791,7 +791,7 @@ void displayTagInfo(const StringVector &parameterValues, const Argument &filesAr
// write tag name and target, eg. MP4/iTunes tag // write tag name and target, eg. MP4/iTunes tag
cout << tag->typeName(); cout << tag->typeName();
if(!tag->target().isEmpty()) { if(!tag->target().isEmpty()) {
cout << " targeting \"" << tag->target().toString() << "\""; cout << " targeting \"" << tag->targetString() << "\"";
} }
cout << endl; cout << endl;
// iterate through fields specified by the user // iterate through fields specified by the user

View File

@ -64,7 +64,8 @@ EnterTargetDialog::EnterTargetDialog(QWidget *parent) :
m_tracksModel(new ChecklistModel(this)), m_tracksModel(new ChecklistModel(this)),
m_chaptersModel(new ChecklistModel(this)), m_chaptersModel(new ChecklistModel(this)),
m_editionsModel(new ChecklistModel(this)), m_editionsModel(new ChecklistModel(this)),
m_attachmentsModel(new ChecklistModel(this)) m_attachmentsModel(new ChecklistModel(this)),
m_currentContainerFormat(ContainerFormat::Unknown)
{ {
// setup UI // setup UI
m_ui->setupUi(this); m_ui->setupUi(this);
@ -79,7 +80,7 @@ EnterTargetDialog::EnterTargetDialog(QWidget *parent) :
m_ui->attachmentsListView->setModel(m_attachmentsModel); m_ui->attachmentsListView->setModel(m_attachmentsModel);
// connect signals and slots // connect signals and slots
connect(m_ui->levelSpinBox, static_cast<void (QSpinBox:: *) (int)>(&QSpinBox::valueChanged), this, &EnterTargetDialog::updateLevelNamePlaceholderText); connect(m_ui->levelSpinBox, static_cast<void (QSpinBox:: *) (int)>(&QSpinBox::valueChanged), this, &EnterTargetDialog::updateLevelNamePlaceholderText);
connect(m_ui->confirmPushButton, &QPushButton::clicked, this, &EnterTargetDialog::accept); connect(m_ui->confirmPushButton, &QPushButton::clicked, this, &EnterTargetDialog::accept);
connect(m_ui->abortPushButton, &QPushButton::clicked, this, &EnterTargetDialog::reject); connect(m_ui->abortPushButton, &QPushButton::clicked, this, &EnterTargetDialog::reject);
} }
@ -88,7 +89,8 @@ EnterTargetDialog::~EnterTargetDialog()
void EnterTargetDialog::updateLevelNamePlaceholderText(int i) void EnterTargetDialog::updateLevelNamePlaceholderText(int i)
{ {
m_ui->levelNameLineEdit->setPlaceholderText(QString::fromLocal8Bit(matroskaTargetTypeName(static_cast<uint32>(i)))); const char *levelName = i >= 0 ? tagTargetLevelName(containerTargetLevel(m_currentContainerFormat, static_cast<uint32>(i))) : nullptr;
m_ui->levelNameLineEdit->setPlaceholderText(levelName ? QString::fromUtf8(levelName) : QString());
} }
Media::TagTarget EnterTargetDialog::target() const Media::TagTarget EnterTargetDialog::target() const
@ -105,6 +107,7 @@ Media::TagTarget EnterTargetDialog::target() const
void EnterTargetDialog::setTarget(const TagTarget &target, const MediaFileInfo *file) void EnterTargetDialog::setTarget(const TagTarget &target, const MediaFileInfo *file)
{ {
m_currentContainerFormat = file ? file->containerFormat() : ContainerFormat::Unknown;
if(m_ui->levelSpinBox->maximum() >= 0 if(m_ui->levelSpinBox->maximum() >= 0
&& target.level() <= static_cast<unsigned int>(m_ui->levelSpinBox->maximum()) && target.level() <= static_cast<unsigned int>(m_ui->levelSpinBox->maximum())
&& (m_ui->levelSpinBox->minimum() < 0 || target.level() >= static_cast<unsigned int>(m_ui->levelSpinBox->minimum()))) { && (m_ui->levelSpinBox->minimum() < 0 || target.level() >= static_cast<unsigned int>(m_ui->levelSpinBox->minimum()))) {

View File

@ -13,6 +13,7 @@ class ChecklistModel;
namespace Media { namespace Media {
class MediaFileInfo; class MediaFileInfo;
enum class ContainerFormat;
} }
namespace QtGui { namespace QtGui {
@ -42,6 +43,7 @@ private:
Models::ChecklistModel *m_chaptersModel; Models::ChecklistModel *m_chaptersModel;
Models::ChecklistModel *m_editionsModel; Models::ChecklistModel *m_editionsModel;
Models::ChecklistModel *m_attachmentsModel; Models::ChecklistModel *m_attachmentsModel;
Media::ContainerFormat m_currentContainerFormat;
}; };
} }

View File

@ -339,7 +339,7 @@ void FileInfoModel::updateCache()
auto *tagItem = defaultItem(tag->typeName()); auto *tagItem = defaultItem(tag->typeName());
ItemHelper tagHelper(tagItem); ItemHelper tagHelper(tagItem);
tagHelper.appendRow(tr("Version"), tag->version()); tagHelper.appendRow(tr("Version"), tag->version());
tagHelper.appendRow(tr("Target level"), tag->target().toString()); tagHelper.appendRow(tr("Target level"), tag->targetString());
tagHelper.appendRow(tr("Size"), dataSizeToString(tag->size())); tagHelper.appendRow(tr("Size"), dataSizeToString(tag->size()));
tagHelper.appendRow(tr("Field count"), tag->fieldCount()); tagHelper.appendRow(tr("Field count"), tag->fieldCount());
tagsItem->appendRow(tagItem); tagsItem->appendRow(tagItem);

View File

@ -318,13 +318,10 @@ void MainWindow::showSettingsDlg()
{ {
if(!m_settingsDlg) { if(!m_settingsDlg) {
m_settingsDlg = new SettingsDialog(this); m_settingsDlg = new SettingsDialog(this);
//connect(m_settingsDlg, &SettingsDialog::accept, this, &MainWindow::applySettingsFromDialog); connect(m_settingsDlg, &SettingsDialog::applied, this, &MainWindow::applySettingsFromDialog);
//connect(m_settingsDlg, &SettingsDialog::accept, m_ui->tagEditorWidget, &TagEditorWidget::applySettingsFromDialog); connect(m_settingsDlg, &SettingsDialog::applied, m_ui->tagEditorWidget, &TagEditorWidget::applySettingsFromDialog);
}
if(m_settingsDlg->exec() == QDialog::Accepted) {
applySettingsFromDialog();
m_ui->tagEditorWidget->applySettingsFromDialog();
} }
m_settingsDlg->exec();
} }
/*! /*!

View File

@ -3,6 +3,7 @@
#include "../application/settings.h" #include "../application/settings.h"
#include "../application/knownfieldmodel.h" #include "../application/knownfieldmodel.h"
#include "../application/targetlevelmodel.h"
#include <tagparser/mediafileinfo.h> #include <tagparser/mediafileinfo.h>
#include <tagparser/backuphelper.h> #include <tagparser/backuphelper.h>
@ -140,6 +141,7 @@ QWidget *EditorTempOptionPage::setupWidget()
// EditorFieldsOptionPage // EditorFieldsOptionPage
EditorFieldsOptionPage::EditorFieldsOptionPage(QWidget *parentWidget) : EditorFieldsOptionPage::EditorFieldsOptionPage(QWidget *parentWidget) :
EditorFieldsOptionPageBase(parentWidget),
m_model(nullptr) m_model(nullptr)
{} {}
@ -443,6 +445,40 @@ void Id3v2OptionPage::reset()
} }
} }
// TagProcessingTargetsOptionPage
TagProcessingTargetsOptionPage::TagProcessingTargetsOptionPage(QWidget *parentWidget) :
TagProcessingTargetsOptionPageBase(parentWidget),
m_model(nullptr)
{}
TagProcessingTargetsOptionPage::~TagProcessingTargetsOptionPage()
{}
bool TagProcessingTargetsOptionPage::apply()
{
if(hasBeenShown() && m_model) {
Settings::defaultTargetsModel().setItems(m_model->items());
}
return true;
}
void TagProcessingTargetsOptionPage::reset()
{
if(hasBeenShown() && m_model) {
m_model->setItems(Settings::defaultTargetsModel().items());
}
}
QWidget *TagProcessingTargetsOptionPage::setupWidget()
{
auto *w = TagProcessingTargetsOptionPageBase::setupWidget();
if(!m_model) {
m_model = new TargetLevelModel(w);
}
ui()->targetsToBeAddedListView->setModel(m_model);
return w;
}
// FileLayoutPage // FileLayoutPage
FileLayoutPage::FileLayoutPage(QWidget *parentWidget) : FileLayoutPage::FileLayoutPage(QWidget *parentWidget) :
FileLayoutPageBase(parentWidget) FileLayoutPageBase(parentWidget)
@ -548,8 +584,8 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
category = new Dialogs::OptionCategory(this); category = new Dialogs::OptionCategory(this);
category->setDisplayName(tr("Tag processing")); category->setDisplayName(tr("Tag processing"));
category->assignPages(QList<Dialogs::OptionPage *>() category->assignPages(QList<Dialogs::OptionPage *>()
<< new TagProcessingGeneralOptionPage << new TagProcessingGeneralOptionPage << new Id3v1OptionPage
<< new Id3v1OptionPage << new Id3v2OptionPage << new FileLayoutPage); << new Id3v2OptionPage << new TagProcessingTargetsOptionPage << new FileLayoutPage);
category->setIcon(QIcon::fromTheme(QStringLiteral("tag"), QIcon(QStringLiteral(":/tageditor/icons/hicolor/32x32/settingscategories/tag.png")))); category->setIcon(QIcon::fromTheme(QStringLiteral("tag"), QIcon(QStringLiteral(":/tageditor/icons/hicolor/32x32/settingscategories/tag.png"))));
categories << category; categories << category;

View File

@ -11,6 +11,7 @@
#include "ui_tagprocessinggeneraloptionpage.h" #include "ui_tagprocessinggeneraloptionpage.h"
#include "ui_id3v1optionpage.h" #include "ui_id3v1optionpage.h"
#include "ui_id3v2optionpage.h" #include "ui_id3v2optionpage.h"
#include "ui_tagprocessingtargetsoptionpage.h"
#include "ui_filelayout.h" #include "ui_filelayout.h"
#include <qtutilities/settingsdialog/settingsdialog.h> #include <qtutilities/settingsdialog/settingsdialog.h>
@ -20,6 +21,7 @@
namespace Settings { namespace Settings {
class KnownFieldModel; class KnownFieldModel;
class TargetLevelModel;
} }
DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE(QtAppearanceOptionPage) DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE(QtAppearanceOptionPage)
@ -54,6 +56,11 @@ DECLARE_UI_FILE_BASED_OPTION_PAGE(Id3v1OptionPage)
DECLARE_UI_FILE_BASED_OPTION_PAGE(Id3v2OptionPage) DECLARE_UI_FILE_BASED_OPTION_PAGE(Id3v2OptionPage)
BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(TagProcessingTargetsOptionPage)
DECLARE_SETUP_WIDGETS
Settings::TargetLevelModel *m_model;
END_DECLARE_OPTION_PAGE
DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_SETUP(FileLayoutPage) DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_SETUP(FileLayoutPage)
class SettingsDialog : public Dialogs::SettingsDialog class SettingsDialog : public Dialogs::SettingsDialog

View File

@ -144,7 +144,7 @@ QString TagEdit::generateLabel() const
res.append(tr(" with different targets")); res.append(tr(" with different targets"));
} else { } else {
if(!target.isEmpty()) { if(!target.isEmpty()) {
res.append(tr(" targeting %1").arg(QString::fromLocal8Bit(target.toString().c_str()))); res.append(tr(" targeting %1").arg(QString::fromLocal8Bit(m_tags.front()->targetString().c_str())));
} }
} }
return res; return res;

View File

@ -6,6 +6,7 @@
#include "./fileinfomodel.h" #include "./fileinfomodel.h"
#include "../application/settings.h" #include "../application/settings.h"
#include "../application/targetlevelmodel.h"
#include "../misc/htmlinfo.h" #include "../misc/htmlinfo.h"
#include "../misc/utility.h" #include "../misc/utility.h"
@ -56,6 +57,7 @@ using namespace Dialogs;
using namespace Widgets; using namespace Widgets;
using namespace ThreadingUtils; using namespace ThreadingUtils;
using namespace Media; using namespace Media;
using namespace Models;
namespace QtGui { namespace QtGui {
@ -237,7 +239,7 @@ void TagEditorWidget::updateDocumentTitleEdits()
{ {
// get container, segment count and present titles // get container, segment count and present titles
AbstractContainer *container = m_fileInfo.container(); AbstractContainer *container = m_fileInfo.container();
int segmentCount = container ? static_cast<int>(container->segmentCount()) : 0; const int segmentCount = container ? static_cast<int>(container->segmentCount()) : 0;
const vector<string> &titles = container ? container->titles() : vector<string>(); const vector<string> &titles = container ? container->titles() : vector<string>();
// get layout // get layout
@ -470,7 +472,9 @@ void TagEditorWidget::updateTagManagementMenu()
if(m_fileInfo.areTagsSupported() && m_fileInfo.container()) { if(m_fileInfo.areTagsSupported() && m_fileInfo.container()) {
// there is a container object which is able to create tags // there is a container object which is able to create tags
QString label; QString label;
if(m_fileInfo.containerFormat() == ContainerFormat::Matroska) { switch(m_fileInfo.containerFormat()) {
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
// tag format supports targets (Matroska tags are currently the only tag format supporting targets.) // tag format supports targets (Matroska tags are currently the only tag format supporting targets.)
label = tr("Matroska tag"); label = tr("Matroska tag");
connect(m_addTagMenu->addAction(label), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [this] (MediaFileInfo &file) -> Media::Tag * { connect(m_addTagMenu->addAction(label), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [this] (MediaFileInfo &file) -> Media::Tag * {
@ -483,7 +487,9 @@ void TagEditorWidget::updateTagManagementMenu()
} }
return nullptr; return nullptr;
})); }));
} else { break;
default:
// tag format does not support targets // tag format does not support targets
if(!m_fileInfo.container()->tagCount()) { if(!m_fileInfo.container()->tagCount()) {
switch(m_fileInfo.containerFormat()) { switch(m_fileInfo.containerFormat()) {
@ -491,7 +497,7 @@ void TagEditorWidget::updateTagManagementMenu()
label = tr("MP4/iTunes tag"); label = tr("MP4/iTunes tag");
break; break;
case ContainerFormat::Ogg: case ContainerFormat::Ogg:
label = tr("Vorbis/Opus comment"); label = tr("Vorbis comment");
break; break;
default: default:
label = tr("Tag"); label = tr("Tag");
@ -502,16 +508,28 @@ void TagEditorWidget::updateTagManagementMenu()
} }
} }
} else { } else {
// there is no container object which is able to create tags; creation of ID3 tags is always possible // there is no container object which is able to create tags
if(!m_fileInfo.hasId3v1Tag()) { switch(m_fileInfo.containerFormat()) {
connect(m_addTagMenu->addAction(tr("ID3v1 tag")), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [] (MediaFileInfo &file) { case ContainerFormat::Flac:
return file.createId3v1Tag(); if(!m_fileInfo.vorbisComment()) {
})); connect(m_addTagMenu->addAction(tr("Vorbis comment")), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [] (MediaFileInfo &file) {
} return file.createVorbisComment();
if(!m_fileInfo.hasId3v2Tag()) { }));
connect(m_addTagMenu->addAction(tr("ID3v2 tag")), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [] (MediaFileInfo &file) { }
return file.createId3v2Tag(); break;
}));
default:
// creation of ID3 tags is always possible
if(!m_fileInfo.hasId3v1Tag()) {
connect(m_addTagMenu->addAction(tr("ID3v1 tag")), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [] (MediaFileInfo &file) {
return file.createId3v1Tag();
}));
}
if(!m_fileInfo.hasId3v2Tag()) {
connect(m_addTagMenu->addAction(tr("ID3v2 tag")), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [] (MediaFileInfo &file) {
return file.createId3v2Tag();
}));
}
} }
} }
// add "Remove tag" and "Change target" actions // add "Remove tag" and "Change target" actions
@ -539,7 +557,7 @@ void TagEditorWidget::updateTagManagementMenu()
} }
/*! /*!
* \brief Inserts the title from the filename keeping a possibly available title from the tags. * \brief Inserts the title from the filename if no title is available from the tags.
* \remarks Does nothing if there are no tags assigned and if this feature is not enabled in the settings. * \remarks Does nothing if there are no tags assigned and if this feature is not enabled in the settings.
*/ */
void TagEditorWidget::insertTitleFromFilename() void TagEditorWidget::insertTitleFromFilename()
@ -548,8 +566,13 @@ void TagEditorWidget::insertTitleFromFilename()
QString title; QString title;
int trackNum; int trackNum;
parseFileName(QString::fromLocal8Bit(m_fileInfo.fileName().c_str()), title, trackNum); parseFileName(QString::fromLocal8Bit(m_fileInfo.fileName().c_str()), title, trackNum);
TagValue titleValue = qstringToTagValue(title, TagTextEncoding::Utf16LittleEndian); const TagValue titleValue = qstringToTagValue(title, TagTextEncoding::Utf16LittleEndian);
foreachTagEdit([&titleValue] (TagEdit *edit) { foreachTagEdit([&titleValue] (TagEdit *edit) {
for(const Tag *tag : edit->tags()) {
if(tag->supportsTarget() && tag->isTargetingLevel(TagTargetLevel::Part)) {
return;
}
}
edit->setValue(KnownField::Title, titleValue, PreviousValueHandling::Keep); edit->setValue(KnownField::Title, titleValue, PreviousValueHandling::Keep);
}); });
} }
@ -849,11 +872,19 @@ void TagEditorWidget::showFile(char result)
} }
// create appropriate tags according to file type and user preferences when automatic tag management is enabled // create appropriate tags according to file type and user preferences when automatic tag management is enabled
if(Settings::autoTagManagement()) { if(Settings::autoTagManagement()) {
const QList<ChecklistItem> &targetItems = Settings::defaultTargetsModel().items();
vector<TagTarget> requiredTargets;
requiredTargets.reserve(2);
for(const ChecklistItem &targetItem : targetItems) {
if(targetItem.isChecked()) {
requiredTargets.emplace_back(containerTargetLevelValue(m_fileInfo.containerFormat(), static_cast<TagTargetLevel>(targetItem.id().toInt())));
}
}
if(!m_fileInfo.createAppropriateTags(false, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(), if(!m_fileInfo.createAppropriateTags(false, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(),
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed())) { Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed(), requiredTargets)) {
if(confirmCreationOfId3TagForUnsupportedFile()) { if(confirmCreationOfId3TagForUnsupportedFile()) {
m_fileInfo.createAppropriateTags(true, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(), m_fileInfo.createAppropriateTags(true, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(),
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed()); Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed(), requiredTargets);
} }
} }
// tags might have been adjusted -> reload tags // tags might have been adjusted -> reload tags

View File

@ -50,7 +50,7 @@ class TagEditorWidget : public QWidget
public: public:
explicit TagEditorWidget(QWidget *parent = nullptr); explicit TagEditorWidget(QWidget *parent = nullptr);
virtual ~TagEditorWidget(); ~TagEditorWidget();
public: public:
QMutex &fileOperationMutex(); QMutex &fileOperationMutex();

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>361</width> <width>647</width>
<height>398</height> <height>330</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -106,7 +106,7 @@
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>&lt;span style=&quot;font-weight: bold;&quot;&gt;Automatic tag management&lt;/span&gt; <string>&lt;span style=&quot;font-weight: bold;&quot;&gt;Automatic tag management&lt;/span&gt;
&lt;br&gt;If enabled, appropriate tags will be created and removed according to the settings automatically when opening a file. Otherwise you have to do this manually and settings like ID3 usage have no effect.</string> &lt;br&gt;If enabled, appropriate tags will be created and removed according to the settings automatically when opening a file. Otherwise you have to do this manually (eg. adding an ID3 tag if none is present yet) and settings like ID3 usage have no effect.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@ -138,7 +138,7 @@
<resources/> <resources/>
<connections/> <connections/>
<buttongroups> <buttongroups>
<buttongroup name="preferredTextEncodingButtonGroup"/>
<buttongroup name="unsupportedButtonGroup"/> <buttongroup name="unsupportedButtonGroup"/>
<buttongroup name="preferredTextEncodingButtonGroup"/>
</buttongroups> </buttongroups>
</ui> </ui>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QtGui::TagProcessingTargetsOptionPage</class>
<widget class="QWidget" name="QtGui::TagProcessingTargetsOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>330</height>
</rect>
</property>
<property name="windowTitle">
<string>Targets</string>
</property>
<property name="styleSheet">
<string notr="true">QGroupBox { font-weight: bold };</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="toBeAddedLabel">
<property name="styleSheet">
<string notr="true">font-weight: bold;</string>
</property>
<property name="text">
<string>Target levels to be added when opening a file</string>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="targetsToBeAddedListView">
<property name="lineWidth">
<number>1</number>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="movement">
<enum>QListView::Free</enum>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
<property name="isWrapping" stdset="0">
<bool>true</bool>
</property>
<property name="spacing">
<number>3</number>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoLabel">
<property name="text">
<string>&lt;html&gt;
&lt;head/&gt;
&lt;body&gt;
Remarks
&lt;ul style=&quot;margin-left: -20px; margin-top: -3px; text-indent: 0px &quot;&gt;
&lt;li&gt;It is ensured that at least one tag for each selected target level is present when opening a file.&lt;/li&gt;
&lt;li&gt;Has no effect if automatic tag management is disabled.&lt;/li&gt;
&lt;li&gt;Has no effect when targets are not supported by the file format.&lt;/li&gt;
&lt;ul&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -935,7 +935,7 @@ public:
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Version"), qstr(tag->version())); rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Version"), qstr(tag->version()));
} }
if(tag->supportsTarget() && !tag->target().isEmpty()) { if(tag->supportsTarget() && !tag->target().isEmpty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Target level"), qstr(tag->target().toString())); rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Target level"), qstr(tag->targetString()));
} }
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Size"), qstr(dataSizeToString(tag->size(), true))); rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Size"), qstr(dataSizeToString(tag->size(), true)));
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Field count"), QString::number(tag->fieldCount())); rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Field count"), QString::number(tag->fieldCount()));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff