summaryrefslogtreecommitdiffhomepage
path: root/options
diff options
context:
space:
mode:
Diffstat (limited to 'options')
-rw-r--r--options/base-value.cpp44
-rw-r--r--options/base-value.hpp118
-rw-r--r--options/bundle.cpp191
-rw-r--r--options/bundle.hpp102
-rw-r--r--options/connector.cpp125
-rw-r--r--options/connector.hpp50
-rw-r--r--options/defs.hpp7
-rw-r--r--options/globals.cpp195
-rw-r--r--options/globals.hpp74
-rw-r--r--options/group.cpp190
-rw-r--r--options/group.hpp102
-rw-r--r--options/lang/de_DE.ts4
-rw-r--r--options/lang/zh_CN.ts2
-rw-r--r--options/metatype.cpp36
-rw-r--r--options/metatype.hpp47
-rw-r--r--options/options.hpp3
-rw-r--r--options/scoped.cpp26
-rw-r--r--options/scoped.hpp13
-rw-r--r--options/slider.cpp56
-rw-r--r--options/slider.hpp51
-rw-r--r--options/tie.cpp109
-rw-r--r--options/tie.hpp56
-rw-r--r--options/value-traits.hpp127
-rw-r--r--options/value.cpp2
-rw-r--r--options/value.hpp237
25 files changed, 1044 insertions, 923 deletions
diff --git a/options/base-value.cpp b/options/base-value.cpp
index 6c287321..950629d0 100644
--- a/options/base-value.cpp
+++ b/options/base-value.cpp
@@ -1,39 +1,33 @@
#include "base-value.hpp"
+#include <QThread>
using namespace options;
-base_value::base_value(bundle b, const QString& name, base_value::comparator cmp, std::type_index type_idx) :
- b(b),
- self_name(name),
- cmp(cmp),
- type_index(type_idx)
-{
- b->on_value_created(name, this);
-}
+//#define OTR_TRACE_NOTIFY
-base_value::~base_value()
-{
- b->on_value_destructed(self_name, this);
-}
+const bool value_::TRACE_NOTIFY =
+#ifdef OTR_TRACE_NOTIFY
+ true;
+#else
+ [] {
+ auto b = qgetenv("OTR_TRACE_NOTIFY");
+ return !b.isEmpty() && b != "0";
+ }();
+#endif
-void base_value::notify() const
+value_::value_(bundle const& b, const QString& name) noexcept :
+ b(b), self_name(name)
{
- bundle_value_changed();
+ b->on_value_created(this);
}
-void base_value::store(const QVariant& datum)
+value_::~value_()
{
- b->store_kv(self_name, datum);
+ b->on_value_destructed(this);
}
-namespace options {
-namespace detail {
-
-void set_base_value_to_default(base_value* val)
+void value_::maybe_trace(const char* str) const
{
- val->set_to_default();
+ if (TRACE_NOTIFY)
+ qDebug().noquote() << str << QThread::currentThreadId() << b->name() << self_name << get_variant();
}
-
-} // ns options::detail
-} // ns options
-
diff --git a/options/base-value.hpp b/options/base-value.hpp
index b094d693..5317191b 100644
--- a/options/base-value.hpp
+++ b/options/base-value.hpp
@@ -3,9 +3,12 @@
#include "bundle.hpp"
#include "slider.hpp"
#include "connector.hpp"
+#include "metatype.hpp"
#include "export.hpp"
-#include "compat/meta.hpp"
+#include "value-traits.hpp"
+
+#include <utility>
#include <QObject>
#include <QString>
@@ -13,92 +16,87 @@
#include <QPointF>
#include <QVariant>
-#include <typeindex>
-
-#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
-#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t) const
+#define OTR_OPTIONS_SLOT(t) void setValue(t datum) noexcept { store_(datum); }
+#define OTR_OPTIONS_SIGNAL(t) void valueChanged(t) const
namespace options {
-class OTR_OPTIONS_EXPORT base_value : public QObject
+class OTR_OPTIONS_EXPORT value_ : public QObject
{
Q_OBJECT
- friend class detail::connector;
-
- using comparator = bool(*)(const QVariant& val1, const QVariant& val2);
+ template<typename t> using cv_qualified = detail::cv_qualified<t>;
template<typename t>
- using signal_sig = void(base_value::*)(cv_qualified<t>) const;
+ using signal_sig = void(value_::*)(cv_qualified<t>) const;
public:
- bundle get_bundle() { return b; }
QString name() const { return self_name; }
- base_value(bundle b, const QString& name, comparator cmp, std::type_index type_idx);
- ~base_value() override;
+ value_(bundle const& b, const QString& name) noexcept;
+ ~value_() override;
- // MSVC has ODR problems in 15.4
- // no C++17 "constexpr inline" for data declarations in MSVC
template<typename t>
- constexpr static auto value_changed()
+ static constexpr auto value_changed()
{
- return static_cast<signal_sig<t>>(&base_value::valueChanged);
+ return static_cast<signal_sig<t>>(&value_::valueChanged);
}
- void notify() const;
+ static const bool TRACE_NOTIFY;
signals:
- OPENTRACK_DEFINE_SIGNAL(double);
- OPENTRACK_DEFINE_SIGNAL(float);
- OPENTRACK_DEFINE_SIGNAL(int);
- OPENTRACK_DEFINE_SIGNAL(bool);
- OPENTRACK_DEFINE_SIGNAL(const QString&);
- OPENTRACK_DEFINE_SIGNAL(const slider_value&);
- OPENTRACK_DEFINE_SIGNAL(const QPointF&);
- OPENTRACK_DEFINE_SIGNAL(const QVariant&);
-
- OPENTRACK_DEFINE_SIGNAL(const QList<double>&);
- OPENTRACK_DEFINE_SIGNAL(const QList<float>&);
- OPENTRACK_DEFINE_SIGNAL(const QList<int>&);
- OPENTRACK_DEFINE_SIGNAL(const QList<bool>&);
- OPENTRACK_DEFINE_SIGNAL(const QList<QString>&);
- OPENTRACK_DEFINE_SIGNAL(const QList<slider_value>&);
- OPENTRACK_DEFINE_SIGNAL(const QList<QPointF>&);
+ OTR_OPTIONS_SIGNAL(double);
+ OTR_OPTIONS_SIGNAL(float);
+ OTR_OPTIONS_SIGNAL(int);
+ OTR_OPTIONS_SIGNAL(bool);
+ OTR_OPTIONS_SIGNAL(const QString&);
+ OTR_OPTIONS_SIGNAL(const slider_value&);
+ OTR_OPTIONS_SIGNAL(const QPointF&);
+ OTR_OPTIONS_SIGNAL(const QVariant&);
+
+ OTR_OPTIONS_SIGNAL(const QList<double>&);
+ OTR_OPTIONS_SIGNAL(const QList<float>&);
+ OTR_OPTIONS_SIGNAL(const QList<int>&);
+ OTR_OPTIONS_SIGNAL(const QList<bool>&);
+ OTR_OPTIONS_SIGNAL(const QList<QString>&);
+ OTR_OPTIONS_SIGNAL(const QList<slider_value>&);
+ OTR_OPTIONS_SIGNAL(const QList<QPointF>&);
+
protected:
bundle b;
QString self_name;
- comparator cmp;
- std::type_index type_index;
- void store(const QVariant& datum);
+ virtual void store_variant(QVariant&&) noexcept = 0;
+ virtual void store_variant(const QVariant&) noexcept = 0;
+
+ void maybe_trace(const char* str) const;
template<typename t>
- void store(const t& datum)
+ void store_(const t& datum)
{
- b->store_kv(self_name, QVariant::fromValue(datum));
+ using traits = detail::value_traits<t>;
+ store_variant(traits::qvariant_from_value(datum));
}
public slots:
- OPENTRACK_DEFINE_SLOT(double)
- OPENTRACK_DEFINE_SLOT(int)
- OPENTRACK_DEFINE_SLOT(bool)
- OPENTRACK_DEFINE_SLOT(const QString&)
- OPENTRACK_DEFINE_SLOT(const slider_value&)
- OPENTRACK_DEFINE_SLOT(const QPointF&)
- OPENTRACK_DEFINE_SLOT(const QVariant&)
-
- OPENTRACK_DEFINE_SLOT(const QList<double>&)
- OPENTRACK_DEFINE_SLOT(const QList<float>&)
- OPENTRACK_DEFINE_SLOT(const QList<int>&)
- OPENTRACK_DEFINE_SLOT(const QList<bool>&)
- OPENTRACK_DEFINE_SLOT(const QList<QString>&)
- OPENTRACK_DEFINE_SLOT(const QList<slider_value>&)
- OPENTRACK_DEFINE_SLOT(const QList<QPointF>&)
-
- virtual void reload() = 0;
- virtual void bundle_value_changed() const = 0;
- virtual void set_to_default() = 0;
-
- friend void ::options::detail::set_base_value_to_default(base_value* val);
+ OTR_OPTIONS_SLOT(double)
+ OTR_OPTIONS_SLOT(int)
+ OTR_OPTIONS_SLOT(bool)
+ OTR_OPTIONS_SLOT(const QString&)
+ OTR_OPTIONS_SLOT(const slider_value&)
+ OTR_OPTIONS_SLOT(const QPointF&)
+ OTR_OPTIONS_SLOT(const QVariant&)
+
+ OTR_OPTIONS_SLOT(const QList<double>&)
+ OTR_OPTIONS_SLOT(const QList<float>&)
+ OTR_OPTIONS_SLOT(const QList<int>&)
+ OTR_OPTIONS_SLOT(const QList<bool>&)
+ OTR_OPTIONS_SLOT(const QList<QString>&)
+ OTR_OPTIONS_SLOT(const QList<slider_value>&)
+ OTR_OPTIONS_SLOT(const QList<QPointF>&)
+
+ virtual void set_to_default() noexcept = 0;
+ virtual void notify() const = 0;
+ virtual void notify_() const = 0;
+ virtual QVariant get_variant() const noexcept = 0;
};
} //ns options
diff --git a/options/bundle.cpp b/options/bundle.cpp
index 4a133402..517005f0 100644
--- a/options/bundle.cpp
+++ b/options/bundle.cpp
@@ -8,133 +8,134 @@
#include "bundle.hpp"
#include "value.hpp"
+#include "globals.hpp"
-#include <QThread>
-#include <QApplication>
+#include <cstdlib>
-using options::base_value;
+#include <QThread>
+#include <QCoreApplication>
-namespace options
-{
+using namespace options;
+using namespace options::globals;
-namespace detail {
+namespace options::detail {
-bundle::bundle(const QString& group_name)
- : mtx(QMutex::Recursive),
+bundle::bundle(const QString& group_name) :
group_name(group_name),
saved(group_name),
transient(saved)
{
}
-bundle::~bundle()
+bundle::~bundle() = default;
+
+void bundle::reload_no_notify()
{
+ if (group_name.isEmpty())
+ return;
+
+ QMutexLocker l{&mtx};
+
+ saved = group(group_name);
+ transient = saved;
}
-void bundle::reload()
+void bundle::notify()
{
- if (group_name.size())
{
QMutexLocker l(&mtx);
- saved = group(group_name);
- const bool has_changes = is_modified();
- transient = saved;
+ connector::notify_all_values();
+ }
- if (has_changes)
- {
- connector::notify_all_values();
- emit reloading();
- emit changed();
- }
+ emit reloading();
+ emit changed();
+}
+
+void bundle::reload()
+{
+ {
+ QMutexLocker l{&mtx};
+ reload_no_notify();
+ connector::notify_all_values();
}
+ emit reloading();
+ emit changed();
}
void bundle::set_all_to_default()
{
- QMutexLocker l(&mtx);
-
- forall([](const QString&, base_value* val) { set_base_value_to_default(val); });
-
- if (is_modified())
- group::mark_ini_modified();
+ connector::set_all_to_default_();
}
-void bundle::store_kv(const QString& name, const QVariant& datum)
+void bundle::store_kv(const QString& name, QVariant&& value)
{
- QMutexLocker l(&mtx);
-
- transient.put(name, datum);
+ if (group_name.isEmpty())
+ return;
- if (group_name.size())
+ {
+ options::globals::detail::mark_ini_modified();
+ QMutexLocker l{&mtx};
+ transient.put(name, value);
connector::notify_values(name);
+ }
emit changed();
}
+void bundle::store_kv(const QString& name, const QVariant& value)
+{
+ if (group_name.isEmpty())
+ return;
+
+ store_kv(name, QVariant{value});
+}
+
+QVariant bundle::get_variant(const QString& name) const
+{
+ QMutexLocker l{&mtx};
+ return transient.get_variant(name);
+}
+
bool bundle::contains(const QString &name) const
{
- QMutexLocker l(mtx);
+ QMutexLocker l{&mtx};
return transient.contains(name);
}
void bundle::save()
{
- if (QThread::currentThread() != qApp->thread())
+ if (QThread::currentThread() != qApp->thread()) // NOLINT
qDebug() << "group::save - current thread not ui thread";
- if (group_name.size() == 0)
+ if (group_name.isEmpty())
return;
- bool modified_ = false;
-
{
- QMutexLocker l(&mtx);
-
- if (is_modified())
- {
- //qDebug() << "bundle" << group_name << "changed, saving";
- modified_ = true;
- saved = transient;
- saved.save();
- }
+ QMutexLocker l{&mtx};
+ saved = transient;
+ saved.save();
}
- if (modified_)
- {
- qDebug() << "saving" << name();
- emit saving();
- }
+ emit saving();
}
-bool bundle::is_modified() const
+void bundler::reload_no_notify_()
{
- QMutexLocker l(mtx);
-
- for (const auto& kv : transient.kvs)
- {
- const QVariant other = saved.get<QVariant>(kv.first);
- if (!saved.contains(kv.first) || !is_equal(kv.first, kv.second, other))
- {
- //if (logspam)
- // qDebug() << "bundle" << group_name << "modified" << "key" << kv.first << "-" << other << "<>" << kv.second;
- return true;
- }
- }
+ QMutexLocker l(&implsgl_mtx);
- for (const auto& kv : saved.kvs)
+ for (auto& kv : implsgl_data)
{
- if (!transient.contains(kv.first))
+ weak bundle = kv.second;
+ shared bundle_ = bundle.lock();
+ if (bundle_)
{
- //if (logspam)
- // qDebug() << "bundle" << group_name << "modified" << "key" << kv.first << "-" << other << "<>" << kv.second;
- return true;
+ //qDebug() << "bundle: reverting" << kv.first << "due to profile change";
+ bundle_->reload_no_notify();
}
}
-
- return false;
}
-void bundler::after_profile_changed_()
+void bundler::notify_()
{
QMutexLocker l(&implsgl_mtx);
@@ -145,30 +146,31 @@ void bundler::after_profile_changed_()
if (bundle_)
{
//qDebug() << "bundle: reverting" << kv.first << "due to profile change";
- bundle_->reload();
+ bundle_->notify();
}
}
}
-void bundler::refresh_all_bundles()
+void bundler::reload_()
{
- singleton().after_profile_changed_();
+ QMutexLocker l(&implsgl_mtx);
+ notify_();
+ reload_no_notify_();
}
-bundler::bundler() : implsgl_mtx(QMutex::Recursive)
-{
-}
+void bundler::notify() { singleton().notify_(); }
+void bundler::reload_no_notify() { singleton().reload_no_notify_(); }
+void bundler::reload() { singleton().reload_(); }
-bundler::~bundler()
-{
- //qDebug() << "exit: bundle singleton";
-}
+bundler::bundler() = default;
+bundler::~bundler() = default;
-std::shared_ptr<bundler::v> bundler::make_bundle(const bundler::k& key)
+std::shared_ptr<bundler::v> bundler::make_bundle_(const k& key)
{
QMutexLocker l(&implsgl_mtx);
- auto it = implsgl_data.find(key);
+ using iter = decltype(implsgl_data.cbegin());
+ const iter it = implsgl_data.find(key);
if (it != implsgl_data.end())
{
@@ -182,33 +184,36 @@ std::shared_ptr<bundler::v> bundler::make_bundle(const bundler::k& key)
auto shr = shared(new v(key), [this, key](v* ptr) {
QMutexLocker l(&implsgl_mtx);
- auto it = implsgl_data.find(key);
+ const iter it = implsgl_data.find(key);
if (it != implsgl_data.end())
- implsgl_data.erase(it);
+ (void)implsgl_data.erase(it);
else
- qDebug() << "ERROR: can't find self-bundle!";
+ {
+ qCritical() << "ERROR: can't find self-bundle!";
+ std::abort();
+ }
delete ptr;
});
implsgl_data[key] = weak(shr);
return shr;
}
-OTR_OPTIONS_EXPORT bundler& singleton()
+bundler& bundler::singleton()
{
static bundler ret;
return ret;
}
-QMutex* bundle::get_mtx() const { return mtx; }
+} // ns options::detail
-} // end options::detail
+namespace options {
-OTR_OPTIONS_EXPORT std::shared_ptr<bundle_> make_bundle(const QString& name)
+std::shared_ptr<bundle_> make_bundle(const QString& name)
{
- if (name.size())
- return detail::singleton().make_bundle(name);
+ if (!name.isEmpty())
+ return detail::bundler::singleton().make_bundle_(name);
else
return std::make_shared<bundle_>(QString());
}
-} // end options
+} // ns options
diff --git a/options/bundle.hpp b/options/bundle.hpp
index 9b7b7f02..158fcef9 100644
--- a/options/bundle.hpp
+++ b/options/bundle.hpp
@@ -10,10 +10,11 @@
#include "group.hpp"
#include "connector.hpp"
+#include "compat/qhash.hpp"
#include <memory>
#include <tuple>
-#include <map>
+#include <unordered_map>
#include <memory>
#include <vector>
@@ -21,93 +22,96 @@
#include <QString>
#include <QVariant>
#include <QMutex>
-#include <QMutexLocker>
#include <QDebug>
#include "export.hpp"
-namespace options {
-
-namespace detail {
+namespace options::detail {
+ class bundle;
+ struct bundler;
+} // ns options::detail
-void set_base_value_to_default(base_value* val);
+namespace options {
+ using bundle_ = detail::bundle;
+ using bundle = std::shared_ptr<bundle_>;
+ OTR_OPTIONS_EXPORT std::shared_ptr<detail::bundle> make_bundle(const QString& name);
+} // ns options
-struct bundler;
+namespace options::detail {
class OTR_OPTIONS_EXPORT bundle final : public QObject, public connector
{
Q_OBJECT
- class OTR_OPTIONS_EXPORT mutex final : public QMutex
- {
- public:
- mutex(QMutex::RecursionMode mode) : QMutex(mode) {}
- operator QMutex*() const { return const_cast<QMutex*>(static_cast<const QMutex*>(this)); }
- };
+ friend struct bundler;
-private:
- mutex mtx;
+ mutable QMutex mtx { QMutex::Recursive };
const QString group_name;
group saved;
group transient;
- bundle(const bundle&) = delete;
- bundle(bundle&&) = delete;
- bundle& operator=(bundle&&) = delete;
- bundle& operator=(const bundle&) = delete;
- QMutex* get_mtx() const override;
+ void reload_no_notify();
signals:
void reloading();
void saving() const;
void changed() const;
+
public:
- never_inline bundle(const QString& group_name);
- never_inline ~bundle() override;
+ bundle(const bundle&) = delete;
+ bundle& operator=(const bundle&) = delete;
+
+ QMutex* get_mtx() const override { return &mtx; }
QString name() const { return group_name; }
- never_inline void store_kv(const QString& name, const QVariant& datum);
- never_inline bool contains(const QString& name) const;
- never_inline bool is_modified() const;
-
- template<typename t>
- t get(const QString& name) const
- {
- QMutexLocker l(mtx);
- return transient.get<t>(name);
- }
+
+ explicit bundle(const QString& group_name);
+ ~bundle() override;
+
+ void store_kv(const QString& name, QVariant&& datum);
+ void store_kv(const QString& name, const QVariant& datum);
+ bool contains(const QString& name) const;
+
+ QVariant get_variant(const QString& name) const;
+ void notify();
+
public slots:
void save();
void reload();
void set_all_to_default();
};
-OTR_OPTIONS_EXPORT bundler& singleton();
-
-struct OTR_OPTIONS_EXPORT bundler
+struct OTR_OPTIONS_EXPORT bundler final
{
-public:
using k = QString;
using v = bundle;
using weak = std::weak_ptr<v>;
using shared = std::shared_ptr<v>;
+
+ static void notify();
+ static void reload_no_notify();
+ static void reload();
+
private:
- QMutex implsgl_mtx;
- std::map<k, weak> implsgl_data;
- void after_profile_changed_();
-public:
+ QMutex implsgl_mtx { QMutex::Recursive };
+ std::unordered_map<k, weak> implsgl_data {};
+
+ void notify_();
+ void reload_no_notify_();
+
+ void reload_();
+
+ friend OTR_OPTIONS_EXPORT
+ std::shared_ptr<v> options::make_bundle(const QString& name);
+
+ std::shared_ptr<v> make_bundle_(const k& key);
+ static bundler& singleton();
+
bundler();
~bundler();
- std::shared_ptr<v> make_bundle(const k& key);
- static void refresh_all_bundles();
};
-OTR_OPTIONS_EXPORT bundler& singleton();
-} // ns options::detail
+void set_value_to_default(value_* val);
-using bundle_ = detail::bundle;
-using bundle = std::shared_ptr<bundle_>;
-
-OTR_OPTIONS_EXPORT std::shared_ptr<bundle_> make_bundle(const QString& name);
+} // ns options::detail
-} // ns options
diff --git a/options/connector.cpp b/options/connector.cpp
index 6031d9ed..e86958f7 100644
--- a/options/connector.cpp
+++ b/options/connector.cpp
@@ -9,132 +9,85 @@
#include "connector.hpp"
#include "value.hpp"
-#include <utility>
+namespace options::detail {
-namespace options {
-namespace detail {
+connector::connector() = default;
+connector::~connector() = default;
-static bool generic_is_equal(const QVariant& val1, const QVariant& val2)
-{
- return val1 == val2;
-}
-
-connector::~connector() {}
-
-bool connector::is_equal(const QString& name, const QVariant& val1, const QVariant& val2) const
-{
- QMutexLocker l(get_mtx());
-
- auto it = connected_values.find(name);
-
- if (it != connected_values.cend() && std::get<0>((*it).second).size() != 0)
- return std::get<1>((*it).second)(val1, val2);
- else
- return generic_is_equal(val1, val2);
-}
-
-bool connector::on_value_destructed_impl(const QString& name, value_type val)
+void connector::on_value_destructed(value_type val)
{
+ const QString& name = val->name();
QMutexLocker l(get_mtx());
- const bool ok = progn(
- auto it = connected_values.find(name);
+ const auto it = connected_values.find(name);
- if (it != connected_values.end())
+ if (it != connected_values.end())
+ {
+ value_vec& values = it->second;
+ for (auto it = values.begin(); it != values.end(); )
{
- std::vector<value_type>& values = std::get<0>((*it).second);
- for (auto it = values.begin(); it != values.end(); it++)
- {
- if (*it == val)
- {
- values.erase(it);
- return true;
- }
- }
+ if (*it == val)
+ it = values.erase(it);
+ else
+ it++;
}
- return false;
- );
+ }
- return ok;
+ if (it != connected_values.end() && it->second.empty())
+ connected_values.erase(it);
}
-
-
-void connector::on_value_destructed(const QString& name, value_type val)
+void connector::on_value_created(value_type val)
{
- if (!name.size())
- return;
+ const QString& name = val->name();
- const bool ok = on_value_destructed_impl(name, val);
-
- if (!ok)
- qWarning() << "options/connector: value destructed without creating;"
- << "bundle"
- << val->b->name()
- << "value-name" << name
- << "value-ptr" << quintptr(val);
-}
-
-void connector::on_value_created(const QString& name, value_type val)
-{
- if (!name.size())
+ if (name.isEmpty())
return;
QMutexLocker l(get_mtx());
- int i = 1;
- while (on_value_destructed_impl(name, val))
- {
- qWarning() << "options/connector: value created twice;"
- << "cnt" << i++
- << "bundle" << val->b->name()
- << "value-name" << name
- << "value-ptr" << quintptr(val);
- }
-
auto it = connected_values.find(name);
if (it != connected_values.end())
{
- tt& tmp = (*it).second;
- std::type_index& typeidx = std::get<2>(tmp);
- std::vector<value_type>& values = std::get<0>(tmp);
-
- if (typeidx != val->type_index)
- std::get<1>((*it).second) = generic_is_equal;
+ value_vec& values = it->second;
values.push_back(val);
}
else
{
- std::vector<value_type> vec;
+ value_vec vec;
+ vec.reserve(4);
vec.push_back(val);
- connected_values.emplace(name, tt(vec, val->cmp, val->type_index));
+ connected_values.emplace(name, vec);
}
}
void connector::notify_values(const QString& name) const
{
+ QMutexLocker l(get_mtx());
+
auto it = connected_values.find(name);
if (it != connected_values.cend())
- {
- for (value_type val : std::get<0>((*it).second))
- {
- val->bundle_value_changed();
- }
- }
+ for (value_type val : it->second)
+ val->notify();
}
void connector::notify_all_values() const
{
- for (auto& pair : connected_values)
- for (value_type val : std::get<0>(pair.second))
- val->bundle_value_changed();
+ QMutexLocker l(get_mtx());
+
+ for (const auto& [k, v] : connected_values)
+ for (value_type val : v)
+ val->notify();
}
-connector::connector()
+void connector::set_all_to_default_()
{
-}
+ QMutexLocker l(get_mtx());
+ for (auto& pair : connected_values)
+ for (auto& val : pair.second)
+ val->set_to_default();
}
-}
+} // ns options::detail
diff --git a/options/connector.hpp b/options/connector.hpp
index 5091ae67..025efda2 100644
--- a/options/connector.hpp
+++ b/options/connector.hpp
@@ -8,64 +8,42 @@
#pragma once
-#include <map>
+#include "compat/qhash.hpp"
+
+#include <unordered_map>
#include <vector>
-#include <tuple>
-#include <typeinfo>
-#include <typeindex>
-#include <QVariant>
+
#include <QString>
#include <QMutex>
-#include <QMutexLocker>
#include "export.hpp"
namespace options {
+ class value_;
+}
-class base_value;
-
-namespace detail {
+namespace options::detail {
class OTR_OPTIONS_EXPORT connector
{
- friend class ::options::base_value;
+ friend class ::options::value_;
- using value_type = base_value*;
+ using value_type = value_*;
using value_vec = std::vector<value_type>;
- using comparator = bool(*)(const QVariant&, const QVariant&);
- using tt = std::tuple<value_vec, comparator, std::type_index>;
- std::map<QString, tt> connected_values;
+ std::unordered_map<QString, value_vec> connected_values;
- void on_value_destructed(const QString& name, value_type val);
- void on_value_created(const QString& name, value_type val);
- bool on_value_destructed_impl(const QString& name, value_type val);
+ void on_value_destructed(value_type val);
+ void on_value_created(value_type val);
protected:
void notify_values(const QString& name) const;
void notify_all_values() const;
virtual QMutex* get_mtx() const = 0;
-
- template<typename F>
- void forall(F&& fun)
- {
- QMutexLocker l(get_mtx());
-
- for (auto& pair : connected_values)
- for (auto& val : std::get<0>(pair.second))
- fun(pair.first, val);
- }
+ void set_all_to_default_();
public:
connector();
virtual ~connector();
-
- bool is_equal(const QString& name, const QVariant& val1, const QVariant& val2) const;
-
- connector(const connector&) = default;
- connector& operator=(const connector&) = default;
- connector(connector&&) = default;
- connector& operator=(connector&&) = default;
};
-} // ns detail
-} // ns options
+} // ns options::detail
diff --git a/options/defs.hpp b/options/defs.hpp
index f093a22a..797a8fda 100644
--- a/options/defs.hpp
+++ b/options/defs.hpp
@@ -1,7 +1,4 @@
#pragma once
-#include <QString>
-
-#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename"
-#define OPENTRACK_DEFAULT_CONFIG "default.ini"
-#define OPENTRACK_DEFAULT_CONFIG_Q QStringLiteral("default.ini")
+#define OPENTRACK_PROFILE_FILENAME_KEY "settings-filename"
+#define OPENTRACK_DEFAULT_PROFILE "default.ini"
diff --git a/options/globals.cpp b/options/globals.cpp
new file mode 100644
index 00000000..39eb6014
--- /dev/null
+++ b/options/globals.cpp
@@ -0,0 +1,195 @@
+#include "globals.hpp"
+#include "compat/base-path.hpp"
+#include "defs.hpp"
+#include "opentrack-org.hxx"
+
+#include <QFile>
+#include <QFileInfo>
+#include <QDir>
+#include <QStandardPaths>
+#include <QDateTime>
+#include <QDebug>
+
+namespace options::globals::detail {
+
+ini_ctx::ini_ctx() = default;
+
+static bool is_portable_installation()
+{
+#if defined _WIN32
+ // must keep consistent between invocations
+ static const bool ret = QFile::exists(OPENTRACK_BASE_PATH + "/portable.txt");
+ return ret;
+#else
+ return false;
+#endif
+}
+
+saver_::~saver_()
+{
+ if (--ctx.refcount == 0 && ctx.modifiedp)
+ {
+ auto& settings = *ctx.qsettings;
+ settings.sync();
+ if (settings.status() != QSettings::NoError)
+ qDebug() << "error with .ini file" << settings.fileName() << settings.status();
+ ctx.modifiedp = false;
+ }
+ ctx.mtx.unlock();
+}
+
+saver_::saver_(ini_ctx& ini) : ctx { ini }
+{
+ ctx.refcount++;
+}
+
+ini_ctx& cur_settings()
+{
+ static ini_ctx ini;
+ const QString filename = ini_filename();
+
+ ini.mtx.lock();
+
+ if (ini.pathname != filename)
+ {
+ ini.qsettings.emplace(ini_combine(filename), QSettings::IniFormat);
+ ini.pathname = filename;
+ }
+
+ return ini;
+}
+
+ini_ctx& global_settings()
+{
+ static ini_ctx& ret = progn(
+ static ini_ctx ini;
+
+ if (!is_portable_installation())
+ // Windows registry or xdg on Linux
+ ini.qsettings.emplace(OPENTRACK_ORG);
+ else
+ // file in executable's directory
+ ini.qsettings.emplace(OPENTRACK_BASE_PATH + QStringLiteral("/globals.ini"),
+ QSettings::IniFormat);
+
+ ini.pathname = QStringLiteral(".");
+ return (ini_ctx&)ini;
+ );
+ ret.mtx.lock();
+ return ret;
+}
+
+void mark_ini_modified(bool value)
+{
+ auto& ini = cur_settings();
+ ini.modifiedp = value;
+ ini.mtx.unlock();
+}
+
+} // ns options::globals::detail
+
+namespace options::globals
+{
+
+using namespace detail;
+
+bool is_ini_modified()
+{
+ ini_ctx& ini = cur_settings();
+ bool ret = ini.modifiedp;
+ ini.mtx.unlock();
+ return ret;
+}
+
+QString ini_filename()
+{
+ return with_global_settings_object([&](QSettings& settings) {
+ static_assert(sizeof(OPENTRACK_DEFAULT_PROFILE) > 1);
+ static_assert(sizeof(OPENTRACK_PROFILE_FILENAME_KEY) > 1);
+
+ const QString ret = settings.value(QStringLiteral(OPENTRACK_PROFILE_FILENAME_KEY),
+ QStringLiteral(OPENTRACK_DEFAULT_PROFILE)).toString();
+ if (ret.isEmpty())
+ return QStringLiteral(OPENTRACK_DEFAULT_PROFILE);
+ return ret;
+ });
+}
+
+QString ini_pathname()
+{
+ return ini_combine(ini_filename());
+}
+
+QString ini_combine(const QString& filename)
+{
+ return QStringLiteral("%1/%2").arg(ini_directory(), filename);
+}
+
+QStringList ini_list()
+{
+ static QMutex mtx;
+ static QStringList list;
+ QMutexLocker l{&mtx};
+
+ const QString dirname = ini_directory();
+
+ {
+ static QDateTime last_time = {};
+ auto time = QFileInfo{dirname}.lastModified();
+ if (time == last_time)
+ return list;
+ last_time = time;
+ }
+
+ QDir settings_dir(dirname);
+
+ using f = QDir::Filter;
+ list = settings_dir.entryList({ QStringLiteral("*.ini") }, f::Files | f::Readable, QDir::Name);
+ std::sort(list.begin(), list.end());
+ return list;
+}
+
+void mark_global_ini_modified(bool value)
+{
+ auto& ini = global_settings();
+ ini.modifiedp = value;
+ ini.mtx.unlock();
+}
+
+static QString ini_directory_()
+{
+ if (detail::is_portable_installation())
+ {
+fail: constexpr const char* subdir = "ini";
+ QString dir = OPENTRACK_BASE_PATH;
+ if (dir.isEmpty())
+ dir = '.';
+ (void)QDir(dir).mkpath(subdir);
+ return QStringLiteral("%1/%2").arg(dir, subdir);
+ }
+ else
+ {
+ QString dir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0, QString());
+ if (dir.isEmpty())
+ goto fail;
+ const QString fmt = QStringLiteral("%1/%2");
+#if !defined _WIN32 && !defined __APPLE__
+ if (!QFile::exists(fmt.arg(dir, OPENTRACK_ORG)))
+ {
+ dir = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).value(0, QString());
+ if (dir.isEmpty())
+ goto fail;
+ }
+#endif
+ (void)QDir(dir).mkpath(OPENTRACK_ORG);
+ return fmt.arg(dir, OPENTRACK_ORG);
+ }
+}
+
+QString ini_directory()
+{
+ static const QString dir = ini_directory_();
+ return dir;
+}
+
+} // ns options::globals
diff --git a/options/globals.hpp b/options/globals.hpp
new file mode 100644
index 00000000..af242dc9
--- /dev/null
+++ b/options/globals.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "export.hpp"
+#include "compat/macros.h"
+
+#include <optional>
+
+#include <QString>
+#include <QSettings>
+#include <QMutex>
+
+namespace options::globals::detail {
+
+struct saver_;
+
+struct OTR_OPTIONS_EXPORT ini_ctx
+{
+ std::optional<QSettings> qsettings { std::in_place };
+ QString pathname;
+ QMutex mtx { QMutex::Recursive };
+
+ unsigned refcount = 0;
+ bool modifiedp = false;
+
+ ini_ctx();
+};
+
+struct OTR_OPTIONS_EXPORT saver_ final
+{
+ ini_ctx& ctx;
+
+ never_inline ~saver_();
+ explicit never_inline saver_(ini_ctx& ini);
+};
+
+template<typename F>
+never_inline
+auto with_settings_object_(ini_ctx& ini, F&& fun)
+{
+ saver_ saver { ini };
+
+ return fun(*ini.qsettings);
+}
+
+OTR_OPTIONS_EXPORT ini_ctx& cur_settings();
+OTR_OPTIONS_EXPORT ini_ctx& global_settings();
+
+OTR_OPTIONS_EXPORT void mark_ini_modified(bool value = true);
+
+} // ns options::globals::detail
+
+namespace options::globals
+{
+ OTR_OPTIONS_EXPORT void mark_global_ini_modified(bool value = true);
+ OTR_OPTIONS_EXPORT QString ini_directory();
+ OTR_OPTIONS_EXPORT QString ini_filename();
+ OTR_OPTIONS_EXPORT QString ini_pathname();
+ OTR_OPTIONS_EXPORT QString ini_combine(const QString& filename);
+ OTR_OPTIONS_EXPORT QStringList ini_list();
+
+ template<typename F>
+ auto with_settings_object(F&& fun)
+ {
+ using namespace detail;
+ return with_settings_object_(cur_settings(), fun);
+ }
+
+ template<typename F>
+ auto with_global_settings_object(F&& fun)
+ {
+ using namespace detail;
+ return with_settings_object_(global_settings(), fun);
+ }
+} // ns options::globals
diff --git a/options/group.cpp b/options/group.cpp
index 69378d51..d5829008 100644
--- a/options/group.cpp
+++ b/options/group.cpp
@@ -8,205 +8,75 @@
#include "group.hpp"
#include "defs.hpp"
+#include "globals.hpp"
-#include "compat/timer.hpp"
-#include "compat/library-path.hpp"
-
-#include <cmath>
+#include <utility>
+#include <algorithm>
#include <QFile>
-#include <QStandardPaths>
#include <QDir>
-#include <QDebug>
-namespace options {
+namespace options::detail {
+
+using namespace options::globals;
group::group(const QString& name) : name(name)
{
- if (name == "")
+ constexpr unsigned reserved_buckets = 64;
+ kvs.reserve(reserved_buckets);
+ kvs.max_load_factor(0.4375);
+
+ if (name.isEmpty())
return;
with_settings_object([&](QSettings& conf) {
conf.beginGroup(name);
- for (auto& k_ : conf.childKeys())
- {
- auto tmp = k_.toUtf8();
- QString k(tmp);
- QVariant val = conf.value(k_);
- if (val.type() != QVariant::Invalid)
- kvs[k] = std::move(val);
- }
+ for (auto const& k : conf.childKeys())
+ kvs[k] = conf.value(k);
conf.endGroup();
});
}
void group::save() const
{
- if (name == "")
+ if (name.isEmpty())
return;
with_settings_object([&](QSettings& s) {
s.beginGroup(name);
- for (auto& i : kvs)
+ for (auto const& i : kvs)
s.setValue(i.first, i.second);
s.endGroup();
-
- mark_ini_modified();
});
}
-void group::put(const QString &s, const QVariant &d)
-{
- kvs[s] = d;
-}
-
-bool group::contains(const QString &s) const
-{
- const auto it = kvs.find(s);
- return it != kvs.cend() && it->second != QVariant::Invalid;
-}
-
-bool group::is_portable_installation()
+void group::put(const QString& s, QVariant&& d)
{
-#if defined _WIN32
- if (QFile::exists(OPENTRACK_BASE_PATH + "/portable.txt"))
- return true;
-#endif
- return false;
-}
-
-QString group::ini_directory()
-{
-
- QString dir;
-
- if (is_portable_installation())
- {
- dir = OPENTRACK_BASE_PATH;
-
- static const QString subdir = "ini";
-
- if (!QDir(dir).mkpath(subdir))
- return QString();
-
- return dir + '/' + subdir;
- }
+ if (d.isNull())
+ kvs.erase(s);
else
- {
- dir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0, QString());
- if (dir.isEmpty())
- return QString();
- if (!QDir(dir).mkpath(OPENTRACK_ORG))
- return QString();
-
- dir += '/';
- dir += OPENTRACK_ORG;
- }
-
- return dir;
-}
-
-QString group::ini_filename()
-{
- return with_global_settings_object([&](QSettings& settings) {
- const QString ret = settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
- if (ret.size() == 0)
- return QStringLiteral(OPENTRACK_DEFAULT_CONFIG);
- return ret;
- });
+ kvs[s] = d;
}
-QString group::ini_pathname()
+void group::put(const QString& s, const QVariant& d)
{
- const auto dir = ini_directory();
- if (dir == "")
- return "";
- return dir + "/" + ini_filename();
+ put(s, QVariant{d});
}
-QString group::ini_combine(const QString& filename)
+bool group::contains(const QString& s) const
{
- return ini_directory() + QStringLiteral("/") + filename;
-}
-
-QStringList group::ini_list()
-{
- const auto dirname = ini_directory();
- if (dirname == "")
- return QStringList();
- QDir settings_dir(dirname);
- QStringList list = settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name );
- std::sort(list.begin(), list.end());
- return list;
-}
-
-void group::mark_ini_modified()
-{
- QMutexLocker l(&cur_ini_mtx);
- ini_modifiedp = true;
-}
-
-QString group::cur_ini_pathname;
-
-std::shared_ptr<QSettings> group::cur_ini;
-QMutex group::cur_ini_mtx(QMutex::Recursive);
-int group::ini_refcount = 0;
-bool group::ini_modifiedp = false;
-
-std::shared_ptr<QSettings> group::cur_global_ini;
-QMutex group::global_ini_mtx(QMutex::Recursive);
-int group::global_ini_refcount = 0;
-bool group::global_ini_modifiedp = false;
-
-std::shared_ptr<QSettings> group::cur_settings_object()
-{
- const QString pathname = ini_pathname();
-
- if (pathname.isEmpty())
- return std::make_shared<QSettings>();
-
- if (pathname != cur_ini_pathname)
- {
- cur_ini = std::make_shared<QSettings>(pathname, QSettings::IniFormat);
- cur_ini_pathname = pathname;
- }
-
- return cur_ini;
+ const auto it = kvs.find(s);
+ return it != kvs.cend();
}
-std::shared_ptr<QSettings> group::cur_global_settings_object()
+QVariant group::get_variant(const QString& name) const
{
- if (cur_global_ini)
- return cur_global_ini;
-
- if (!is_portable_installation())
- cur_global_ini = std::make_shared<QSettings>(OPENTRACK_ORG);
- else
- {
- static const QString pathname = OPENTRACK_BASE_PATH + QStringLiteral("/globals.ini");
- cur_global_ini = std::make_shared<QSettings>(pathname, QSettings::IniFormat);
- }
+ auto it = kvs.find(name);
+ if (it != kvs.cend())
+ return it->second;
- return cur_global_ini;
+ return {};
}
-never_inline
-group::saver_::~saver_()
-{
- if (--refcount == 0 && modifiedp)
- {
- modifiedp = false;
- s.sync();
- if (s.status() != QSettings::NoError)
- qDebug() << "error with .ini file" << s.fileName() << s.status();
- }
-}
-
-never_inline
-group::saver_::saver_(QSettings& s, int& refcount, bool& modifiedp) :
- s(s), refcount(refcount), modifiedp(modifiedp)
-{
- refcount++;
-}
+} // ns options::detail
-} // ns options
diff --git a/options/group.hpp b/options/group.hpp
index c933b134..11bab965 100644
--- a/options/group.hpp
+++ b/options/group.hpp
@@ -1,93 +1,39 @@
#pragma once
+#include "options/defs.hpp"
+
+#include "compat/base-path.hpp"
+#include "compat/library-path.hpp"
+#include "compat/macros.h"
+#include "compat/qhash.hpp"
#include "export.hpp"
-// included here to propagate into callers of options::group
-#include "metatype.hpp"
+#include <optional>
+#include <unordered_map>
-#include <map>
-#include <memory>
#include <QString>
-#include <QList>
#include <QVariant>
-#include <QSettings>
-#include <QMutex>
-
-namespace options {
-
-// snapshot of qsettings group at given time
-class OTR_OPTIONS_EXPORT group final
-{
- QString name;
-
- static QString cur_ini_pathname;
-
- static std::shared_ptr<QSettings> cur_ini;
- static int ini_refcount;
- static bool ini_modifiedp;
- static QMutex cur_ini_mtx;
-
- static std::shared_ptr<QSettings> cur_global_ini;
- static int global_ini_refcount;
- static bool global_ini_modifiedp;
- static QMutex global_ini_mtx;
-
- struct OTR_OPTIONS_EXPORT saver_ final
- {
- QSettings& s;
- int& refcount;
- bool& modifiedp;
-
- never_inline ~saver_();
- never_inline saver_(QSettings& s, int& refcount, bool& modifiedp);
- };
- static std::shared_ptr<QSettings> cur_settings_object();
- static std::shared_ptr<QSettings> cur_global_settings_object();
-public:
- std::map<QString, QVariant> kvs;
- group(const QString& name);
- void save() const;
- void put(const QString& s, const QVariant& d);
- bool contains(const QString& s) const;
- static QString ini_directory();
- static QString ini_filename();
- static QString ini_pathname();
- static QString ini_combine(const QString& filename);
- static QStringList ini_list();
- static bool is_portable_installation();
+#include <QDebug>
- static void mark_ini_modified();
+// XXX TODO remove qsettings usage -sh 20180624
- template<typename t>
- never_inline
- t get(const QString& k) const
+namespace options::detail {
+ // snapshot of qsettings group at given time
+ class OTR_OPTIONS_EXPORT group final
{
- auto value = kvs.find(k);
- if (value != kvs.cend())
- return value->second.value<t>();
- return t();
- }
+ QString name;
- template<typename F>
- never_inline
- static auto with_settings_object(F&& fun)
- {
- QMutexLocker l(&cur_ini_mtx);
- saver_ saver { *cur_settings_object(), ini_refcount, ini_modifiedp };
+ public:
+ std::unordered_map<QString, QVariant> kvs;
+ explicit group(const QString& name);
+ void save() const;
+ void put(const QString& s, const QVariant& d);
+ void put(const QString& s, QVariant&& d);
+ bool contains(const QString& s) const;
- return fun(saver.s);
- }
-
- template<typename F>
- static auto with_global_settings_object(F&& fun)
- {
- QMutexLocker l(&global_ini_mtx);
- saver_ saver { *cur_global_settings_object(), global_ini_refcount, global_ini_modifiedp };
- global_ini_modifiedp = true;
+ never_inline QVariant get_variant(const QString& name) const;
+ };
+} // ns options::detail
- return fun(saver.s);
- }
-};
-}
diff --git a/options/lang/de_DE.ts b/options/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/options/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/options/lang/zh_CN.ts b/options/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/options/lang/zh_CN.ts
+++ b/options/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/options/metatype.cpp b/options/metatype.cpp
index ed12821b..7430c00a 100644
--- a/options/metatype.cpp
+++ b/options/metatype.cpp
@@ -1,22 +1,26 @@
-#include "metatype.hpp"
-
-#define OPENTRACK_REGISTER_METATYPE(t) ::options::detail::custom_type_initializer::declare_for_type<t>(#t)
+#include <QMetaType>
-namespace options {
-namespace detail {
+namespace options::detail {
-custom_type_initializer::custom_type_initializer()
+template<typename t>
+void declare_metatype_for_type(const char* str)
{
- OPENTRACK_REGISTER_METATYPE(options::slider_value);
- OPENTRACK_REGISTER_METATYPE(QList<double>);
- OPENTRACK_REGISTER_METATYPE(QList<float>);
- OPENTRACK_REGISTER_METATYPE(QList<int>);
- OPENTRACK_REGISTER_METATYPE(QList<bool>);
- OPENTRACK_REGISTER_METATYPE(QList<QString>);
- OPENTRACK_REGISTER_METATYPE(QList<QPointF>);
+ qRegisterMetaType<t>(str);
+ qRegisterMetaTypeStreamOperators<t>();
}
-const custom_type_initializer custom_type_initializer::singleton;
+} // ns options::detail
-}
-}
+#define OPENTRACK_DEFINE_METATYPE3(t, ctr) \
+ static \
+ const char init_##ctr = /* NOLINT(misc-definitions-in-headers) */ \
+ (::options::detail::declare_metatype_for_type<t>(#t), 0); \
+
+#define OPENTRACK_DEFINE_METATYPE2(t, ctr) \
+ OPENTRACK_DEFINE_METATYPE3(t, ctr)
+
+#define OPENTRACK_DEFINE_METATYPE(t) \
+ OPENTRACK_DEFINE_METATYPE2(t, __COUNTER__)
+
+#define OPENTRACK_METATYPE_(x) OPENTRACK_DEFINE_METATYPE(x)
+#include "metatype.hpp"
diff --git a/options/metatype.hpp b/options/metatype.hpp
index c889766a..ca0947fb 100644
--- a/options/metatype.hpp
+++ b/options/metatype.hpp
@@ -1,38 +1,25 @@
#pragma once
-#include <QMetaType>
+#include "slider.hpp"
+#include "defs.hpp"
+
#include <QList>
#include <QString>
#include <QPointF>
-#include <QDataStream>
#include <QDebug>
-#include "export.hpp"
-#include "slider.hpp"
-
-Q_DECLARE_METATYPE(QList<double>)
-Q_DECLARE_METATYPE(QList<float>)
-Q_DECLARE_METATYPE(QList<int>)
-Q_DECLARE_METATYPE(QList<bool>)
-Q_DECLARE_METATYPE(QList<QString>)
-Q_DECLARE_METATYPE(QList<QPointF>)
-Q_DECLARE_METATYPE(::options::slider_value)
-
-namespace options {
-namespace detail {
-
-struct custom_type_initializer final
-{
- static const custom_type_initializer singleton;
-
- custom_type_initializer();
-
- template<typename t> static void declare_for_type(const char* str)
- {
- qRegisterMetaType<t>(str);
- qRegisterMetaTypeStreamOperators<t>();
- }
-};
+#include <QMetaType>
-}
-}
+#if !defined OPENTRACK_METATYPE_
+# define OPENTRACK_METATYPE(x) Q_DECLARE_METATYPE(x)
+#else
+# define OPENTRACK_METATYPE(x) Q_DECLARE_METATYPE(x) OPENTRACK_METATYPE_(x)
+#endif
+
+OPENTRACK_METATYPE(QList<double>)
+OPENTRACK_METATYPE(QList<float>)
+OPENTRACK_METATYPE(QList<int>)
+OPENTRACK_METATYPE(QList<bool>)
+OPENTRACK_METATYPE(QList<QString>)
+OPENTRACK_METATYPE(QList<QPointF>)
+OPENTRACK_METATYPE(::options::slider_value)
diff --git a/options/options.hpp b/options/options.hpp
index bc892120..a99d5c59 100644
--- a/options/options.hpp
+++ b/options/options.hpp
@@ -8,9 +8,8 @@
#include "defs.hpp"
#include "bundle.hpp"
-#include "group.hpp"
#include "slider.hpp"
#include "value.hpp"
#include "tie.hpp"
-#include "metatype.hpp"
#include "scoped.hpp"
+#include "globals.hpp"
diff --git a/options/scoped.cpp b/options/scoped.cpp
index 2974dfdb..33db7907 100644
--- a/options/scoped.cpp
+++ b/options/scoped.cpp
@@ -1,4 +1,5 @@
#include "scoped.hpp"
+#include "compat/run-in-thread.hpp"
#include <QApplication>
#include <QThread>
@@ -10,27 +11,27 @@
namespace options {
// XXX hack: the flag shouldn't be here as action at distance -sh 20160926
-static std::atomic_bool teardown_flag(false);
-static void set_teardown_flag(bool value);
+static std::atomic<bool> teardown_flag = false;
+[[nodiscard]] static bool set_teardown_flag(bool value);
static void ensure_thread();
static bool is_tracker_teardown();
-static void set_teardown_flag(bool value)
+static bool set_teardown_flag(bool value)
{
// flag being set means "opts" is about to go out of scope due to tracker stop
// in this case we don't reload options. we only want to reload when cancel is pressed.
ensure_thread();
- teardown_flag = value;
+ return teardown_flag.exchange(value);
}
static void ensure_thread()
{
// only as a bug check
- if (qApp == nullptr)
+ if (qApp == nullptr) // NOLINT
abort();
- const QThread* ui_thread = qApp->thread();
+ const QThread* ui_thread = qApp->thread(); // NOLINT
const QThread* curthread = QThread::currentThread();
if (ui_thread == nullptr)
@@ -47,8 +48,14 @@ static bool is_tracker_teardown()
opts::~opts()
{
- if (!is_tracker_teardown())
+ // XXX TODO debug crash with ps3eye dialog in pt tracker -sh 20211019
+ if (!is_tracker_teardown() && raii)
+#if 1
+ run_in_thread_sync(qApp->thread(), [this]{ b->reload(); });
+#else
+ assert(b);
b->reload();
+#endif
#if 0
else
qDebug() << "in teardown, not reloading" << b->name();
@@ -59,14 +66,13 @@ opts::opts(const QString &name) : b(make_bundle(name))
{
}
-with_tracker_teardown::with_tracker_teardown() : old_value(teardown_flag)
+with_tracker_teardown::with_tracker_teardown() : old_value{set_teardown_flag(true)}
{
- set_teardown_flag(true);
}
with_tracker_teardown::~with_tracker_teardown()
{
- set_teardown_flag(old_value);
+ (void)set_teardown_flag(old_value);
}
} // ns options
diff --git a/options/scoped.hpp b/options/scoped.hpp
index 49c3d18f..81e6bd19 100644
--- a/options/scoped.hpp
+++ b/options/scoped.hpp
@@ -6,8 +6,6 @@
#include "export.hpp"
-#include <atomic>
-
namespace options {
struct OTR_OPTIONS_EXPORT with_tracker_teardown final
@@ -26,11 +24,16 @@ struct OTR_OPTIONS_EXPORT opts
bundle b;
- virtual ~opts();
- opts(const QString& name);
-
opts& operator=(const opts&) = delete;
opts(const opts&) = delete;
+
+ void set_raii_dtor_state(bool x) { raii = x; }
+
+protected:
+ explicit opts(const QString& name);
+ ~opts();
+private:
+ bool raii = true;
};
}
diff --git a/options/slider.cpp b/options/slider.cpp
index 33000ee9..76d1dddc 100644
--- a/options/slider.cpp
+++ b/options/slider.cpp
@@ -24,39 +24,24 @@ slider_value::slider_value(double cur, double min, double max) :
cur_ = min_;
}
-slider_value::slider_value(const slider_value& v) : slider_value(v.cur(), v.min(), v.max())
-{
-}
-
-slider_value::slider_value() : slider_value(0, 0, 0)
-{
-}
-
-slider_value& slider_value::operator=(const slider_value& v)
-{
- cur_ = v.cur_;
-
- min_ = v.min_;
- max_ = v.max_;
-
- return *this;
-}
-
bool slider_value::operator==(const slider_value& v) const
{
- using std::fabs;
-
constexpr double eps = 2e-3;
#if 1
- return (fabs(v.cur_ - cur_) < eps &&
- fabs(v.min_ - min_) < eps &&
- fabs(v.max_ - max_) < eps);
+ return (std::fabs(v.cur_ - cur_) < eps &&
+ std::fabs(v.min_ - min_) < eps &&
+ std::fabs(v.max_ - max_) < eps);
#else
- return (fabs(v.cur_ - cur_) < eps);
+ return (std::fabs(v.cur_ - cur_) < eps);
#endif
}
+bool slider_value::operator!=(const slider_value& v) const
+{
+ return !(*this == v);
+}
+
slider_value slider_value::update_from_slider(int pos, int q_min, int q_max) const
{
slider_value v(*this);
@@ -67,11 +52,11 @@ slider_value slider_value::update_from_slider(int pos, int q_min, int q_max) con
: (((pos - q_min) * (v.max() - v.min())) / q_diff + v.min());
if (sv_pos < v.min())
- v = slider_value(v.min(), v.min(), v.max());
+ v = { v.min(), v.min(), v.max() };
else if (sv_pos > v.max())
- v = slider_value(v.max(), v.min(), v.max());
+ v = { v.max(), v.min(), v.max() };
else
- v = slider_value(sv_pos, v.min(), v.max());
+ v = { sv_pos, v.min(), v.max() };
return v;
}
@@ -86,11 +71,11 @@ int slider_value::to_slider_pos(int q_min, int q_max) const
return int(std::round(((cur() - min()) * q_diff / div) + q_min));
}
-} // end ns options
+} // ns options
-QT_BEGIN_NAMESPACE
+using options::slider_value;
-QDataStream& operator << (QDataStream& out, const options::slider_value& v)
+QDataStream& operator << (QDataStream& out, const slider_value& v)
{
out << v.cur()
<< v.min()
@@ -98,17 +83,16 @@ QDataStream& operator << (QDataStream& out, const options::slider_value& v)
return out;
}
-QDebug operator << (QDebug dbg, const options::slider_value& val)
+QDebug operator << (QDebug dbg, const slider_value& v)
{
- return dbg << val.cur();
+ return dbg << QStringLiteral("slider_value(cur=%1, min=%2, max=%3)")
+ .arg(v.cur()).arg(v.min()).arg(v.max()).toUtf8().constData();
}
-QDataStream& operator >> (QDataStream& in, options::slider_value& v)
+QDataStream& operator >> (QDataStream& in, slider_value& v)
{
double cur = 0, min = 0, max = 0;
in >> cur >> min >> max;
- v = options::slider_value(cur, min, max);
+ v = slider_value(cur, min, max);
return in;
}
-
-QT_END_NAMESPACE
diff --git a/options/slider.hpp b/options/slider.hpp
index f3ba44f8..1e721ae0 100644
--- a/options/slider.hpp
+++ b/options/slider.hpp
@@ -8,10 +8,11 @@
#pragma once
#include "export.hpp"
-#include "compat/macros.hpp"
-#include <QMetaType>
+#include <type_traits>
+
#include <QDataStream>
+#include <QMetaType>
#include <QDebug>
namespace options
@@ -20,42 +21,28 @@ namespace options
{
double cur_, min_, max_;
- template<typename t>
- using arith_conversion_t = std::enable_if_t<std::is_arithmetic_v<std::decay_t<t>>, std::decay_t<t>>;
public:
slider_value(double cur, double min, double max);
- template<typename t, typename u, typename v> slider_value(t cur, u min, v max) :
- cur_(double(cur)),
- min_(double(min)),
- max_(double(max))
- {}
-
- template<typename t>
- never_inline
- explicit operator arith_conversion_t<t>() const
- {
- return t(cur_);
- }
-
- slider_value(const slider_value& v);
- slider_value();
- slider_value& operator=(const slider_value& v);
+ template<typename t, typename u, typename v>
+ slider_value(t cur, u min, v max) :
+ slider_value((double)cur, (double)min, (double)max) {}
+
+ slider_value& operator=(const slider_value& v) = default;
+ slider_value(const slider_value& v) = default;
+ slider_value() : slider_value{0, 0, 0} {}
+
bool operator==(const slider_value& v) const;
- operator double() const { return cur_; }
- double cur() const { return cur_; }
- double min() const { return min_; }
- double max() const { return max_; }
+ bool operator!=(const slider_value& v) const;
+ constexpr operator double() const { return cur_; }
+ constexpr double cur() const { return cur_; }
+ constexpr double min() const { return min_; }
+ constexpr double max() const { return max_; }
slider_value update_from_slider(int pos, int q_min, int q_max) const;
int to_slider_pos(int q_min, int q_max) const;
};
}
-QT_BEGIN_NAMESPACE
-
-QDebug operator << (QDebug dbg, const options::slider_value& val);
-QDataStream& operator << (QDataStream& out, const options::slider_value& v);
-QDataStream& operator >> (QDataStream& in, options::slider_value& v);
-
-QT_END_NAMESPACE
-
+OTR_OPTIONS_EXPORT QDebug operator << (QDebug dbg, const options::slider_value& val);
+OTR_OPTIONS_EXPORT QDataStream& operator << (QDataStream& out, const options::slider_value& v);
+OTR_OPTIONS_EXPORT QDataStream& operator >> (QDataStream& in, options::slider_value& v);
diff --git a/options/tie.cpp b/options/tie.cpp
index baa8bb82..adf26b53 100644
--- a/options/tie.cpp
+++ b/options/tie.cpp
@@ -8,39 +8,45 @@
#include "tie.hpp"
#include "compat/run-in-thread.hpp"
+#include "compat/macros.h"
+
+#include "value-traits.hpp"
+
+#include <cmath>
namespace options {
-OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QComboBox* cb)
+void tie_setting(value<int>& v, QComboBox* cb)
{
cb->setCurrentIndex(v);
v = cb->currentIndex();
- base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
+ value_::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QComboBox* cb)
+void tie_setting(value<QString>& v, QComboBox* cb)
{
cb->setCurrentText(v);
v = cb->currentText();
- base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(const QString&)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(const QString&)), cb, SLOT(setCurrentText(const QString&)), v.SAFE_CONNTYPE);
+ auto set_current_text = [cb, &v](const QString& str) {
+ cb->setCurrentText(str);
+ v = cb->currentText();
+ };
+ value_::connect(cb, &QComboBox::currentTextChanged, &v, v.value_changed<QString>(), v.DIRECT_CONNTYPE);
+ value_::connect(&v, v.value_changed<QString>(), cb, set_current_text, v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb)
+void tie_setting(value<QVariant>& v, QComboBox* cb)
{
auto set_idx = [cb](const QVariant& var) {
const int sz = cb->count();
int idx = -1;
for (int k = 0; k < sz; k++)
- {
- if (cb->itemData(k) == var)
- {
+ if (cb->itemData(k) == var) {
idx = k;
break;
}
- }
cb->setCurrentIndex(idx);
return idx;
};
@@ -50,65 +56,65 @@ OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb)
if (idx != -1)
v = cb->itemData(idx);
else
- v = QVariant(QVariant::Invalid);
-
- base_value::connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- &v, [cb, &v](int idx) {
- v = cb->itemData(idx);
- }, v.DIRECT_CONNTYPE);
- base_value::connect(&v, base_value::value_changed<QVariant>(),
- cb,
- [cb, set_idx](const QVariant& var) {
- run_in_thread_sync(cb, [&]() {
- set_idx(var);
- });
- }, v.DIRECT_CONNTYPE);
+ v = {};
+
+ value_::connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ &v, [cb, &v](int idx) { v = cb->itemData(idx); },
+ v.DIRECT_CONNTYPE);
+ value_::connect(&v, value_::value_changed<QVariant>(),
+ cb, [set_idx](const QVariant& var) { set_idx(var); },
+ v.SAFE_CONNTYPE);
}
-// XXX TODO need variant with setEnabled based on lambda retval -- sh 20170524
+void tie_setting(value<bool>& v, QRadioButton* cb)
+{
+ cb->setChecked(v);
+ value_::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
+}
-OTR_OPTIONS_EXPORT void tie_setting(value<bool>& v, QCheckBox* cb)
+void tie_setting(value<bool>& v, QCheckBox* cb)
{
cb->setChecked(v);
- base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
+ value_::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
+void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
{
dsb->setValue(v);
- base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE);
+ value_::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QSpinBox* sb)
+void tie_setting(value<int>& v, QSpinBox* sb)
{
sb->setValue(v);
- base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+ value_::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QLineEdit* le)
+void tie_setting(value<QString>& v, QLineEdit* le)
{
le->setText(v);
- base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, base_value::value_changed<QString>(), le, &QLineEdit::setText, v.SAFE_CONNTYPE);
+ value_::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, value_::value_changed<QString>(), le, &QLineEdit::setText, v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QLabel* lb)
+void tie_setting(value<QString>& v, QLabel* lb)
{
lb->setText(v);
- base_value::connect(&v, base_value::value_changed<QString>(), lb, &QLabel::setText, v.SAFE_CONNTYPE);
+ value_::connect(&v, value_::value_changed<QString>(), lb, &QLabel::setText, v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QTabWidget* t)
+void tie_setting(value<int>& v, QTabWidget* t)
{
t->setCurrentIndex(v);
- base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
+ value_::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ value_::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
}
-OTR_OPTIONS_EXPORT void tie_setting(value<slider_value>& v, QSlider* w)
+void tie_setting(value<slider_value>& v, QSlider* w)
{
{
const int q_min = w->minimum();
@@ -118,10 +124,7 @@ OTR_OPTIONS_EXPORT void tie_setting(value<slider_value>& v, QSlider* w)
v = v().update_from_slider(w->value(), q_min, q_max);
}
- base_value::connect(w,
- &QSlider::valueChanged,
- &v,
- [=, &v](int pos)
+ value_::connect(w, &QSlider::valueChanged, &v, [=, &v](int pos)
{
run_in_thread_sync(w, [&]()
{
@@ -133,17 +136,17 @@ OTR_OPTIONS_EXPORT void tie_setting(value<slider_value>& v, QSlider* w)
},
v.DIRECT_CONNTYPE);
- base_value::connect(&v,
- base_value::value_changed<slider_value>(),
- w,
- [=, &v](double) {
+ value_::connect(&v,
+ value_::value_changed<slider_value>(),
+ w,
+ [=, &v](double) {
run_in_thread_sync(w, [=, &v]()
{
const int q_min = w->minimum();
const int q_max = w->maximum();
- const int pos = v().to_slider_pos(q_min, q_max);
+ const int pos = v->to_slider_pos(q_min, q_max);
+ v = v->update_from_slider(pos, q_min, q_max);
w->setValue(pos);
- v = v().update_from_slider(w->value(), q_min, q_max);
});
},
v.DIRECT_CONNTYPE);
diff --git a/options/tie.hpp b/options/tie.hpp
index 4ebc1846..194a3a5d 100644
--- a/options/tie.hpp
+++ b/options/tie.hpp
@@ -20,6 +20,7 @@
#include <QLineEdit>
#include <QLabel>
#include <QTabWidget>
+#include <QRadioButton>
#include <cmath>
@@ -31,39 +32,43 @@
namespace options {
template<typename t>
-std::enable_if_t<std::is_enum<t>::value>
-tie_setting(value<t>& v, QComboBox* cb)
+std::enable_if_t<std::is_enum_v<t>> tie_setting(value<t>& v, QComboBox* cb)
{
cb->setCurrentIndex(cb->findData(int(static_cast<t>(v))));
v = static_cast<t>(cb->currentData().toInt());
- base_value::connect(cb,
- static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- &v,
- [&v, cb](int idx)
- {
- run_in_thread_sync(cb,
- [&]() {
- v = static_cast<t>(cb->itemData(idx).toInt());
- });
- },
- v.DIRECT_CONNTYPE);
- base_value::connect(&v, base_value::value_changed<int>(),
- cb, [cb](int x) {
- run_in_thread_sync(cb, [&]() { cb->setCurrentIndex(cb->findData(x)); });
- },
- v.DIRECT_CONNTYPE);
+ value_::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ &v, [&v, cb](int idx) { v = static_cast<t>(cb->itemData(idx).toInt()); },
+ v.DIRECT_CONNTYPE);
+
+ value_::connect(&v, value_::value_changed<int>(),
+ cb, [cb](int x) { cb->setCurrentIndex(cb->findData(x)); },
+ v.SAFE_CONNTYPE);
+}
+
+template<typename t, typename From, typename To>
+void tie_setting(value<t>& v, QComboBox* cb, From&& fn_to_index, To&& fn_to_value)
+{
+ cb->setCurrentIndex(fn_to_index(v));
+ v = fn_to_value(cb->currentIndex(), cb->currentData());
+
+ value_::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ &v, [&v, cb, fn_to_value](int idx) { v = fn_to_value(idx, cb->currentData()); },
+ v.DIRECT_CONNTYPE);
+ value_::connect(&v, value_::value_changed<t>(),
+ cb, [cb, fn_to_index](detail::cv_qualified<t>& v) { cb->setCurrentIndex(fn_to_index(v)); },
+ v.SAFE_CONNTYPE);
}
template<typename t, typename F>
void tie_setting(value<t>& v, QLabel* lb, F&& fun)
{
- auto closure = [=](cv_qualified<t> x) { lb->setText(fun(x)); };
+ auto closure = [lb, fun](detail::cv_qualified<t> v) { lb->setText(fun(v)); };
closure(v());
- base_value::connect(&v, base_value::value_changed<t>(),
- lb, closure,
- v.SAFE_CONNTYPE);
+ value_::connect(&v, value_::value_changed<t>(),
+ lb, closure,
+ v.SAFE_CONNTYPE);
}
template<typename t, typename F>
@@ -74,15 +79,16 @@ void tie_setting(value<t>& v, QObject* obj, F&& fun)
fun(v());
- base_value::connect(&v, base_value::value_changed<t>(),
- obj, fun,
- v.DIRECT_CONNTYPE);
+ value_::connect(&v, value_::value_changed<t>(),
+ obj, fun,
+ v.DIRECT_CONNTYPE);
}
OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QComboBox* cb);
OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QComboBox* cb);
OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb);
OTR_OPTIONS_EXPORT void tie_setting(value<bool>& v, QCheckBox* cb);
+OTR_OPTIONS_EXPORT void tie_setting(value<bool>& v, QRadioButton* cb);
OTR_OPTIONS_EXPORT void tie_setting(value<double>& v, QDoubleSpinBox* dsb);
OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QSpinBox* sb);
OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QLineEdit* le);
diff --git a/options/value-traits.hpp b/options/value-traits.hpp
index ac5f6e5c..145cd924 100644
--- a/options/value-traits.hpp
+++ b/options/value-traits.hpp
@@ -1,49 +1,122 @@
-#include "export.hpp"
+#pragma once
#include "slider.hpp"
+#include "export.hpp"
-#include <QString>
-
+#include <cmath>
#include <type_traits>
-namespace options {
-namespace detail {
+#include <QString>
+
+namespace options::detail {
+template<typename t>
+using cv_qualified =
+ std::conditional_t<std::is_fundamental_v<std::remove_cvref_t<t>>,
+ std::remove_cvref_t<t>,
+ std::add_lvalue_reference_t<std::add_const_t<std::remove_cvref_t<t>>>>;
template<typename t, typename u = t, typename Enable = void>
struct default_value_traits
{
- using element_type = std::decay_t<t>;
- using value_type = std::decay_t<u>;
+ using value_type = t;
+ using stored_type = u;
+
+ static inline
+ value_type value_with_default(cv_qualified<value_type> val, cv_qualified<value_type>)
+ {
+ return val;
+ }
+
+ static inline
+ value_type value_from_storage(cv_qualified<stored_type> x)
+ {
+ return static_cast<value_type>(x);
+ }
+
+ static inline
+ stored_type storage_from_value(cv_qualified<value_type> val)
+ {
+ return static_cast<stored_type>(val);
+ }
+
+ static inline
+ value_type value_from_qvariant(const QVariant& x)
+ {
+ return value_from_storage(storage_from_qvariant(x));
+ }
+
+ static inline
+ QVariant qvariant_from_value(cv_qualified<value_type> val)
+ {
+ return qvariant_from_storage(storage_from_value(val));
+ }
+
+ static constexpr inline
+ value_type pass_value(cv_qualified<value_type> x)
+ {
+ if constexpr(std::is_same_v<value_type, stored_type>)
+ return x;
+ else
+ return value_from_storage(storage_from_value(x));
+ }
- static inline value_type from_value(const value_type& val, const value_type&) { return val; }
- static inline value_type from_storage(const element_type& x) { return static_cast<value_type>(x); }
- static inline element_type to_storage(const value_type& val) { return static_cast<element_type>(val); }
+ static inline
+ stored_type storage_from_qvariant(const QVariant& x)
+ {
+ // XXX TODO
+ return x.value<stored_type>();
+ }
+
+ static inline
+ QVariant qvariant_from_storage(cv_qualified<stored_type> val)
+ {
+ // XXX TODO
+ return QVariant::fromValue<stored_type>(val);
+ }
+
+ static inline
+ bool is_equal(cv_qualified<value_type> x, cv_qualified<value_type> y)
+ {
+ return x == y;
+ }
};
-template<typename t, typename u = t, typename Enable = void>
-struct value_traits : default_value_traits<t, u, Enable>
+template<typename t, typename Enable = void>
+struct value_traits : default_value_traits<t> {};
+
+template<>
+inline
+bool default_value_traits<double>::is_equal(double x, double y)
{
-};
+ return std::fabs(x - y) < 1e-6;
+}
+
+template<> struct value_traits<float, double> : default_value_traits<float> {};
template<>
-struct value_traits<slider_value> : default_value_traits<slider_value>
+inline
+bool default_value_traits<float, double>::is_equal(float x, float y)
{
- static inline slider_value from_value(const slider_value& val, const slider_value& def)
- {
- return slider_value(val.cur(), def.min(), def.max());
- }
-};
+ return std::fabs(x - y) < 1e-6f;
+}
-// Qt uses int a lot in slots so use it for all enums
-template<typename t>
-struct value_traits<t, t, std::enable_if_t<std::is_enum_v<t>>> : public default_value_traits<int, t>
+template<>
+inline
+slider_value default_value_traits<slider_value>::value_with_default(cv_qualified<slider_value> val, cv_qualified<slider_value> def)
{
-};
+ return { val.cur(), def.min(), def.max() };
+}
template<>
-struct value_traits<float> : public default_value_traits<float, double, void>
+inline
+bool default_value_traits<slider_value>::is_equal(cv_qualified<slider_value> x, cv_qualified<slider_value> y)
{
-};
+ return value_traits<double>::is_equal(x.cur(), y.cur());
+}
+
+// Qt uses int a lot in slots so use it for all enums
+template<typename t>
+struct value_traits<t, std::enable_if_t<std::is_enum_v<t>>> : default_value_traits<t, int> {};
+
+} // ns options::detail
-} // ns detail
-} // ns options
diff --git a/options/value.cpp b/options/value.cpp
index 5796dcd6..81a094e6 100644
--- a/options/value.cpp
+++ b/options/value.cpp
@@ -1,5 +1,5 @@
// instantiate the "template class" value<t> symbols
-#define OTR_OPT_VALUE OTR_TEMPLATE_EXPORT
+#define OTR_OPTIONS_INST_VALUE OTR_TEMPLATE_EXPORT
#include "value.hpp"
diff --git a/options/value.hpp b/options/value.hpp
index c8fb02ca..9a7487b8 100644
--- a/options/value.hpp
+++ b/options/value.hpp
@@ -14,157 +14,208 @@
#include "slider.hpp"
#include "base-value.hpp"
#include "value-traits.hpp"
-#include "compat/macros.hpp"
-#include <cstdio>
#include <type_traits>
-#include <typeinfo>
-#include <typeindex>
#include <utility>
-#include <QVariant>
-#include <QString>
-#include <QPointF>
-#include <QList>
-#include <QMutex>
+#include <QMetaType>
+
+namespace options::detail {
+ template<typename t>
+ class dereference_wrapper final
+ {
+ t x;
+ public:
+ constexpr t const* operator->() const { return &x; }
+ constexpr t* operator->() { return &x; }
+ constexpr explicit dereference_wrapper(t&& x) : x(x) {}
+ };
+} // ns options::detail
namespace options {
template<typename t>
-class value final : public base_value
+class value final : public value_
{
- using traits = detail::value_traits<t, t, void>;
- using element_type = typename traits::element_type;
+ static_assert(std::is_same_v<t, std::remove_cvref_t<t>>);
+ mutable QMutex mtx;
+ const t def;
+ mutable t cached_value;
+ using traits = detail::value_traits<t>;
- static bool is_equal(const QVariant& val1, const QVariant& val2)
+ never_inline
+ auto get() const noexcept
{
- return val1.value<element_type>() == val2.value<element_type>();
+ if (!b->contains(self_name))
+ return traits::pass_value(def);
+
+ QVariant variant = b->get_variant(self_name);
+
+ if (variant.isNull() || !variant.isValid())
+ return traits::pass_value(def);
+
+ return traits::pass_value(traits::value_with_default(traits::value_from_qvariant(variant), def));
}
never_inline
- t get() const
+ void store_variant(QVariant&& value) noexcept override
{
- if (self_name.isEmpty())
- return def;
+ if (traits::is_equal(get(), traits::value_from_qvariant(value)))
+ return;
- QVariant variant = b->get<QVariant>(self_name);
+ if (is_null())
+ return;
- if (!b->contains(self_name) || variant.type() == QVariant::Invalid)
- return def;
+ if (value.isValid() && !value.isNull())
+ b->store_kv(self_name, value);
+ else
+ b->store_kv(self_name, traits::qvariant_from_value(def));
+ }
- const element_type x(variant.value<element_type>());
+ never_inline
+ void store_variant(const QVariant& value) noexcept override
+ {
+ QVariant copy{value};
+ store_variant(std::move(copy));
+ }
- return traits::from_value(traits::from_storage(x), def);
+ bool is_null() const
+ {
+ return self_name.isEmpty() || b->name().isEmpty();
}
public:
- never_inline
- t operator=(const t& datum)
+ QVariant get_variant() const noexcept override
{
- if (self_name.isEmpty())
- return def;
-
- if (datum != get())
- store(traits::to_storage(datum));
+ if (QVariant ret{b->get_variant(self_name)}; ret.isValid() && !ret.isNull())
+ return ret;
- return datum;
+ return traits::qvariant_from_value(def);
}
- static constexpr inline Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection;
- static constexpr inline Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
+ never_inline
+ void notify_() const override
+ {
+ auto x = get();
+ {
+ QMutexLocker l(&mtx);
+ cached_value = x;
+ }
+ maybe_trace("notify +");
+ emit valueChanged(traits::storage_from_value(x));
+ maybe_trace("notify -");
+ }
never_inline
- value(bundle b, const QString& name, t def) :
- base_value(b, name, &is_equal, std::type_index(typeid(element_type))),
- def(def)
- {
- if (!self_name.isEmpty())
- QObject::connect(b.get(), SIGNAL(reloading()),
- this, SLOT(reload()),
- DIRECT_CONNTYPE);
+ void notify() const override
+ {
+ if (is_null())
+ return;
+
+ auto x = get();
+ {
+ QMutexLocker l(&mtx);
+ if (traits::is_equal(x, cached_value))
+ {
+ //maybe_trace("notify ~");
+ return;
+ }
+ else
+ {
+ cached_value = x;
+ l.unlock();
+ maybe_trace("notify +");
+ emit valueChanged(traits::storage_from_value(x));
+ maybe_trace("notify -");
+ }
+ }
}
- template<unsigned k>
- inline value(bundle b, const char (&name)[k], t def) : value(b, QLatin1String(name, k-1), def)
+ auto& operator=(t&& datum) noexcept
{
- static_assert(k > 0, "");
+ if (is_null())
+ return *this;
+
+ store_variant(traits::qvariant_from_value(traits::pass_value(datum)));
+ maybe_trace("set-value");
+ return *this;
}
- never_inline
- t default_value() const
+ auto& operator=(const t& datum) noexcept
{
- return def;
+ if (is_null())
+ return *this;
+
+ t copy{datum};
+ *this = std::move(copy);
+ return *this;
}
- never_inline
- void set_to_default() override
+ auto& operator=(const value<t>& datum) noexcept
{
- *this = def;
+ *this = *datum;
+ return *this;
}
- never_inline
- operator t() const { return get(); }
+ static constexpr Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection;
+ static constexpr Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
- never_inline
- t operator->() const
+ value(bundle b, const QString& name, t def) noexcept : value_(b, name), def(std::move(def)), cached_value{get()}
{
- return get();
}
- never_inline
- void reload() override
+ //value(const value<t>& other) noexcept : value{other.b, other.self_name, other.def} {}
+ value(const value<t>&) = delete;
+
+ t default_value() const
{
- if (!self_name.isEmpty())
- *this = static_cast<t>(*this);
+ return def;
}
- never_inline
- void bundle_value_changed() const override
+ void set_to_default() noexcept override
{
- if (!self_name.isEmpty())
- emit valueChanged(traits::to_storage(get()));
+ *this = def;
}
- never_inline
- t operator()() const
+ operator t() const { return get(); }
+
+ template<typename u>
+ explicit force_inline operator u() const { return to<u>(); }
+
+ auto operator->() const noexcept
{
- return get();
+ return detail::dereference_wrapper<t>{get()};
}
+ force_inline auto operator()() const noexcept { return get(); }
+ force_inline auto operator*() const noexcept { return get(); }
+
template<typename u>
- never_inline
- u to() const
+ u to() const noexcept
{
return static_cast<u>(get());
}
-
-private:
- const t def;
};
-#if defined _MSC_VER
-
-# if !defined OTR_OPT_VALUE
-# define OTR_OPT_VALUE OTR_TEMPLATE_IMPORT
-# endif
-
- OTR_OPT_VALUE value<double>;
- OTR_OPT_VALUE value<float>;
- OTR_OPT_VALUE value<int>;
- OTR_OPT_VALUE value<bool>;
- OTR_OPT_VALUE value<QString>;
- OTR_OPT_VALUE value<slider_value>;
- OTR_OPT_VALUE value<QPointF>;
- OTR_OPT_VALUE value<QVariant>;
-
- OTR_OPT_VALUE value<QList<double>>;
- OTR_OPT_VALUE value<QList<float>>;
- OTR_OPT_VALUE value<QList<int>>;
- OTR_OPT_VALUE value<QList<bool>>;
- OTR_OPT_VALUE value<QList<QString>>;
- OTR_OPT_VALUE value<QList<slider_value>>;
- OTR_OPT_VALUE value<QList<QPointF>>;
-
+#if !defined OTR_OPTIONS_INST_VALUE
+# define OTR_OPTIONS_INST_VALUE OTR_TEMPLATE_IMPORT
#endif
+OTR_OPTIONS_INST_VALUE(value<double>)
+OTR_OPTIONS_INST_VALUE(value<float>)
+OTR_OPTIONS_INST_VALUE(value<int>)
+OTR_OPTIONS_INST_VALUE(value<bool>)
+OTR_OPTIONS_INST_VALUE(value<QString>)
+OTR_OPTIONS_INST_VALUE(value<slider_value>)
+OTR_OPTIONS_INST_VALUE(value<QPointF>)
+OTR_OPTIONS_INST_VALUE(value<QVariant>)
+OTR_OPTIONS_INST_VALUE(value<QList<double>>)
+OTR_OPTIONS_INST_VALUE(value<QList<float>>)
+OTR_OPTIONS_INST_VALUE(value<QList<int>>)
+OTR_OPTIONS_INST_VALUE(value<QList<bool>>)
+OTR_OPTIONS_INST_VALUE(value<QList<QString>>)
+OTR_OPTIONS_INST_VALUE(value<QList<slider_value>>)
+OTR_OPTIONS_INST_VALUE(value<QList<QPointF>>)
+OTR_OPTIONS_INST_VALUE(value<QList<QVariant>>)
+
} // ns options