#ifndef QTMVVM_SETTINGSENTRY_H #define QTMVVM_SETTINGSENTRY_H #include #include #include "QtMvvmCore/qtmvvmcore_global.h" #include "QtMvvmCore/isettingsaccessor.h" namespace QtMvvm { template class SettingsEntry { Q_DISABLE_COPY(SettingsEntry) public: SettingsEntry() = default; bool isSet() const; QString key() const; T get() const; void set(const T &value); void reset(); SettingsEntry &operator=(const T &value); operator T() const; void addChangeCallback(const std::function &callback); void addChangeCallback(QObject *scope, const std::function &callback); // internal void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {}); private: QString _key; ISettingsAccessor *_accessor = nullptr; QVariant _default; }; template class SettingsEntry> { Q_DISABLE_COPY(SettingsEntry) public: class ListElement { Q_DISABLE_COPY(ListElement) public: inline ListElement(ListElement &&other) noexcept = default; inline ListElement &operator=(ListElement &&other) noexcept = default; TType get() const && { return _self->getAt(_index); } void set(const TType &value) && { _self->setAt(_index, value); } inline ListElement &operator=(const TType &value) && { _self->setAt(_index, value); return *this; } inline operator TType() const && { return _self->getAt(_index); } private: friend class SettingsEntry>; inline ListElement(SettingsEntry> *self, int index) : _self{self}, _index{index} {} SettingsEntry> *_self; int _index; }; template class iterator_base; class iterator_value { public: TType get() const { return _self->getAt(_index); } void set(const TType &value) { _self->setAt(_index, value); } inline iterator_value &operator=(const TType &value) { _self->setAt(_index, value); return *this; } inline operator TType() const { return _self->getAt(_index); } inline friend void swap(iterator_value& lhs, iterator_value& rhs) { qSwap(lhs._self, rhs._self); qSwap(lhs._index, rhs._index); } inline friend bool operator==(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index == rhs._index; } inline friend bool operator!=(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index != rhs._index; } inline friend bool operator<(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index < rhs._index; } inline friend bool operator>(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index > rhs._index; } inline friend bool operator<=(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index <= rhs._index; } inline friend bool operator>=(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index >= rhs._index; } inline friend iterator_value operator+(const iterator_value &value, int delta) { return iterator_value{value._self, value._index + delta}; } inline friend iterator_value operator+(int delta, const iterator_value &value) { return iterator_value{value._self, value._index + delta}; } inline friend iterator_value operator-(const iterator_value &value, int delta) { return iterator_value{value._self, value._index - delta}; } inline friend int operator-(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index - rhs._index; } private: template friend class SettingsEntry>::iterator_base; inline iterator_value(SettingsEntry> *self, int index) : _self{self}, _index{index} {} inline iterator_value(const iterator_value &other) = default; inline iterator_value &operator=(const iterator_value &other) = default; inline iterator_value(iterator_value &&other) noexcept = default; inline iterator_value &operator=(iterator_value &&other) noexcept = default; SettingsEntry> *_self; int _index; }; template class iterator_base { public: using iterator_category = std::random_access_iterator_tag; using difference_type = int; using value_type = T; using pointer = value_type*; using reference = value_type&; inline iterator_base(const iterator_base &other) = default; inline iterator_base &operator=(const iterator_base &other) = default; inline iterator_base(iterator_base &&other) noexcept = default; inline iterator_base &operator=(iterator_base &&other) noexcept = default; inline iterator_base &operator++() { ++_value._index; return *this; } inline iterator_base operator++(int) { return iterator_base{_value._self, _value._index++}; } inline iterator_base &operator--() { --_value._index; return *this; } inline iterator_base operator--(int) { return iterator_base{_value._self, _value._index--}; } inline const value_type &operator*() const { return _value; } inline value_type &operator*() { return _value; } inline const value_type *operator->() const { return &_value; } inline value_type *operator->() { return &_value; } inline friend void swap(iterator_base &lhs, iterator_base &rhs) { swap(lhs._value, rhs._value); } inline friend bool operator==(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value == rhs._value; } inline friend bool operator!=(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value != rhs._value; } inline friend bool operator<(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value < rhs._value; } inline friend bool operator>(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value > rhs._value; } inline friend bool operator<=(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value <= rhs._value; } inline friend bool operator>=(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value >= rhs._value; } inline iterator_base &operator+=(difference_type delta) { _value._index += delta; return *this; } inline friend iterator_base operator+(const iterator_base &iter, difference_type delta) { return iterator_base{iter._value + delta}; } inline friend iterator_base operator+(difference_type delta, const iterator_base &iter) { return iterator_base{iter._value + delta}; } inline iterator_base &operator-=(difference_type delta) { _value._index -= delta; return *this; } inline friend iterator_base operator-(const iterator_base &iter, difference_type delta) { return iterator_base{iter._value - delta}; } inline friend difference_type operator-(const iterator_base &lhs, const iterator_base &rhs) { return lhs._value - rhs._value; } inline value_type operator[](difference_type delta) const { return iterator_value{_value._self, _value._index + delta}; } private: friend class SettingsEntry>; inline iterator_base(SettingsEntry> *self, int index) : _value{self, index} {} inline iterator_base(iterator_value value) : _value{std::move(value)} {} iterator_value _value; }; using iterator = iterator_base; using const_iterator = iterator_base; SettingsEntry() = default; bool isSet() const; QString key() const; QList get() const; void set(const QList &value); void reset(bool reInit = true); SettingsEntry> &operator=(const QList &value); operator QList() const; int size() const; TType getAt(int index) const; void setAt(int index, const TType &value); void push(const TType &value); void push(const QList &values); TType pop(); void chop(int count); ListElement operator[](int index); const ListElement operator[](int index) const; SettingsEntry> &operator+=(const TType &value); SettingsEntry> &operator+=(const QList &values); iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; const_iterator constBegin() const; const_iterator constEnd() const; void addChangeCallback(const std::function)> &callback); void addChangeCallback(QObject *scope, const std::function)> &callback); void addChangeCallback(const std::function &callback); // index, value void addChangeCallback(QObject *scope, const std::function &callback); void addSizeChangeCallback(const std::function &callback); // size void addSizeChangeCallback(QObject *scope, const std::function &callback); // internal void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {}); void setupInit(QList init); private: QString _key; ISettingsAccessor *_accessor = nullptr; QVariant _default; QList _init; }; template <> class SettingsEntry { Q_DISABLE_COPY(SettingsEntry) public: inline SettingsEntry() = default; // internal inline void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {} }; // ------------- Generic Implementation ------------- template bool SettingsEntry::isSet() const { return _accessor->contains(_key); } template QString SettingsEntry::key() const { return _key; } template T SettingsEntry::get() const { return _accessor->load(_key, _default).template value(); } template<> Q_MVVMCORE_EXPORT QVariant SettingsEntry::get() const; template void SettingsEntry::set(const T &value) { _accessor->save(_key, QVariant::fromValue(value)); } template<> Q_MVVMCORE_EXPORT void SettingsEntry::set(const QVariant &value); template void SettingsEntry::reset() { _accessor->remove(_key); } template SettingsEntry &SettingsEntry::operator=(const T &value) { set(value); return (*this); } template SettingsEntry::operator T() const { return get(); } template void SettingsEntry::addChangeCallback(const std::function &callback) { addChangeCallback(_accessor, callback); } template void SettingsEntry::addChangeCallback(QObject *scope, const std::function &callback) { auto mKey = _key; auto mDefault = _default; QObject::connect(_accessor, &ISettingsAccessor::entryChanged, scope, [mKey, callback](const QString &key, const QVariant &value) { if(key == mKey) callback(value.template value()); }); QObject::connect(_accessor, &ISettingsAccessor::entryRemoved, scope, [mKey, mDefault, callback](const QString &key) { if(key == mKey) callback(mDefault.template value()); }); } template<> Q_MVVMCORE_EXPORT void SettingsEntry::addChangeCallback(QObject *scope, const std::function &callback); template void SettingsEntry::setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue) { Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!"); _key = std::move(key); _accessor = accessor; _default = std::move(defaultValue); } // ------------- Generic Implementation ListEntry ------------- template bool SettingsEntry>::isSet() const { return _accessor->contains(_key + QStringLiteral("/size")); } template QString SettingsEntry>::key() const { return _key; } template QList SettingsEntry>::get() const { auto mSize = size(); QList resList; resList.reserve(mSize); for(auto i = 0; i < mSize; ++i) resList.append(getAt(i)); return resList; } template void SettingsEntry>::set(const QList &value) { reset(false); push(value); } template void SettingsEntry>::reset(bool reInit) { _accessor->remove(_key); if(reInit && !_init.isEmpty()) push(_init); } template SettingsEntry> &SettingsEntry>::operator=(const QList &value) { set(value); return *this; } template SettingsEntry>::operator QList() const { return get(); } template int SettingsEntry>::size() const { return _accessor->load(_key + QStringLiteral("/size"), 0).toInt(); } template TType SettingsEntry>::getAt(int index) const { return _accessor->load(_key + QStringLiteral("/%1/value").arg(index), _default).template value(); } template void SettingsEntry>::setAt(int index, const TType &value) { _accessor->save(_key + QStringLiteral("/%1/value").arg(index), QVariant::fromValue(value)); } template void SettingsEntry>::push(const TType &value) { push(QList{value}); } template void SettingsEntry>::push(const QList &values) { auto cIndex = size(); for(const auto &value : values) setAt(cIndex++, value); _accessor->save(_key + QStringLiteral("/size"), cIndex); } template TType QtMvvm::SettingsEntry>::pop() { auto res = getAt(size() - 1); chop(1); return res; } template void QtMvvm::SettingsEntry>::chop(int count) { auto cSize = size(); auto nSize = qMax(size() - count, 0); for(auto i = cSize - 1; i >= nSize; --i) _accessor->remove(_key + QStringLiteral("/%1/value").arg(i)); _accessor->save(_key + QStringLiteral("/size"), nSize); } template const typename SettingsEntry>::ListElement SettingsEntry>::operator[](int index) const { return ListElement{this, index}; } template typename SettingsEntry>::ListElement SettingsEntry>::operator[](int index) { return ListElement{this, index}; } template SettingsEntry> &SettingsEntry>::operator+=(const TType &value) { push(value); return *this; } template SettingsEntry> &SettingsEntry>::operator+=(const QList &values) { push(values); return *this; } template typename SettingsEntry>::iterator SettingsEntry>::begin() { return iterator{this, 0}; } template typename SettingsEntry>::iterator QtMvvm::SettingsEntry>::end() { return iterator{this, size()}; } template typename SettingsEntry>::const_iterator SettingsEntry>::begin() const { return constBegin(); } template typename SettingsEntry>::const_iterator SettingsEntry>::end() const { return constEnd(); } template typename SettingsEntry>::const_iterator SettingsEntry>::constBegin() const { return const_iterator{const_cast>*>(this), 0}; } template typename SettingsEntry>::const_iterator SettingsEntry>::constEnd() const { return const_iterator{const_cast>*>(this), size()}; } template void SettingsEntry>::addChangeCallback(const std::function)> &callback) { addChangeCallback(_accessor, callback); } template void SettingsEntry>::addChangeCallback(QObject *scope, const std::function)> &callback) { QObject::connect(_accessor, &ISettingsAccessor::entryChanged, scope, [this, callback](const QString &key, const QVariant &) { if(key.startsWith(_key)) callback(get()); }); QObject::connect(_accessor, &ISettingsAccessor::entryRemoved, scope, [this, callback](const QString &key) { if(key.startsWith(_key)) callback(get()); }); } template void SettingsEntry>::addChangeCallback(const std::function &callback) { addChangeCallback(_accessor, callback); } template void SettingsEntry>::addChangeCallback(QObject *scope, const std::function &callback) { QRegularExpression mKey {QStringLiteral("^%1\\/\\d+\\/value$").arg(QRegularExpression::escape(_key))}; mKey.optimize(); auto mDefault = _default; QObject::connect(_accessor, &ISettingsAccessor::entryChanged, scope, [mKey, callback](const QString &key, const QVariant &value) { auto match = mKey.match(key); if(match.hasMatch()) callback(match.captured(1).toInt(), value.template value()); }); QObject::connect(_accessor, &ISettingsAccessor::entryRemoved, scope, [mKey, mDefault, callback](const QString &key) { auto match = mKey.match(key); if(match.hasMatch()) callback(match.captured(1).toInt(), mDefault.template value()); }); } template void SettingsEntry>::addSizeChangeCallback(const std::function &callback) { addSizeChangeCallback(_accessor, callback); } template void SettingsEntry>::addSizeChangeCallback(QObject *scope, const std::function &callback) { QString mKey = _key + QStringLiteral("/size"); auto mInit = _init; QObject::connect(_accessor, &ISettingsAccessor::entryChanged, scope, [mKey, callback](const QString &key, const QVariant &value) { if(key == mKey) callback(value.toInt()); }); QObject::connect(_accessor, &ISettingsAccessor::entryRemoved, scope, [mKey, mInit, callback](const QString &key) { if(key == mKey) callback(0); //is ok, as in case of a "reinit", the entry changed will be emitted next }); } template void SettingsEntry>::setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue) { Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!"); _key = std::move(key); _accessor = accessor; _default = std::move(defaultValue); } template void QtMvvm::SettingsEntry >::setupInit(QList init) { _init = std::move(init); if(!isSet()) push(_init); } } #endif // QTMVVM_SETTINGSENTRY_H