Introduce a config resolver (#2244)
* Add config resolver Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Enable resolver even when using systray Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Fix bugs Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Do not show resolver for shortcut conflicts Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Fix build error on MacOS and Windows Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Add missing translations Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Replace variable i with row Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Improve presentation Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Disambiguate shortcuts and general settings Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Wrap some strings in tr Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com> * Update tooltips in ConfigResolver Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>
This commit is contained in:
@@ -173,19 +173,13 @@ static QMap<QString, QSharedPointer<KeySequence>> recognizedShortcuts = {
|
||||
|
||||
// CLASS CONFIGHANDLER
|
||||
|
||||
ConfigHandler::ConfigHandler(bool skipInitialErrorCheck)
|
||||
ConfigHandler::ConfigHandler()
|
||||
: m_settings(QSettings::IniFormat,
|
||||
QSettings::UserScope,
|
||||
qApp->organizationName(),
|
||||
qApp->applicationName())
|
||||
{
|
||||
static bool wasEverChecked = false;
|
||||
static bool firstInitialization = true;
|
||||
if (!skipInitialErrorCheck && !wasEverChecked) {
|
||||
// check for error on initial call
|
||||
checkAndHandleError();
|
||||
wasEverChecked = true;
|
||||
}
|
||||
if (firstInitialization) {
|
||||
// check for error every time the file changes
|
||||
m_configWatcher.reset(new QFileSystemWatcher());
|
||||
@@ -449,9 +443,6 @@ void ConfigHandler::setValue(const QString& key, const QVariant& value)
|
||||
QVariant ConfigHandler::value(const QString& key) const
|
||||
{
|
||||
assertKeyRecognized(key);
|
||||
// Perform check on entire config if due. Please make sure that this
|
||||
// function is called in all scenarios - best to keep it on top.
|
||||
hasError();
|
||||
|
||||
auto val = m_settings.value(key);
|
||||
|
||||
@@ -468,6 +459,16 @@ QVariant ConfigHandler::value(const QString& key) const
|
||||
return handler->value(val);
|
||||
}
|
||||
|
||||
void ConfigHandler::remove(const QString& key)
|
||||
{
|
||||
m_settings.remove(key);
|
||||
}
|
||||
|
||||
void ConfigHandler::resetValue(const QString& key)
|
||||
{
|
||||
m_settings.setValue(key, valueHandler(key)->fallback());
|
||||
}
|
||||
|
||||
QSet<QString>& ConfigHandler::recognizedGeneralOptions()
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
@@ -492,8 +493,10 @@ QSet<QString>& ConfigHandler::recognizedShortcutNames()
|
||||
return names;
|
||||
}
|
||||
|
||||
// Return keys from group `group`. Use CONFIG_GROUP_GENERAL (General) for
|
||||
// general settings.
|
||||
/**
|
||||
* @brief Return keys from group `group`.
|
||||
* Use CONFIG_GROUP_GENERAL (General) for general settings.
|
||||
*/
|
||||
QSet<QString> ConfigHandler::keysFromGroup(const QString& group) const
|
||||
{
|
||||
QSet<QString> keys;
|
||||
@@ -515,20 +518,6 @@ bool ConfigHandler::checkForErrors(AbstractLogger* log) const
|
||||
checkSemantics(log);
|
||||
}
|
||||
|
||||
void ConfigHandler::cleanUnusedKeys(const QString& group,
|
||||
const QSet<QString>& keys) const
|
||||
{
|
||||
for (const QString& key : keys) {
|
||||
if (group == CONFIG_GROUP_GENERAL && !key.contains('/')) {
|
||||
m_settings.remove(key);
|
||||
} else {
|
||||
m_settings.beginGroup(group);
|
||||
m_settings.remove(key);
|
||||
m_settings.endGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse the config to find settings with unrecognized names.
|
||||
* @return Whether the config passes this check.
|
||||
@@ -537,7 +526,8 @@ void ConfigHandler::cleanUnusedKeys(const QString& group,
|
||||
* `recognizedGeneralOptions` or `recognizedShortcutNames` depending on the
|
||||
* group the option belongs to.
|
||||
*/
|
||||
bool ConfigHandler::checkUnrecognizedSettings(AbstractLogger* log) const
|
||||
bool ConfigHandler::checkUnrecognizedSettings(AbstractLogger* log,
|
||||
QList<QString>* offenders) const
|
||||
{
|
||||
// sort the config keys by group
|
||||
QSet<QString> generalKeys = keysFromGroup(CONFIG_GROUP_GENERAL),
|
||||
@@ -549,38 +539,20 @@ bool ConfigHandler::checkUnrecognizedSettings(AbstractLogger* log) const
|
||||
generalKeys.subtract(recognizedGeneralKeys);
|
||||
shortcutKeys.subtract(recognizedShortcutKeys);
|
||||
|
||||
// automatically clean up unused keys
|
||||
if (!generalKeys.isEmpty()) {
|
||||
cleanUnusedKeys(CONFIG_GROUP_GENERAL, generalKeys);
|
||||
generalKeys = keysFromGroup(CONFIG_GROUP_GENERAL),
|
||||
generalKeys.subtract(recognizedGeneralKeys);
|
||||
}
|
||||
if (!shortcutKeys.isEmpty()) {
|
||||
cleanUnusedKeys(CONFIG_GROUP_SHORTCUTS, shortcutKeys);
|
||||
shortcutKeys = keysFromGroup(CONFIG_GROUP_SHORTCUTS);
|
||||
shortcutKeys.subtract(recognizedShortcutKeys);
|
||||
}
|
||||
|
||||
// clean up unused groups
|
||||
QStringList settingsGroups = m_settings.childGroups();
|
||||
for (const auto& group : settingsGroups) {
|
||||
if (group != QLatin1String(CONFIG_GROUP_SHORTCUTS) &&
|
||||
group != QLatin1String(CONFIG_GROUP_GENERAL)) {
|
||||
m_settings.beginGroup(group);
|
||||
m_settings.remove("");
|
||||
m_settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
// what is left are the unrecognized keys - hopefully empty
|
||||
bool ok = generalKeys.isEmpty() && shortcutKeys.isEmpty();
|
||||
if (log != nullptr) {
|
||||
if (log != nullptr || offenders != nullptr) {
|
||||
for (const QString& key : generalKeys) {
|
||||
*log << QStringLiteral("Unrecognized setting: '%1'\n").arg(key);
|
||||
if (log)
|
||||
*log << tr("Unrecognized setting: '%1'\n").arg(key);
|
||||
if (offenders)
|
||||
offenders->append(key);
|
||||
}
|
||||
for (const QString& key : shortcutKeys) {
|
||||
*log
|
||||
<< QStringLiteral("Unrecognized shortcut name: '%1'.\n").arg(key);
|
||||
if (log)
|
||||
*log << tr("Unrecognized shortcut name: '%1'.\n").arg(key);
|
||||
if (offenders)
|
||||
offenders->append(CONFIG_GROUP_SHORTCUTS "/" + key);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
@@ -619,8 +591,8 @@ bool ConfigHandler::checkShortcutConflicts(AbstractLogger* log) const
|
||||
!reportedInLog.contains(*key2)) { // log entries
|
||||
reportedInLog.append(*key1);
|
||||
reportedInLog.append(*key2);
|
||||
*log << QStringLiteral("Shortcut conflict: '%1' and '%2' "
|
||||
"have the same shortcut: %3\n")
|
||||
*log << tr("Shortcut conflict: '%1' and '%2' "
|
||||
"have the same shortcut: %3\n")
|
||||
.arg(*key1)
|
||||
.arg(*key2)
|
||||
.arg(value1);
|
||||
@@ -634,9 +606,12 @@ bool ConfigHandler::checkShortcutConflicts(AbstractLogger* log) const
|
||||
|
||||
/**
|
||||
* @brief Check each config value semantically.
|
||||
* @param log Destination for error log output.
|
||||
* @param offenders Destination for the semantically invalid keys.
|
||||
* @return Whether the config passes this check.
|
||||
*/
|
||||
bool ConfigHandler::checkSemantics(AbstractLogger* log) const
|
||||
bool ConfigHandler::checkSemantics(AbstractLogger* log,
|
||||
QList<QString>* offenders) const
|
||||
{
|
||||
QStringList allKeys = m_settings.allKeys();
|
||||
bool ok = true;
|
||||
@@ -650,14 +625,17 @@ bool ConfigHandler::checkSemantics(AbstractLogger* log) const
|
||||
QVariant val = m_settings.value(key);
|
||||
auto valueHandler = this->valueHandler(key);
|
||||
if (val.isValid() && !valueHandler->check(val)) {
|
||||
// Key does not pass the check
|
||||
ok = false;
|
||||
if (log == nullptr) {
|
||||
if (log == nullptr && offenders == nullptr)
|
||||
break;
|
||||
} else {
|
||||
*log << QStringLiteral("Semantic error in '%1'. Expected: %2\n")
|
||||
if (log != nullptr) {
|
||||
*log << tr("Bad value in '%1'. Expected: %2\n")
|
||||
.arg(key)
|
||||
.arg(valueHandler->expected());
|
||||
}
|
||||
if (offenders != nullptr)
|
||||
offenders->append(key);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
@@ -724,7 +702,8 @@ bool ConfigHandler::hasError() const
|
||||
/// Error message that can be used by other classes as well
|
||||
QString ConfigHandler::errorMessage() const
|
||||
{
|
||||
return tr("The configuration contains an error. Falling back to default.");
|
||||
return tr(
|
||||
"The configuration contains an error. Open configuration to resolve.");
|
||||
}
|
||||
|
||||
void ConfigHandler::ensureFileWatched() const
|
||||
@@ -801,7 +780,7 @@ QString ConfigHandler::baseName(QString key) const
|
||||
// STATIC MEMBER DEFINITIONS
|
||||
|
||||
bool ConfigHandler::m_hasError = false;
|
||||
bool ConfigHandler::m_errorCheckPending = false;
|
||||
bool ConfigHandler::m_errorCheckPending = true;
|
||||
bool ConfigHandler::m_skipNextErrorCheck = false;
|
||||
|
||||
QSharedPointer<QFileSystemWatcher> ConfigHandler::m_configWatcher;
|
||||
|
||||
Reference in New Issue
Block a user