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
set(HEADER_FILES
application/knownfieldmodel.h
application/targetlevelmodel.h
application/main.h
application/settings.h
cli/mainfeatures.h
@ -22,6 +23,7 @@ set(HEADER_FILES
)
set(SRC_FILES
application/knownfieldmodel.cpp
application/targetlevelmodel.cpp
application/main.cpp
application/settings.cpp
cli/mainfeatures.cpp
@ -95,6 +97,7 @@ set(WIDGETS_UI_FILES
gui/id3v2optionpage.ui
gui/id3v1optionpage.ui
gui/tagprocessinggeneraloptionpage.ui
gui/tagprocessingtargetsoptionpage.ui
gui/editorgeneraloptionpage.ui
gui/filebrowsergeneraloptionpage.ui
gui/mainwindow.ui

View File

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

View File

@ -1,5 +1,5 @@
#ifndef KNOWNFIELDSELECTION_H
#define KNOWNFIELDSELECTION_H
#ifndef KNOWNFIELDMODEL_H
#define KNOWNFIELDMODEL_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),
verboseArg(verboseArg),
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=\")"),
treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", string(), "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)"),
id3v2UsageArg("id3v2-usage", string(), "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"),
id3v2VersionArg("id3v2-version", string(), "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used"),
encodingArg("encoding", string(), "specifies the preferred encoding"),
removeTargetsArg("remove-targets", string(), "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 \",\""),
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", nullptr, "if present unknown files will be treatet as MP3 files"),
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", 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", nullptr, "if present multiple successive ID3v2 tags will be merged"),
id3v2VersionArg("id3v2-version", nullptr, "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used"),
encodingArg("encoding", nullptr, "specifies the preferred encoding"),
removeTargetsArg("remove-targets", nullptr, "removes all tags with the specified targets (which 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"),
minPaddingArg("min-padding", string(), "specifies the minimum padding before the media data"),
maxPaddingArg("max-padding", string(), "specifies the maximum padding before the media data"),
prefPaddingArg("preferred-padding", string(), "specifies the preferred padding before the media data"),
tagPosArg("tag-pos", string(), "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"),
indexPosArg("index-pos", string(), "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"),
forceRewriteArg("force-rewrite", string(), "forces the file to rewritten from the scratch"),
minPaddingArg("min-padding", nullptr, "specifies the minimum padding before the media data"),
maxPaddingArg("max-padding", nullptr, "specifies the maximum padding before the media data"),
prefPaddingArg("preferred-padding", nullptr, "specifies the preferred padding before the media data"),
tagPosArg("tag-pos", nullptr, "specifies the preferred tag position"),
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", nullptr, "specifies the preferred index position"),
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", nullptr, "forces the file to rewritten from the scratch"),
setTagInfoArg("set-tag-info", "set", "sets the values of all specified tag fields")
{
docTitleArg.setCombinable(true);
@ -135,7 +135,7 @@ int main(int argc, char *argv[])
outputFileArg.setRequired(true);
outputFileArg.setCombinable(true);
// 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);
// display general file info
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");
validateArg.setDenotesOperation(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.setSecondaryArguments({&fileArg, &validateArg, &outputFileArg});
genInfoArg.setCallback(std::bind(Cli::generateFileInfo, _1, std::cref(fileArg), std::cref(outputFileArg), std::cref(validateArg)));
// 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.setCallback(std::bind(Cli::removeBackupFiles, _1, std::cref(recursiveArg)));
remBackupFilesArg.setValueNames({"directory"});
remBackupFilesArg.setRequiredValueCount(1);
remBackupFilesArg.setSecondaryArguments({&recursiveArg});
// 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);
// set arguments to parser
qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(&filesArg);

View File

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

View File

@ -79,6 +79,10 @@ size_t &minPadding();
size_t &maxPadding();
size_t &preferredPadding();
// targets
class TargetLevelModel;
TargetLevelModel &defaultTargetsModel();
// fields
class KnownFieldModel;
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
cout << tag->typeName();
if(!tag->target().isEmpty()) {
cout << " targeting \"" << tag->target().toString() << "\"";
cout << " targeting \"" << tag->targetString() << "\"";
}
cout << endl;
// iterate through fields specified by the user

View File

@ -64,7 +64,8 @@ EnterTargetDialog::EnterTargetDialog(QWidget *parent) :
m_tracksModel(new ChecklistModel(this)),
m_chaptersModel(new ChecklistModel(this)),
m_editionsModel(new ChecklistModel(this)),
m_attachmentsModel(new ChecklistModel(this))
m_attachmentsModel(new ChecklistModel(this)),
m_currentContainerFormat(ContainerFormat::Unknown)
{
// setup UI
m_ui->setupUi(this);
@ -79,7 +80,7 @@ EnterTargetDialog::EnterTargetDialog(QWidget *parent) :
m_ui->attachmentsListView->setModel(m_attachmentsModel);
// connect signals and slots
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);
}
@ -88,7 +89,8 @@ EnterTargetDialog::~EnterTargetDialog()
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
@ -105,6 +107,7 @@ Media::TagTarget EnterTargetDialog::target() const
void EnterTargetDialog::setTarget(const TagTarget &target, const MediaFileInfo *file)
{
m_currentContainerFormat = file ? file->containerFormat() : ContainerFormat::Unknown;
if(m_ui->levelSpinBox->maximum() >= 0
&& 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()))) {

View File

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

View File

@ -339,7 +339,7 @@ void FileInfoModel::updateCache()
auto *tagItem = defaultItem(tag->typeName());
ItemHelper tagHelper(tagItem);
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("Field count"), tag->fieldCount());
tagsItem->appendRow(tagItem);

