diff --git a/src/utils/kateconfig.h b/src/utils/kateconfig.h --- a/src/utils/kateconfig.h +++ b/src/utils/kateconfig.h @@ -26,6 +26,9 @@ #include "ktexteditor/view.h" #include +#include +#include + #include #include #include @@ -48,19 +51,115 @@ /** * Base Class for the Kate Config Classes */ -class KateConfig +class KTEXTEDITOR_EXPORT KateConfig { public: /** - * Default Constructor + * Construct a KateConfig. + * @param parent parent config object, if any */ - KateConfig(); + KateConfig(const KateConfig *parent = nullptr); /** * Virtual Destructor */ virtual ~KateConfig(); +protected: + /** + * One config entry. + */ + class ConfigEntry { + public: + /** + * Construct one config entry. + * @param enumId value of the enum for this config entry + * @param configId value of the key for the KConfig file for this config entry + * @param command command name + * @param defaultVal default value + * @param valid validator function, default none + */ + ConfigEntry(int enumId, const char *configId, QString command, QVariant defaultVal, std::function valid = nullptr) + : enumKey(enumId) + , configKey(configId) + , commandName(command) + , defaultValue(defaultVal) + , value(defaultVal) + , validator(valid) + { + } + + /** + * Enum key for this config entry, shall be unique + */ + const int enumKey; + + /** + * KConfig entry key for this config entry, shall be unique in its group + * e.g. "Tab Width" + */ + const char * const configKey; + + /** + * Command name as used in e.g. ConfigInterface or modeline/command line + * e.g. tab-width + */ + const QString commandName; + + /** + * Default value if nothing configured + */ + const QVariant defaultValue; + + /** + * concrete value, per default == defaultValue + */ + QVariant value; + + /** + * a validator function, only if this returns true we accept a value for the entry + * is ignored if not set + */ + std::function validator; + }; + + /** + * Get full map of config entries, aka the m_configEntries of the top config object + * @return full map with all config entries + */ + const std::map &fullConfigEntries () const + { + return m_parent ? m_parent->fullConfigEntries() : m_configEntries; + } + + /** + * Read all config entries from given config group + * @param config config group to read from + */ + void readConfigEntries(const KConfigGroup &config); + + /** + * Write all config entries to given config group + * @param config config group to write to + */ + void writeConfigEntries(KConfigGroup &config) const; + + /** + * Register a new config entry. + * Used by the sub classes to register all there known ones. + * @param entry new entry to add + */ + void addConfigEntry(const ConfigEntry &&entry) + { + m_configEntries.emplace(entry.enumKey, entry); + } + + /** + * Finalize the config entries. + * Called by the sub classes after all entries are registered + */ + void finalizeConfigEntries(); + public: /** * start some config changes @@ -76,13 +175,34 @@ */ void configEnd(); + /** + * Get a config value. + * @param key config key, aka enum from KateConfig* classes + * @return value for the wanted key, will assert if key is not valid + */ + QVariant value(const int key) const; + + /** + * Set a config value. + * Will assert if key is invalid. + * Might not alter the value if given value fails validation. + * @param key config key, aka enum from KateConfig* classes + * @param value value to set + */ + void setValue(const int key, const QVariant &value); + protected: /** * do the real update */ virtual void updateConfig() = 0; private: + /** + * parent config object, if any + */ + const KateConfig *m_parent = nullptr; + /** * recursion depth */ @@ -92,6 +212,15 @@ * is a config session running */ bool configIsRunning = false; + + /** + * two cases: + * - we have m_parent == nullptr => this contains all known config entries + * - we have m_parent != nullptr => this contains all set config entries for this level of configuration + * + * uses a map ATM for deterministic iteration e.g. for read/writeConfig + */ + std::map m_configEntries; }; class KTEXTEDITOR_EXPORT KateGlobalConfig : public KateConfig @@ -179,6 +308,16 @@ return (this == global()); } + /** + * Known config entries + */ + enum ConfigEntryTypes { + /** + * Tabulator width + */ + TabWidth + }; + public: /** * Read config from object @@ -194,8 +333,15 @@ void updateConfig() override; public: - int tabWidth() const; - void setTabWidth(int tabWidth); + int tabWidth() const + { + return value(TabWidth).toInt(); + } + + void setTabWidth(int tabWidth) + { + setValue(TabWidth, QVariant(tabWidth)); + } int indentationWidth() const; void setIndentationWidth(int indentationWidth); @@ -330,7 +476,6 @@ private: QString m_indentationMode; int m_indentationWidth = 2; - int m_tabWidth = 4; uint m_tabHandling = tabSmart; uint m_configFlags = 0; int m_wordWrapAt = 80; @@ -349,7 +494,6 @@ bool m_onTheFlySpellCheck; int m_lineLengthLimit; - bool m_tabWidthSet : 1; bool m_indentationWidthSet : 1; bool m_indentationModeSet : 1; bool m_wordWrapSet : 1; diff --git a/src/utils/kateconfig.cpp b/src/utils/kateconfig.cpp --- a/src/utils/kateconfig.cpp +++ b/src/utils/kateconfig.cpp @@ -37,14 +37,39 @@ #include //BEGIN KateConfig -KateConfig::KateConfig() +KateConfig::KateConfig(const KateConfig *parent) + : m_parent(parent) { } KateConfig::~KateConfig() { } +void KateConfig::finalizeConfigEntries() +{ +} + +void KateConfig::readConfigEntries(const KConfigGroup &config) +{ + configStart(); + + // read all config entries, even the ones ATM not set in this config object but known in the toplevel one + for (const auto &entry : fullConfigEntries()) { + setValue(entry.second.enumKey, config.readEntry(entry.second.configKey, entry.second.defaultValue)); + } + + configEnd(); +} + +void KateConfig::writeConfigEntries(KConfigGroup &config) const +{ + // write all config entries, even the ones ATM not set in this config object but known in the toplevel one + for (const auto &entry : fullConfigEntries()) { + config.writeEntry(entry.second.configKey, value(entry.second.enumKey)); + } +} + void KateConfig::configStart() { configSessionNumber++; @@ -72,6 +97,58 @@ updateConfig(); } + +QVariant KateConfig::value(const int key) const +{ + // first: local lookup + const auto it = m_configEntries.find(key); + if (it != m_configEntries.end()) { + return it->second.value; + } + + // else: fallback to parent config, if any + if (m_parent) { + return m_parent->value(key); + } + + // if we arrive here, the key was invalid! + Q_ASSERT(false); + return QVariant(); +} + +void KateConfig::setValue(const int key, const QVariant &value) +{ + // check: is this key known at all? => if not, programming error + const auto &knownEntries = fullConfigEntries(); + const auto knownIt = knownEntries.find(key); + Q_ASSERT(knownIt != knownEntries.end()); + + // validator set? use it, if not accepting, abort setting + if (knownIt->second.validator && !knownIt->second.validator(value)) { + return; + } + + // check if value already there for this config + auto valueIt = m_configEntries.find(key); + if (valueIt != m_configEntries.end()) { + // skip any work if value is equal + if (valueIt->second.value == value) { + return; + } + + // else: alter value and be done + configStart(); + valueIt->second.value = value; + configEnd(); + return; + } + + // if not in this hash, we must copy the known entry and adjust the value + configStart(); + auto res = m_configEntries.emplace(key, knownIt->second); + res.first->second.value = value; + configEnd(); +} //END //BEGIN KateDocumentConfig @@ -166,8 +243,7 @@ } KateDocumentConfig::KateDocumentConfig() - : m_tabWidthSet(false), - m_indentationWidthSet(false), + : m_indentationWidthSet(false), m_indentationModeSet(false), m_wordWrapSet(false), m_wordWrapAtSet(false), @@ -200,18 +276,27 @@ { s_global = this; + /** + * init all known config entries + */ + addConfigEntry(ConfigEntry(TabWidth, "Tab Width", QStringLiteral("tab-width"), 4, [](const QVariant &value) { return value.toInt() >= 1; })); + + /** + * finalize the entries, e.g. hashs them + */ + finalizeConfigEntries(); + // init with defaults from config or really hardcoded ones KConfigGroup cg(KTextEditor::EditorPrivate::config(), "KTextEditor Document"); readConfig(cg); } KateDocumentConfig::KateDocumentConfig(const KConfigGroup &cg) - : m_indentationWidth(2), - m_tabWidth(4), + : KateConfig(s_global), + m_indentationWidth(2), m_tabHandling(tabSmart), m_configFlags(0), m_wordWrapAt(80), - m_tabWidthSet(false), m_indentationWidthSet(false), m_indentationModeSet(false), m_wordWrapSet(false), @@ -249,9 +334,9 @@ } KateDocumentConfig::KateDocumentConfig(KTextEditor::DocumentPrivate *doc) - : m_tabHandling(tabSmart), + : KateConfig(s_global), + m_tabHandling(tabSmart), m_configFlags(0), - m_tabWidthSet(false), m_indentationWidthSet(false), m_indentationModeSet(false), m_wordWrapSet(false), @@ -292,7 +377,6 @@ namespace { -const char KEY_TAB_WIDTH[] = "Tab Width"; const char KEY_INDENTATION_WIDTH[] = "Indentation Width"; const char KEY_INDENTATION_MODE[] = "Indentation Mode"; const char KEY_TAB_HANDLING[] = "Tab Handling"; @@ -329,7 +413,8 @@ { configStart(); - setTabWidth(config.readEntry(KEY_TAB_WIDTH, 4)); + // read generic entries + readConfigEntries(config); setIndentationWidth(config.readEntry(KEY_INDENTATION_WIDTH, 4)); @@ -380,7 +465,8 @@ void KateDocumentConfig::writeConfig(KConfigGroup &config) { - config.writeEntry(KEY_TAB_WIDTH, tabWidth()); + // write generic entries + writeConfigEntries(config); config.writeEntry(KEY_INDENTATION_WIDTH, indentationWidth()); config.writeEntry(KEY_INDENTATION_MODE, indentationMode()); @@ -446,33 +532,6 @@ } } -int KateDocumentConfig::tabWidth() const -{ - if (m_tabWidthSet || isGlobal()) { - return m_tabWidth; - } - - return s_global->tabWidth(); -} - -void KateDocumentConfig::setTabWidth(int tabWidth) -{ - if (tabWidth < 1) { - return; - } - - if (m_tabWidthSet && m_tabWidth == tabWidth) { - return; - } - - configStart(); - - m_tabWidthSet = true; - m_tabWidth = tabWidth; - - configEnd(); -} - int KateDocumentConfig::indentationWidth() const { if (m_indentationWidthSet || isGlobal()) { @@ -1228,9 +1287,7 @@ //BEGIN KateViewConfig KateViewConfig::KateViewConfig() - : - - m_dynWordWrapSet(false), + : m_dynWordWrapSet(false), m_dynWrapAtStaticMarkerSet(false), m_dynWordWrapIndicatorsSet(false), m_dynWordWrapAlignIndentSet(false), @@ -1277,7 +1334,7 @@ } KateViewConfig::KateViewConfig(KTextEditor::ViewPrivate *view) - : + : KateConfig(s_global), m_searchFlags(PowerModePlainText), m_maxHistorySize(100), m_showWordCount(false), @@ -1372,6 +1429,9 @@ { configStart(); + // read generic entries + readConfigEntries(config); + // default on setDynWordWrap(config.readEntry(KEY_DYN_WORD_WRAP, true)); setDynWrapAtStaticMarker(config.readEntry(KEY_DYN_WORD_WRAP_AT_STATIC_MARKER, false)); @@ -1438,6 +1498,9 @@ void KateViewConfig::writeConfig(KConfigGroup &config) { + // write generic entries + writeConfigEntries(config); + config.writeEntry(KEY_DYN_WORD_WRAP, dynWordWrap()); config.writeEntry(KEY_DYN_WORD_WRAP_AT_STATIC_MARKER, dynWrapAtStaticMarker()); config.writeEntry(KEY_DYN_WORD_WRAP_INDICATORS, dynWordWrapIndicators()); @@ -2396,7 +2459,8 @@ } KateRendererConfig::KateRendererConfig(KateRenderer *renderer) - : m_fontMetrics(QFont()), + : KateConfig(s_global), + m_fontMetrics(QFont()), m_lineMarkerColor(KTextEditor::MarkInterface::reservedMarkersCount()), m_schemaSet(false), m_fontSet(false), @@ -2445,6 +2509,9 @@ { configStart(); + // read generic entries + readConfigEntries(config); + // "Normal" Schema MUST BE THERE, see global kateschemarc setSchema(config.readEntry(KEY_SCHEMA, "Normal")); @@ -2461,6 +2528,9 @@ void KateRendererConfig::writeConfig(KConfigGroup &config) { + // write generic entries + writeConfigEntries(config); + config.writeEntry(KEY_SCHEMA, schema()); config.writeEntry(KEY_WORD_WRAP_MARKER, wordWrapMarker());