summaryrefslogtreecommitdiffhomepage
path: root/opentrack-compat
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2015-11-01 11:29:24 +0100
committerStanislaw Halik <sthalik@misaki.pl>2015-11-01 11:29:24 +0100
commitbe36337e46218c6f843a478036497a3f494502ed (patch)
treedbb9dcb91f8a0233d3facb91e7d860c23a97ca4c /opentrack-compat
parent0424a2802aec3600774ba9347c49312bb09b8cfe (diff)
options: don't define options singleton in every module
Instead, define in opentrack-api.so only. Also, move to opentrack-compat to break a circular dependency
Diffstat (limited to 'opentrack-compat')
-rw-r--r--opentrack-compat/options.cpp16
-rw-r--r--opentrack-compat/options.hpp485
2 files changed, 501 insertions, 0 deletions
diff --git a/opentrack-compat/options.cpp b/opentrack-compat/options.cpp
new file mode 100644
index 00000000..91cd3664
--- /dev/null
+++ b/opentrack-compat/options.cpp
@@ -0,0 +1,16 @@
+#include "options.hpp"
+
+namespace options
+{
+
+namespace detail
+{
+OPENTRACK_COMPAT_EXPORT opt_singleton& singleton()
+{
+ static auto ret = std::make_shared<opt_singleton>();
+ return *ret;
+}
+
+}
+
+}
diff --git a/opentrack-compat/options.hpp b/opentrack-compat/options.hpp
new file mode 100644
index 00000000..be8c688c
--- /dev/null
+++ b/opentrack-compat/options.hpp
@@ -0,0 +1,485 @@
+/* Copyright (c) 2013-2015 Stanislaw Halik
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#pragma once
+
+#include <memory>
+#include <tuple>
+#include <map>
+#include <string>
+
+#include <QObject>
+#include <QSettings>
+#include <QString>
+#include <QVariant>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QWidget>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QDoubleSpinBox>
+#include <QSpinBox>
+#include <QSlider>
+#include <QLineEdit>
+#include <QLabel>
+#include <QTabWidget>
+#include <QCoreApplication>
+#include <QFileInfo>
+#include <QDir>
+#include <QStandardPaths>
+
+#include <cinttypes>
+
+#include <QDebug>
+
+#include <memory>
+
+#ifdef BUILD_compat
+# include "compat-export.hpp"
+#else
+# include "compat-import.hpp"
+#endif
+
+template<typename t> using mem = std::shared_ptr<t>;
+
+#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename"
+#define OPENTRACK_DEFAULT_CONFIG "default.ini"
+#define OPENTRACK_ORG "opentrack-2.3"
+
+namespace options {
+ template<typename k, typename v>
+ using map = std::map<k, v>;
+ using std::string;
+
+ template<typename t>
+ // don't elide usages of the function, qvariant default implicit
+ // conversion results in nonsensical runtime behavior -sh
+ inline t qcruft_to_t (const QVariant& datum);
+
+ template<>
+ inline int qcruft_to_t<int>(const QVariant& t)
+ {
+ return t.toInt();
+ }
+
+ template<>
+ inline QString qcruft_to_t<QString>(const QVariant& t)
+ {
+ return t.toString();
+ }
+
+ template<>
+ inline bool qcruft_to_t<bool>(const QVariant& t)
+ {
+ return t.toBool();
+ }
+
+ template<>
+ inline double qcruft_to_t<double>(const QVariant& t)
+ {
+ return t.toDouble();
+ }
+
+ template<>
+ inline QVariant qcruft_to_t<QVariant>(const QVariant& t)
+ {
+ return t;
+ }
+
+ // snapshot of qsettings group at given time
+ class group {
+ private:
+ map<string, QVariant> kvs;
+ string name;
+ public:
+ group(const string& name) : name(name)
+ {
+ auto conf = ini_file();
+ auto q_name = QString::fromStdString(name);
+ conf->beginGroup(q_name);
+ for (auto& k_ : conf->childKeys())
+ {
+ auto tmp = k_.toUtf8();
+ string k(tmp);
+ kvs[k] = conf->value(k_);
+ }
+ conf->endGroup();
+ }
+
+ void save()
+ {
+ auto s = ini_file();
+ auto q_name = QString::fromStdString(name);
+ s->beginGroup(q_name);
+ for (auto& i : kvs)
+ {
+ auto k = QString::fromStdString(i.first);
+ s->setValue(k, i.second);
+ }
+ s->endGroup();
+ s->sync();
+ }
+
+ template<typename t>
+ t get(const string& k)
+ {
+ return qcruft_to_t<t>(kvs[k]);
+ }
+
+ void put(const string& s, const QVariant& d)
+ {
+ kvs[s] = d;
+ }
+
+ bool contains(const string& s)
+ {
+ return kvs.count(s) != 0;
+ }
+
+ static QString ini_directory()
+ {
+ const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
+ if (dirs.size() == 0)
+ return "";
+ if (QDir(dirs[0]).mkpath(OPENTRACK_ORG))
+ return dirs[0] + "/" OPENTRACK_ORG;
+ return "";
+ }
+
+ static QString ini_filename()
+ {
+ QSettings settings(OPENTRACK_ORG);
+ return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
+ }
+
+ static QString ini_pathname()
+ {
+ const auto dir = ini_directory();
+ if (dir == "")
+ return "";
+ QSettings settings(OPENTRACK_ORG);
+ return dir + "/" + settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
+ }
+
+ static const QStringList ini_list()
+ {
+ const auto dirname = ini_directory();
+ if (dirname == "")
+ return QStringList();
+ QDir settings_dir(dirname);
+ return settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name );
+ }
+
+ static const mem<QSettings> ini_file()
+ {
+ const auto pathname = ini_pathname();
+ if (pathname != "")
+ return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat);
+ return std::make_shared<QSettings>();
+ }
+ };
+
+ class OPENTRACK_COMPAT_EXPORT impl_bundle : public QObject {
+ Q_OBJECT
+ protected:
+ QMutex mtx;
+ const string group_name;
+ group saved;
+ group transient;
+ bool modified;
+ impl_bundle(const impl_bundle&) = delete;
+ impl_bundle& operator=(const impl_bundle&) = delete;
+ signals:
+ void reloading();
+ void saving();
+ public:
+ impl_bundle(const string& group_name) :
+ mtx(QMutex::Recursive),
+ group_name(group_name),
+ saved(group_name),
+ transient(saved),
+ modified(false)
+ {
+ }
+
+ string name() { return group_name; }
+
+ void reload() {
+ {
+ QMutexLocker l(&mtx);
+ saved = group(group_name);
+ transient = saved;
+ modified = false;
+ }
+ emit reloading();
+ }
+
+ void store_kv(const string& name, const QVariant& datum)
+ {
+ QMutexLocker l(&mtx);
+
+ auto old = transient.get<QVariant>(name);
+ if (!transient.contains(name) || datum != old)
+ {
+ modified = true;
+ transient.put(name, datum);
+ }
+ }
+ bool contains(const string& name)
+ {
+ QMutexLocker l(&mtx);
+ return transient.contains(name);
+ }
+ template<typename t>
+ t get(const string& name)
+ {
+ QMutexLocker l(&mtx);
+ return transient.get<t>(name);
+ }
+ void save()
+ {
+ {
+ QMutexLocker l(&mtx);
+ modified = false;
+ saved = transient;
+ transient.save();
+ }
+ emit saving();
+ }
+
+ bool modifiedp() {
+ QMutexLocker l(&mtx);
+ return modified;
+ }
+ };
+
+ class opt_bundle;
+
+ namespace detail
+ {
+ struct OPENTRACK_COMPAT_EXPORT opt_singleton
+ {
+ public:
+ using k = std::string;
+ using v = opt_bundle;
+ using cnt = int;
+ using pbundle = std::shared_ptr<v>;
+ using tt = std::tuple<cnt, std::weak_ptr<v>>;
+ private:
+ QMutex implsgl_mtx;
+ map<k, tt> implsgl_data;
+ public:
+ opt_singleton() : implsgl_mtx(QMutex::Recursive) {}
+
+ pbundle bundle(const k& key)
+ {
+ QMutexLocker l(&implsgl_mtx);
+
+ if (implsgl_data.count(key) != 0)
+ {
+ auto shared = std::get<1>(implsgl_data[key]).lock();
+ if (shared != nullptr)
+ return shared;
+ }
+
+ qDebug() << "bundle +" << QString::fromStdString(key);
+
+ auto shr = std::make_shared<v>(key);
+ implsgl_data[key] = tt(cnt(1), shr);
+ return shr;
+ }
+
+ void bundle_decf(const k& key)
+ {
+ QMutexLocker l(&implsgl_mtx);
+
+ if (--std::get<0>(implsgl_data[key]) == 0)
+ implsgl_data.erase(key);
+ }
+ };
+
+ OPENTRACK_COMPAT_EXPORT opt_singleton& singleton();
+ }
+
+ using pbundle = std::shared_ptr<opt_bundle>;
+
+ static inline pbundle bundle(const string name) { return detail::singleton().bundle(name); }
+
+ class opt_bundle : public impl_bundle
+ {
+ public:
+ opt_bundle() : impl_bundle("i-have-no-name") {}
+ opt_bundle(const string& group_name) : impl_bundle(group_name)
+ {
+ }
+
+ ~opt_bundle()
+ {
+ qDebug() << "bundle -" << QString::fromStdString(group_name);
+ detail::singleton().bundle_decf(group_name);
+ }
+ };
+
+ class OPENTRACK_COMPAT_EXPORT base_value : public QObject
+ {
+ Q_OBJECT
+#define DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
+#define DEFINE_SIGNAL(t) void valueChanged(t)
+ public:
+ string name() { return self_name; }
+ base_value(pbundle b, const string& name) : b(b), self_name(name) {}
+ signals:
+ DEFINE_SIGNAL(double);
+ DEFINE_SIGNAL(int);
+ DEFINE_SIGNAL(bool);
+ DEFINE_SIGNAL(QString);
+ protected:
+ pbundle b;
+ string self_name;
+
+ template<typename t>
+ void store(const t& datum)
+ {
+ b->store_kv(self_name, datum);
+ emit valueChanged(static_cast<t>(datum));
+ }
+ public slots:
+ DEFINE_SLOT(double)
+ DEFINE_SLOT(int)
+ DEFINE_SLOT(QString)
+ DEFINE_SLOT(bool)
+ public slots:
+ virtual void reload() = 0;
+ };
+
+ static inline string string_from_qstring(const QString& datum)
+ {
+ auto tmp = datum.toUtf8();
+ return string(tmp.constData());
+ }
+
+ template<typename t>
+ class value : public base_value {
+ public:
+ t operator=(const t datum)
+ {
+ store(datum);
+ return datum;
+ }
+ static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection;
+ static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::UniqueConnection;
+ value(pbundle b, const string& name, t def) : base_value(b, name), def(def)
+ {
+ QObject::connect(b.get(), SIGNAL(reloading()),
+ this, SLOT(reload()),
+ DIRECT_CONNTYPE);
+ if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
+ *this = def;
+ }
+ value(pbundle b, const QString& name, t def) : value(b, string_from_qstring(name), def) {}
+ value(pbundle b, const char* name, t def) : value(b, string(name), def) {}
+
+ operator t()
+ {
+ return b->contains(self_name) ? b->get<t>(self_name) : def;
+ }
+ void reload() override {
+ *this = static_cast<t>(*this);
+ }
+ private:
+ t def;
+ };
+
+ struct opts
+ {
+ pbundle b;
+
+ opts(const std::string& name) : b(bundle(name)) {}
+
+ ~opts()
+ {
+ b->reload();
+ }
+ };
+
+ template<typename t, typename q>
+ inline void tie_setting(value<t>&, q*);
+
+ template<>
+ inline 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);
+ }
+
+ template<>
+ inline void tie_setting(value<QString>& v, QComboBox* cb)
+ {
+ cb->setCurrentText(v);
+ v = cb->currentText();
+ base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_CONNTYPE);
+ }
+
+ template<>
+ inline 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);
+ }
+
+ template<>
+ inline 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);
+ }
+
+ template<>
+ inline 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);
+ }
+
+ template<>
+ inline void tie_setting(value<int>& v, QSlider* sl)
+ {
+ sl->setValue(v);
+ v = sl->value();
+ base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+ }
+
+ template<>
+ inline 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, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_CONNTYPE);
+ }
+
+ template<>
+ inline void tie_setting(value<QString>& v, QLabel* lb)
+ {
+ lb->setText(v);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.SAFE_CONNTYPE);
+ }
+
+ template<>
+ inline 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);
+ }
+}