diff options
| author | Stanislaw Halik <sthalik@misaki.pl> | 2015-11-01 11:29:24 +0100 | 
|---|---|---|
| committer | Stanislaw Halik <sthalik@misaki.pl> | 2015-11-01 11:29:24 +0100 | 
| commit | be36337e46218c6f843a478036497a3f494502ed (patch) | |
| tree | dbb9dcb91f8a0233d3facb91e7d860c23a97ca4c /opentrack-compat | |
| parent | 0424a2802aec3600774ba9347c49312bb09b8cfe (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.cpp | 16 | ||||
| -rw-r--r-- | opentrack-compat/options.hpp | 485 | 
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); +    } +}  | 
