Added a way to specify multiple delimiters in the UI, fixes #473
This commit is contained in:
@@ -36,35 +36,39 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label" for="${id:extra-tags-to-scan}">
|
<label class="form-label" for="${id:extra-tags-to-scan}">
|
||||||
${tr:Lms.Admin.Database.extra-tags-to-scan}
|
${tr:Lms.Admin.Database.extra-tags-to-scan}
|
||||||
|
${extra-tags-to-scan-add-btn class="ms-1 btn btn-sm btn-secondary"}
|
||||||
</label>
|
</label>
|
||||||
${extra-tags-to-scan class="form-control"}
|
${extra-tags-to-scan-container class="row gy-3"}
|
||||||
<div class="invalid-feedback">
|
|
||||||
${extra-tags-to-scan-info}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label" for="${id:artist-tag-delimiter}">
|
<label class="form-label">
|
||||||
${tr:Lms.Admin.Database.artist-tag-delimiter}
|
${tr:Lms.Admin.Database.artist-tag-delimiters}
|
||||||
|
${artist-tag-delimiter-add-btn class="ms-1 btn btn-sm btn-secondary"}
|
||||||
</label>
|
</label>
|
||||||
${artist-tag-delimiter class="form-control"}
|
${artist-tag-delimiter-container class="row gy-3"}
|
||||||
<div class="invalid-feedback">
|
|
||||||
${artist-tag-delimiter-info}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label" for="${id:default-tag-delimiter}">
|
<label class="form-label">
|
||||||
${tr:Lms.Admin.Database.default-tag-delimiter}
|
${tr:Lms.Admin.Database.default-tag-delimiters}
|
||||||
|
${default-tag-delimiter-add-btn class="ms-1 btn btn-sm btn-secondary"}
|
||||||
</label>
|
</label>
|
||||||
${default-tag-delimiter class="form-control"}
|
${default-tag-delimiter-container class="row gy-3"}
|
||||||
<div class="invalid-feedback">
|
|
||||||
${default-tag-delimiter-info}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
${save-btn class="btn btn-primary me-1"}${discard-btn class="btn btn-secondary"}
|
${save-btn class="btn btn-primary me-1"}${discard-btn class="btn btn-secondary"}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<br/>
|
</message>
|
||||||
|
|
||||||
|
<message id="Lms.Admin.Database.template.line-edit-entry">
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
${value class="form-control"}
|
||||||
|
${del-btn class="btn btn-sm btn-outline-warning"}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
${value-info}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
</messages>
|
</messages>
|
||||||
|
|||||||
@@ -65,10 +65,10 @@
|
|||||||
<message id="Lms.Admin.MediaLibrary.root-path">Root directory</message>
|
<message id="Lms.Admin.MediaLibrary.root-path">Root directory</message>
|
||||||
|
|
||||||
<!--Scan settings-->
|
<!--Scan settings-->
|
||||||
<message id="Lms.Admin.Database.artist-tag-delimiter">Delimiter to be used for splitting artist tags</message>
|
<message id="Lms.Admin.Database.artist-tag-delimiters">Delimiters to be used for splitting artist tags</message>
|
||||||
<message id="Lms.Admin.Database.daily">Daily</message>
|
<message id="Lms.Admin.Database.daily">Daily</message>
|
||||||
<message id="Lms.Admin.Database.default-tag-delimiter">Delimiter to be used for splitting other tags</message>
|
<message id="Lms.Admin.Database.default-tag-delimiters">Delimiters to be used for splitting other tags</message>
|
||||||
<message id="Lms.Admin.Database.extra-tags-to-scan">Extra tags to scan (use <kbd>;</kbd> as separator)</message>
|
<message id="Lms.Admin.Database.extra-tags-to-scan">Extra tags to scan</message>
|
||||||
<message id="Lms.Admin.Database.hourly">Hourly</message>
|
<message id="Lms.Admin.Database.hourly">Hourly</message>
|
||||||
<message id="Lms.Admin.Database.immediate-scan">Scan now!</message>
|
<message id="Lms.Admin.Database.immediate-scan">Scan now!</message>
|
||||||
<message id="Lms.Admin.Database.monthly">Monthly</message>
|
<message id="Lms.Admin.Database.monthly">Monthly</message>
|
||||||
|
|||||||
@@ -65,10 +65,10 @@
|
|||||||
<message id="Lms.Admin.MediaLibrary.root-path">Répertoire racine</message>
|
<message id="Lms.Admin.MediaLibrary.root-path">Répertoire racine</message>
|
||||||
|
|
||||||
<!--Scan settings-->
|
<!--Scan settings-->
|
||||||
<message id="Lms.Admin.Database.artist-tag-delimiter">Délimiteur à utiliser pour séparer les tags d'artistes</message>
|
<message id="Lms.Admin.Database.artist-tag-delimiters">Délimiteurs à utiliser pour séparer les tags d'artistes</message>
|
||||||
<message id="Lms.Admin.Database.daily">Tous les jours</message>
|
<message id="Lms.Admin.Database.daily">Tous les jours</message>
|
||||||
<message id="Lms.Admin.Database.default-tag-delimiter">Délimiteur à utiliser pour séparer les autres tags</message>
|
<message id="Lms.Admin.Database.default-tag-delimiters">Délimiteurs à utiliser pour séparer les autres tags</message>
|
||||||
<message id="Lms.Admin.Database.extra-tags-to-scan">Tags supplémentaires à scanner (utiliser <kbd>;</kbd> comme séparateur)</message>
|
<message id="Lms.Admin.Database.extra-tags-to-scan">Tags supplémentaires à scanner</message>
|
||||||
<message id="Lms.Admin.Database.hourly">Toutes les heures</message>
|
<message id="Lms.Admin.Database.hourly">Toutes les heures</message>
|
||||||
<message id="Lms.Admin.Database.immediate-scan">Scanner maintenant !</message>
|
<message id="Lms.Admin.Database.immediate-scan">Scanner maintenant !</message>
|
||||||
<message id="Lms.Admin.Database.monthly">Tous les mois</message>
|
<message id="Lms.Admin.Database.monthly">Tous les mois</message>
|
||||||
|
|||||||
@@ -65,10 +65,10 @@
|
|||||||
<message id="Lms.Admin.MediaLibrary.root-path">Cartella principale</message>
|
<message id="Lms.Admin.MediaLibrary.root-path">Cartella principale</message>
|
||||||
|
|
||||||
<!--Scan settings-->
|
<!--Scan settings-->
|
||||||
<message id="Lms.Admin.Database.artist-tag-delimiter">Delimitatore da utilizzare per separare i tag degli artisti</message>
|
<message id="Lms.Admin.Database.artist-tag-delimiters">Delimitatori da utilizzare per separare i tag degli artisti</message>
|
||||||
<message id="Lms.Admin.Database.daily">Giornaliera</message>
|
<message id="Lms.Admin.Database.daily">Giornaliera</message>
|
||||||
<message id="Lms.Admin.Database.default-tag-delimiter">Delimitatore da utilizzare per separare gli altri tag</message>
|
<message id="Lms.Admin.Database.default-tag-delimiters">Delimitatori da utilizzare per separare gli altri tag</message>
|
||||||
<message id="Lms.Admin.Database.extra-tags-to-scan">Tag aggiuntivi da scansionare (usa <kbd>;</kbd> come separatore)</message>
|
<message id="Lms.Admin.Database.extra-tags-to-scan">Tag aggiuntivi da scansionare</message>
|
||||||
<message id="Lms.Admin.Database.hourly">Ogni ora</message>
|
<message id="Lms.Admin.Database.hourly">Ogni ora</message>
|
||||||
<message id="Lms.Admin.Database.immediate-scan">Scansiona ora!</message>
|
<message id="Lms.Admin.Database.immediate-scan">Scansiona ora!</message>
|
||||||
<message id="Lms.Admin.Database.monthly">Mensile</message>
|
<message id="Lms.Admin.Database.monthly">Mensile</message>
|
||||||
|
|||||||
@@ -66,10 +66,10 @@
|
|||||||
<message id="Lms.Admin.MediaLibrary.root-path">Katalog główny</message>
|
<message id="Lms.Admin.MediaLibrary.root-path">Katalog główny</message>
|
||||||
|
|
||||||
<!--Scan settings-->
|
<!--Scan settings-->
|
||||||
<message id="Lms.Admin.Database.artist-tag-delimiter">Znak rozdzielający artystów</message>
|
<message id="Lms.Admin.Database.artist-tag-delimiters">Znaki rozdzielające artystów</message>
|
||||||
<message id="Lms.Admin.Database.daily">Codziennie</message>
|
<message id="Lms.Admin.Database.daily">Codziennie</message>
|
||||||
<message id="Lms.Admin.Database.default-tag-delimiter">Znak rozdzielający inne oznaczenia</message>
|
<message id="Lms.Admin.Database.default-tag-delimiters">Znaki rozdzielające inne oznaczenia</message>
|
||||||
<message id="Lms.Admin.Database.extra-tags-to-scan">Szukaj dodatkowych znaczników (użyj <kbd>;</kbd> do rozdzielania)</message>
|
<message id="Lms.Admin.Database.extra-tags-to-scan">Szukaj dodatkowych znaczników</message>
|
||||||
<message id="Lms.Admin.Database.hourly">Co godzinę</message>
|
<message id="Lms.Admin.Database.hourly">Co godzinę</message>
|
||||||
<message id="Lms.Admin.Database.immediate-scan">Skanuj teraz!</message>
|
<message id="Lms.Admin.Database.immediate-scan">Skanuj teraz!</message>
|
||||||
<message id="Lms.Admin.Database.monthly">Co miesiąc</message>
|
<message id="Lms.Admin.Database.monthly">Co miesiąc</message>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ namespace lms::db
|
|||||||
return core::stringUtils::splitEscapedStrings(_defaultTagDelimiters, ';', '\\');
|
return core::stringUtils::splitEscapedStrings(_defaultTagDelimiters, ';', '\\');
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScanSettings::setExtraTagsToScan(const std::vector<std::string_view>& extraTags)
|
void ScanSettings::setExtraTagsToScan(std::span<const std::string_view> extraTags)
|
||||||
{
|
{
|
||||||
std::string newTagsToScan{ core::stringUtils::joinStrings(extraTags, ";") };
|
std::string newTagsToScan{ core::stringUtils::joinStrings(extraTags, ";") };
|
||||||
if (newTagsToScan != _extraTagsToScan)
|
if (newTagsToScan != _extraTagsToScan)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace lms::db
|
|||||||
// Setters
|
// Setters
|
||||||
void setUpdateStartTime(Wt::WTime t) { _startTime = t; }
|
void setUpdateStartTime(Wt::WTime t) { _startTime = t; }
|
||||||
void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; }
|
void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; }
|
||||||
void setExtraTagsToScan(const std::vector<std::string_view>& extraTags);
|
void setExtraTagsToScan(std::span<const std::string_view> extraTags);
|
||||||
void setSimilarityEngineType(SimilarityEngineType type) { _similarityEngineType = type; }
|
void setSimilarityEngineType(SimilarityEngineType type) { _similarityEngineType = type; }
|
||||||
void setArtistTagDelimiters(std::span<const std::string_view> delimiters);
|
void setArtistTagDelimiters(std::span<const std::string_view> delimiters);
|
||||||
void setDefaultTagDelimiters(std::span<const std::string_view> delimiters);
|
void setDefaultTagDelimiters(std::span<const std::string_view> delimiters);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace lms::ui
|
|||||||
Wt::WValidator::Result validate(const Wt::WString& input) const override
|
Wt::WValidator::Result validate(const Wt::WString& input) const override
|
||||||
{
|
{
|
||||||
if (input.empty())
|
if (input.empty())
|
||||||
return Wt::WValidator::Result{ Wt::ValidationState::Valid };
|
return Wt::WValidator::validate(input);
|
||||||
|
|
||||||
std::string inputStr{ input.toUTF8() };
|
std::string inputStr{ input.toUTF8() };
|
||||||
if (std::all_of(std::cbegin(inputStr), std::cend(inputStr), [](char c) { return std::isspace(c); }))
|
if (std::all_of(std::cbegin(inputStr), std::cend(inputStr), [](char c) { return std::isspace(c); }))
|
||||||
@@ -69,14 +69,9 @@ namespace lms::ui
|
|||||||
static inline constexpr Field UpdatePeriodField{ "update-period" };
|
static inline constexpr Field UpdatePeriodField{ "update-period" };
|
||||||
static inline constexpr Field UpdateStartTimeField{ "update-start-time" };
|
static inline constexpr Field UpdateStartTimeField{ "update-start-time" };
|
||||||
static inline constexpr Field SimilarityEngineTypeField{ "similarity-engine-type" };
|
static inline constexpr Field SimilarityEngineTypeField{ "similarity-engine-type" };
|
||||||
static inline constexpr Field ExtraTagsField{ "extra-tags-to-scan" };
|
|
||||||
static inline constexpr Field ArtistTagDelimiterField{ "artist-tag-delimiter" };
|
|
||||||
static inline constexpr Field DefaultTagDelimiterField{ "default-tag-delimiter" };
|
|
||||||
|
|
||||||
using UpdatePeriodModel = ValueStringModel<ScanSettings::UpdatePeriod>;
|
using UpdatePeriodModel = ValueStringModel<ScanSettings::UpdatePeriod>;
|
||||||
|
|
||||||
static inline constexpr char extraTagsDelimiter{ ';' };
|
|
||||||
|
|
||||||
DatabaseSettingsModel()
|
DatabaseSettingsModel()
|
||||||
{
|
{
|
||||||
initializeModels();
|
initializeModels();
|
||||||
@@ -84,26 +79,17 @@ namespace lms::ui
|
|||||||
addField(UpdatePeriodField);
|
addField(UpdatePeriodField);
|
||||||
addField(UpdateStartTimeField);
|
addField(UpdateStartTimeField);
|
||||||
addField(SimilarityEngineTypeField);
|
addField(SimilarityEngineTypeField);
|
||||||
addField(ExtraTagsField);
|
|
||||||
addField(ArtistTagDelimiterField);
|
|
||||||
addField(DefaultTagDelimiterField);
|
|
||||||
|
|
||||||
setValidator(UpdatePeriodField, createMandatoryValidator());
|
setValidator(UpdatePeriodField, createMandatoryValidator());
|
||||||
setValidator(UpdateStartTimeField, createMandatoryValidator());
|
setValidator(UpdateStartTimeField, createMandatoryValidator());
|
||||||
setValidator(SimilarityEngineTypeField, createMandatoryValidator());
|
setValidator(SimilarityEngineTypeField, createMandatoryValidator());
|
||||||
setValidator(ExtraTagsField, createUppercaseValidator());
|
|
||||||
setValidator(ArtistTagDelimiterField, std::make_unique<TagDelimitersValidator>());
|
|
||||||
setValidator(DefaultTagDelimiterField, std::make_unique<TagDelimitersValidator>());
|
|
||||||
|
|
||||||
// populate the model with initial data
|
|
||||||
loadData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<UpdatePeriodModel> updatePeriodModel() { return _updatePeriodModel; }
|
std::shared_ptr<UpdatePeriodModel> updatePeriodModel() { return _updatePeriodModel; }
|
||||||
std::shared_ptr<Wt::WAbstractItemModel> updateStartTimeModel() { return _updateStartTimeModel; }
|
std::shared_ptr<Wt::WAbstractItemModel> updateStartTimeModel() { return _updateStartTimeModel; }
|
||||||
std::shared_ptr<Wt::WAbstractItemModel> similarityEngineTypeModel() { return _similarityEngineTypeModel; }
|
std::shared_ptr<Wt::WAbstractItemModel> similarityEngineTypeModel() { return _similarityEngineTypeModel; }
|
||||||
|
|
||||||
void loadData()
|
void loadData(std::vector<std::string>& extraTagsToScan, std::vector<std::string>& artistDelimiters, std::vector<std::string>& defaultDelimiters)
|
||||||
{
|
{
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
||||||
|
|
||||||
@@ -128,20 +114,13 @@ namespace lms::ui
|
|||||||
setValue(SimilarityEngineTypeField, _similarityEngineTypeModel->getString(*similarityEngineTypeRow));
|
setValue(SimilarityEngineTypeField, _similarityEngineTypeModel->getString(*similarityEngineTypeRow));
|
||||||
|
|
||||||
const auto extraTags{ scanSettings->getExtraTagsToScan() };
|
const auto extraTags{ scanSettings->getExtraTagsToScan() };
|
||||||
setValue(ExtraTagsField, core::stringUtils::joinStrings(extraTags, extraTagsDelimiter));
|
extraTagsToScan.clear();
|
||||||
|
std::transform(std::cbegin(extraTags), std::cend(extraTags), std::back_inserter(extraTagsToScan), [](std::string_view extraTag) { return std::string{ extraTag }; });
|
||||||
{
|
artistDelimiters = scanSettings->getArtistTagDelimiters();
|
||||||
std::vector<std::string> delimiters{ scanSettings->getArtistTagDelimiters() };
|
defaultDelimiters = scanSettings->getDefaultTagDelimiters();
|
||||||
setValue(ArtistTagDelimiterField, delimiters.empty() ? "" : delimiters.front());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::vector<std::string> delimiters{ scanSettings->getDefaultTagDelimiters() };
|
|
||||||
setValue(DefaultTagDelimiterField, delimiters.empty() ? "" : delimiters.front());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveData()
|
void saveData(std::span<const std::string_view> extraTagsToScan, std::span<const std::string_view> artistDelimiters, std::span<const std::string_view> defaultDelimiters)
|
||||||
{
|
{
|
||||||
auto transaction{ LmsApp->getDbSession().createWriteTransaction() };
|
auto transaction{ LmsApp->getDbSession().createWriteTransaction() };
|
||||||
|
|
||||||
@@ -159,21 +138,9 @@ namespace lms::ui
|
|||||||
if (similarityEngineTypeRow)
|
if (similarityEngineTypeRow)
|
||||||
scanSettings.modify()->setSimilarityEngineType(_similarityEngineTypeModel->getValue(*similarityEngineTypeRow));
|
scanSettings.modify()->setSimilarityEngineType(_similarityEngineTypeModel->getValue(*similarityEngineTypeRow));
|
||||||
|
|
||||||
scanSettings.modify()->setExtraTagsToScan(core::stringUtils::splitString(valueText(ExtraTagsField).toUTF8(), extraTagsDelimiter));
|
scanSettings.modify()->setExtraTagsToScan(extraTagsToScan);
|
||||||
|
scanSettings.modify()->setArtistTagDelimiters(artistDelimiters);
|
||||||
{
|
scanSettings.modify()->setDefaultTagDelimiters(defaultDelimiters);
|
||||||
std::vector<std::string_view> artistDelimiters;
|
|
||||||
if (std::string artistDelimiter{ valueText(ArtistTagDelimiterField).toUTF8() }; !artistDelimiter.empty())
|
|
||||||
artistDelimiters.push_back(std::move(artistDelimiter));
|
|
||||||
scanSettings.modify()->setArtistTagDelimiters(artistDelimiters);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::vector<std::string_view> defaultDelimiters;
|
|
||||||
if (std::string defaultDelimiter{ valueText(DefaultTagDelimiterField).toUTF8() }; !defaultDelimiter.empty())
|
|
||||||
defaultDelimiters.push_back(std::move(defaultDelimiter));
|
|
||||||
scanSettings.modify()->setDefaultTagDelimiters(defaultDelimiters);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -202,6 +169,118 @@ namespace lms::ui
|
|||||||
std::shared_ptr<ValueStringModel<Wt::WTime>> _updateStartTimeModel;
|
std::shared_ptr<ValueStringModel<Wt::WTime>> _updateStartTimeModel;
|
||||||
std::shared_ptr<ValueStringModel<ScanSettings::SimilarityEngineType>> _similarityEngineTypeModel;
|
std::shared_ptr<ValueStringModel<ScanSettings::SimilarityEngineType>> _similarityEngineTypeModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LineEditEntryModel : public Wt::WFormModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline constexpr Field ValueField{ "value" };
|
||||||
|
|
||||||
|
LineEditEntryModel(const Wt::WString& initialValue, std::shared_ptr<Wt::WValidator> validator)
|
||||||
|
: Wt::WFormModel()
|
||||||
|
{
|
||||||
|
addField(ValueField);
|
||||||
|
|
||||||
|
setValidator(ValueField, validator);
|
||||||
|
setValue(ValueField, initialValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LineEditEntryWidget : public Wt::WTemplateFormView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LineEditEntryWidget(const Wt::WString& initialValue, std::shared_ptr<Wt::WValidator> validator)
|
||||||
|
: Wt::WTemplateFormView{ Wt::WString::tr("Lms.Admin.Database.template.line-edit-entry") }
|
||||||
|
, _model{ std::make_shared<LineEditEntryModel>(initialValue, validator) }
|
||||||
|
{
|
||||||
|
setStyleClass("col-sm-4 col-md-3"); // hack
|
||||||
|
|
||||||
|
setFormWidget(LineEditEntryModel::ValueField, std::make_unique<Wt::WLineEdit>());
|
||||||
|
|
||||||
|
auto* delBtn{ bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.template.trash-btn"), Wt::TextFormat::XHTML) };
|
||||||
|
delBtn->clicked().connect(this, [this] { deleted.emit(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validate() { return _model->validate(); }
|
||||||
|
void updateModel() { Wt::WTemplateFormView::updateModel(_model.get()); }
|
||||||
|
void updateView() { Wt::WTemplateFormView::updateView(_model.get()); }
|
||||||
|
|
||||||
|
Wt::WString getValue() const
|
||||||
|
{
|
||||||
|
return _model->valueText(LineEditEntryModel::ValueField);
|
||||||
|
}
|
||||||
|
|
||||||
|
Wt::Signal<> deleted;
|
||||||
|
std::shared_ptr<LineEditEntryModel> _model;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Terrible hack to make use of the validation system for each added element
|
||||||
|
class LineEditContainerWidget : public Wt::WContainerWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LineEditContainerWidget(std::shared_ptr<Wt::WValidator> validator)
|
||||||
|
: _validator{ validator } {}
|
||||||
|
|
||||||
|
void add(const Wt::WString& value = "")
|
||||||
|
{
|
||||||
|
auto* entry{ addNew<LineEditEntryWidget>(value, _validator) };
|
||||||
|
|
||||||
|
entry->deleted.connect(this, [=, this] {
|
||||||
|
removeWidget(entry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validate()
|
||||||
|
{
|
||||||
|
bool res{ true };
|
||||||
|
|
||||||
|
for (int i{}; i < count(); ++i)
|
||||||
|
{
|
||||||
|
LineEditEntryWidget* entry{ static_cast<LineEditEntryWidget*>(widget(i)) };
|
||||||
|
res &= entry->validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateModels()
|
||||||
|
{
|
||||||
|
for (int i{}; i < count(); ++i)
|
||||||
|
{
|
||||||
|
LineEditEntryWidget* entry{ static_cast<LineEditEntryWidget*>(widget(i)) };
|
||||||
|
entry->updateModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateViews()
|
||||||
|
{
|
||||||
|
for (int i{}; i < count(); ++i)
|
||||||
|
{
|
||||||
|
LineEditEntryWidget* entry{ static_cast<LineEditEntryWidget*>(widget(i)) };
|
||||||
|
entry->updateView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitValues(std::function<void(Wt::WString)> visitor) const
|
||||||
|
{
|
||||||
|
for (int i{}; i < count(); ++i)
|
||||||
|
{
|
||||||
|
LineEditEntryWidget* entry{ static_cast<LineEditEntryWidget*>(widget(i)) };
|
||||||
|
visitor(entry->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> getValues() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> values;
|
||||||
|
visitValues([&](const Wt::WString& value) {
|
||||||
|
values.push_back(value.toUTF8());
|
||||||
|
});
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Wt::WValidator> _validator;
|
||||||
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ScanSettingsView::ScanSettingsView()
|
ScanSettingsView::ScanSettingsView()
|
||||||
@@ -245,24 +324,102 @@ namespace lms::ui
|
|||||||
t->setFormWidget(DatabaseSettingsModel::SimilarityEngineTypeField, std::move(similarityEngineType));
|
t->setFormWidget(DatabaseSettingsModel::SimilarityEngineTypeField, std::move(similarityEngineType));
|
||||||
|
|
||||||
// Extra tags
|
// Extra tags
|
||||||
t->setFormWidget(DatabaseSettingsModel::ExtraTagsField, std::make_unique<Wt::WLineEdit>());
|
std::shared_ptr<Wt::WValidator> extraTagValidator{ createUppercaseValidator() };
|
||||||
|
extraTagValidator->setMandatory(true);
|
||||||
|
auto* extraTagsToScan{ t->bindNew<LineEditContainerWidget>("extra-tags-to-scan-container", extraTagValidator) };
|
||||||
|
{
|
||||||
|
auto* addExtraScanToScanBtn{ t->bindNew<Wt::WPushButton>("extra-tags-to-scan-add-btn", Wt::WString::tr("Lms.add")) };
|
||||||
|
addExtraScanToScanBtn->clicked().connect(this, [=] {
|
||||||
|
extraTagsToScan->add();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Artist tag delimiter
|
// Artist tag delimiter
|
||||||
t->setFormWidget(DatabaseSettingsModel::ArtistTagDelimiterField, std::make_unique<Wt::WLineEdit>());
|
std::shared_ptr<Wt::WValidator> tagDelimiterValidator{ std::make_shared<TagDelimitersValidator>() };
|
||||||
|
tagDelimiterValidator->setMandatory(true);
|
||||||
|
|
||||||
|
auto* artistTagDelimiters{ t->bindNew<LineEditContainerWidget>("artist-tag-delimiter-container", tagDelimiterValidator) };
|
||||||
|
{
|
||||||
|
auto* addArtistDelimiterBtn{ t->bindNew<Wt::WPushButton>("artist-tag-delimiter-add-btn", Wt::WString::tr("Lms.add")) };
|
||||||
|
addArtistDelimiterBtn->clicked().connect(this, [=] {
|
||||||
|
artistTagDelimiters->add();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Default tag delimiter
|
// Default tag delimiter
|
||||||
t->setFormWidget(DatabaseSettingsModel::DefaultTagDelimiterField, std::make_unique<Wt::WLineEdit>());
|
auto* defaultTagDelimiters{ t->bindNew<LineEditContainerWidget>("default-tag-delimiter-container", tagDelimiterValidator) };
|
||||||
|
{
|
||||||
|
auto* addDefaultDelimiterBtn{ t->bindNew<Wt::WPushButton>("default-tag-delimiter-add-btn", Wt::WString::tr("Lms.add")) };
|
||||||
|
addDefaultDelimiterBtn->clicked().connect(this, [=] {
|
||||||
|
defaultTagDelimiters->add();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
Wt::WPushButton* saveBtn = t->bindWidget("save-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.save")));
|
Wt::WPushButton* saveBtn{ t->bindWidget("save-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.save"))) };
|
||||||
Wt::WPushButton* discardBtn = t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
|
Wt::WPushButton* discardBtn{ t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard"))) };
|
||||||
|
|
||||||
|
auto validate{ [=] {
|
||||||
|
bool res{ true };
|
||||||
|
|
||||||
|
res &= model->validate();
|
||||||
|
res &= extraTagsToScan->validate();
|
||||||
|
res &= artistTagDelimiters->validate();
|
||||||
|
res &= defaultTagDelimiters->validate();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} };
|
||||||
|
|
||||||
|
auto updateModels{ [=] {
|
||||||
|
t->updateModel(model.get());
|
||||||
|
extraTagsToScan->updateModels();
|
||||||
|
artistTagDelimiters->updateModels();
|
||||||
|
defaultTagDelimiters->updateModels();
|
||||||
|
} };
|
||||||
|
|
||||||
|
auto updateViews{ [=] {
|
||||||
|
t->updateView(model.get());
|
||||||
|
extraTagsToScan->updateViews();
|
||||||
|
artistTagDelimiters->updateViews();
|
||||||
|
defaultTagDelimiters->updateViews();
|
||||||
|
} };
|
||||||
|
|
||||||
|
auto loadInitialData{ [=] {
|
||||||
|
std::vector<std::string> extraTags;
|
||||||
|
std::vector<std::string> artistDelimiters;
|
||||||
|
std::vector<std::string> defaultDelimiters;
|
||||||
|
model->loadData(extraTags, artistDelimiters, defaultDelimiters);
|
||||||
|
|
||||||
|
extraTagsToScan->clear();
|
||||||
|
for (const std::string& extraTag : extraTags)
|
||||||
|
extraTagsToScan->add(Wt::WString::fromUTF8(std::string{ extraTag }));
|
||||||
|
|
||||||
|
artistTagDelimiters->clear();
|
||||||
|
for (const std::string& artistDelimiter : artistDelimiters)
|
||||||
|
artistTagDelimiters->add(Wt::WString::fromUTF8(artistDelimiter));
|
||||||
|
|
||||||
|
defaultTagDelimiters->clear();
|
||||||
|
for (const std::string& defaultDelimiter : defaultDelimiters)
|
||||||
|
defaultTagDelimiters->add(Wt::WString::fromUTF8(defaultDelimiter));
|
||||||
|
} };
|
||||||
|
|
||||||
saveBtn->clicked().connect([=] {
|
saveBtn->clicked().connect([=] {
|
||||||
t->updateModel(model.get());
|
updateModels();
|
||||||
|
if (validate())
|
||||||
if (model->validate())
|
|
||||||
{
|
{
|
||||||
model->saveData();
|
const std::vector<std::string> extraTags{ extraTagsToScan->getValues() };
|
||||||
|
std::vector<std::string_view> extraTagViews;
|
||||||
|
std::transform(std::cbegin(extraTags), std::cend(extraTags), std::back_inserter(extraTagViews), [](const std::string& tag) -> std::string_view { return tag; });
|
||||||
|
|
||||||
|
const std::vector<std::string> artistDelimiters{ artistTagDelimiters->getValues() };
|
||||||
|
std::vector<std::string_view> artistDelimiterViews;
|
||||||
|
std::transform(std::cbegin(artistDelimiters), std::cend(artistDelimiters), std::back_inserter(artistDelimiterViews), [](const std::string& delimiter) -> std::string_view { return delimiter; });
|
||||||
|
|
||||||
|
const std::vector<std::string> defaultDelimiters{ defaultTagDelimiters->getValues() };
|
||||||
|
std::vector<std::string_view> defaultDelimiterViews;
|
||||||
|
std::transform(std::cbegin(defaultDelimiters), std::cend(defaultDelimiters), std::back_inserter(defaultDelimiterViews), [](const std::string& delimiter) -> std::string_view { return delimiter; });
|
||||||
|
|
||||||
|
model->saveData(extraTagViews, artistDelimiterViews, defaultDelimiterViews);
|
||||||
|
|
||||||
core::Service<recommendation::IRecommendationService>::get()->load();
|
core::Service<recommendation::IRecommendationService>::get()->load();
|
||||||
// Don't want the scanner to go on with wrong settings
|
// Don't want the scanner to go on with wrong settings
|
||||||
@@ -271,16 +428,16 @@ namespace lms::ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Udate the view: Delete any validation message in the view, etc.
|
// Udate the view: Delete any validation message in the view, etc.
|
||||||
t->updateView(model.get());
|
updateViews();
|
||||||
});
|
});
|
||||||
|
|
||||||
discardBtn->clicked().connect([=] {
|
discardBtn->clicked().connect([=] {
|
||||||
model->loadData();
|
loadInitialData();
|
||||||
model->validate();
|
validate();
|
||||||
t->updateView(model.get());
|
updateViews();
|
||||||
});
|
});
|
||||||
|
|
||||||
t->updateView(model.get());
|
loadInitialData();
|
||||||
|
updateViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace lms::ui
|
} // namespace lms::ui
|
||||||
|
|||||||
Reference in New Issue
Block a user