Improve handling of targets
This commit is contained in:
parent
ae08e43e6a
commit
c1269c7945
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -79,6 +79,10 @@ size_t &minPadding();
|
|||
size_t &maxPadding();
|
||||
size_t &preferredPadding();
|
||||
|
||||
// targets
|
||||
class TargetLevelModel;
|
||||
TargetLevelModel &defaultTargetsModel();
|
||||
|
||||
// fields
|
||||
class KnownFieldModel;
|
||||
KnownFieldModel &selectedFieldsModel();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -791,7 +791,7 @@ void displayTagInfo(const StringVector ¶meterValues, 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
|
||||
|
|
|
@ -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);
|
||||
|
@ -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()))) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,7 +508,18 @@ void TagEditorWidget::updateTagManagementMenu()
|
|||
}
|
||||
}
|
||||
} 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
|
||||
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();
|
||||
|
@ -514,6 +531,7 @@ void TagEditorWidget::updateTagManagementMenu()
|
|||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
// add "Remove tag" and "Change target" actions
|
||||
for(Tag *tag : m_tags) {
|
||||
// don't propose removal for Vorbis comments from Voribs or FLAC streams (removing from Opus streams should be ok)
|
||||
|
@ -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
|
||||
|
|
|
@ -50,7 +50,7 @@ class TagEditorWidget : public QWidget
|
|||
|
||||
public:
|
||||
explicit TagEditorWidget(QWidget *parent = nullptr);
|
||||
virtual ~TagEditorWidget();
|
||||
~TagEditorWidget();
|
||||
|
||||
public:
|
||||
QMutex &fileOperationMutex();
|
||||
|
|
|
@ -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><span style="font-weight: bold;">Automatic tag management</span>
|
||||
<br>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>
|
||||
<br>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>
|
||||
|
|
|
@ -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><html>
|
||||
<head/>
|
||||
<body>
|
||||
Remarks
|
||||
<ul style="margin-left: -20px; margin-top: -3px; text-indent: 0px ">
|
||||
<li>It is ensured that at least one tag for each selected target level is present when opening a file.</li>
|
||||
<li>Has no effect if automatic tag management is disabled.</li>
|
||||
<li>Has no effect when targets are not supported by the file format.</li>
|
||||
<ul></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -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
Loading…
Reference in New Issue