View File

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

View File

@ -3,6 +3,7 @@
#include "../application/settings.h"
#include "../application/knownfieldmodel.h"
#include "../application/targetlevelmodel.h"
#include <tagparser/mediafileinfo.h>
#include <tagparser/backuphelper.h>
@ -140,6 +141,7 @@ QWidget *EditorTempOptionPage::setupWidget()
// EditorFieldsOptionPage
EditorFieldsOptionPage::EditorFieldsOptionPage(QWidget *parentWidget) :
EditorFieldsOptionPageBase(parentWidget),
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(QWidget *parentWidget) :
FileLayoutPageBase(parentWidget)
@ -548,8 +584,8 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
category = new Dialogs::OptionCategory(this);
category->setDisplayName(tr("Tag processing"));
category->assignPages(QList<Dialogs::OptionPage *>()
<< new TagProcessingGeneralOptionPage
<< new Id3v1OptionPage << new Id3v2OptionPage << new FileLayoutPage);
<< new TagProcessingGeneralOptionPage << new Id3v1OptionPage
<< new Id3v2OptionPage << new TagProcessingTargetsOptionPage << new FileLayoutPage);
category->setIcon(QIcon::fromTheme(QStringLiteral("tag"), QIcon(QStringLiteral(":/tageditor/icons/hicolor/32x32/settingscategories/tag.png"))));
categories << category;

View File

@ -11,6 +11,7 @@
#include "ui_tagprocessinggeneraloptionpage.h"
#include "ui_id3v1optionpage.h"
#include "ui_id3v2optionpage.h"
#include "ui_tagprocessingtargetsoptionpage.h"
#include "ui_filelayout.h"
#include <qtutilities/settingsdialog/settingsdialog.h>
@ -20,6 +21,7 @@
namespace Settings {
class KnownFieldModel;
class TargetLevelModel;
}
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)
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)
class SettingsDialog : public Dialogs::SettingsDialog

View File

@ -144,7 +144,7 @@ QString TagEdit::generateLabel() const
res.append(tr(" with different targets"));
} else {
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;

View File

@ -6,6 +6,7 @@
#include "./fileinfomodel.h"
#include "../application/settings.h"
#include "../application/targetlevelmodel.h"
#include "../misc/htmlinfo.h"
#include "../misc/utility.h"
@ -56,6 +57,7 @@ using namespace Dialogs;
using namespace Widgets;
using namespace ThreadingUtils;
using namespace Media;
using namespace Models;
namespace QtGui {
@ -237,7 +239,7 @@ void TagEditorWidget::updateDocumentTitleEdits()
{
// get container, segment count and present titles
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>();
// get layout
@ -470,7 +472,9 @@ void TagEditorWidget::updateTagManagementMenu()
if(m_fileInfo.areTagsSupported() && m_fileInfo.container()) {
// there is a container object which is able to create tags
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.)
label = tr("Matroska 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;
}));
} else {
break;
default:
// tag format does not support targets
if(!m_fileInfo.container()->tagCount()) {
switch(m_fileInfo.containerFormat()) {
@ -491,7 +497,7 @@ void TagEditorWidget::updateTagManagementMenu()
label = tr("MP4/iTunes tag");
break;
case ContainerFormat::Ogg:
label = tr("Vorbis/Opus comment");
label = tr("Vorbis comment");
break;
default:
label = tr("Tag");
@ -502,16 +508,28 @@ void TagEditorWidget::updateTagManagementMenu()
}
}
} else {
// there is no container object which is able to create tags; 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();
}));
// there is no container object which is able to create tags
switch(m_fileInfo.containerFormat()) {
case ContainerFormat::Flac:
if(!m_fileInfo.vorbisComment()) {
connect(m_addTagMenu->addAction(tr("Vorbis comment")), &QAction::triggered, std::bind(&TagEditorWidget::addTag, this, [] (MediaFileInfo &file) {
return file.createVorbisComment();
}));
}
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
@ -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.
*/
void TagEditorWidget::insertTitleFromFilename()
@ -548,8 +566,13 @@ void TagEditorWidget::insertTitleFromFilename()
QString title;
int 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) {
for(const Tag *tag : edit->tags()) {
if(tag->supportsTarget() && tag->isTargetingLevel(TagTargetLevel::Part)) {
return;
}
}
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
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(),
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed())) {
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed(), requiredTargets)) {
if(confirmCreationOfId3TagForUnsupportedFile()) {
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

View File

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

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>361</width>
<height>398</height>
<width>647</width>
<height>330</height>
</rect>
</property>
<property name="windowTitle">
@ -106,7 +106,7 @@
<widget class="QLabel" name="label">
<property name="text">
<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 name="wordWrap">
<bool>true</bool>
@ -138,7 +138,7 @@
<resources/>
<connections/>
<buttongroups>
<buttongroup name="preferredTextEncodingButtonGroup"/>
<buttongroup name="unsupportedButtonGroup"/>
<buttongroup name="preferredTextEncodingButtonGroup"/>
</buttongroups>
</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()));
}
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", "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