diff options
56 files changed, 962 insertions, 810 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index bd42afa3..25220fe5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ file(GLOB opentrack-subprojects      "tracker-*/${C}"      "proto-*/${C}"      "filter-*/${C}" +    "options/${C}"      "api/${C}"      "compat/${C}"      "logic/${C}" diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 5151cbf7..0be2c242 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -1,3 +1,3 @@  opentrack_boilerplate(opentrack-api NO-COMPAT BIN) -target_link_libraries(opentrack-api opentrack-compat) +target_link_libraries(opentrack-api opentrack-options opentrack-compat)  target_include_directories(opentrack-api PUBLIC ${CMAKE_BINARY_DIR}) diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp index 072c8da7..8b20d211 100644 --- a/api/plugin-support.hpp +++ b/api/plugin-support.hpp @@ -8,7 +8,7 @@  #pragma once  #include "plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include <QWidget>  #include <QDebug> diff --git a/cmake/opentrack-boilerplate.cmake b/cmake/opentrack-boilerplate.cmake index a731f3f0..41af076c 100644 --- a/cmake/opentrack-boilerplate.cmake +++ b/cmake/opentrack-boilerplate.cmake @@ -87,7 +87,7 @@ function(opentrack_boilerplate__ n files_ no-library_ static_ no-compat_ compile          opentrack_compat(${n})      endif()      if(NOT no-compat_) -        target_link_libraries(${n} opentrack-api opentrack-compat) +        target_link_libraries(${n} opentrack-api opentrack-options opentrack-compat)      endif()      target_link_libraries(${n} ${MY_QT_LIBS})      if(CMAKE_COMPILER_IS_GNUCXX) diff --git a/compat/options.cpp b/compat/options.cpp deleted file mode 100644 index 33c1cf0d..00000000 --- a/compat/options.cpp +++ /dev/null @@ -1,269 +0,0 @@ -#include "options.hpp" - -namespace options -{ - -group::group(const QString& name) : name(name) -{ -    auto conf = ini_file(); -    conf->beginGroup(name); -    for (auto& k_ : conf->childKeys()) -    { -        auto tmp = k_.toUtf8(); -        QString k(tmp); -        kvs[k] = conf->value(k_); -    } -    conf->endGroup(); -} - -void group::save() const -{ -    auto s = ini_file(); -    s->beginGroup(name); -    for (auto& i : kvs) -        s->setValue(i.first, i.second); -    s->endGroup(); -} - -void group::put(const QString &s, const QVariant &d) -{ -    kvs[s] = d; -} - -bool group::contains(const QString &s) const -{ -    return kvs.count(s) != 0; -} - -QString group::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 ""; -} - -QString group::ini_filename() -{ -    QSettings settings(OPENTRACK_ORG); -    return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); -} - -QString group::ini_pathname() -{ -    const auto dir = ini_directory(); -    if (dir == "") -        return ""; -    return dir + "/" + ini_filename(); -} - -const 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; -} - -const mem<QSettings> group::ini_file() -{ -    const auto pathname = ini_pathname(); -    if (pathname != "") -        return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat); -    return std::make_shared<QSettings>(); -} - -bool group::operator==(const group& other) const -{ -    for (const auto& kv : kvs) -    { -        const QVariant val = other.get<QVariant>(kv.first); -        if (!other.contains(kv.first) || kv.second != val) -        { -            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << val << "<>" << kv.second; -            return false; -        } -    } - -    for (const auto& kv : other.kvs) -    { -        const QVariant val = get<QVariant>(kv.first); -        if (!contains(kv.first) || kv.second != val) -        { -            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << kv.second << "<>" << val; -            return false; -        } -    } -    return true; -} - -impl_bundle::impl_bundle(const QString& group_name) -    : -      mtx(QMutex::Recursive), -      group_name(group_name), -      saved(group_name), -      transient(saved) -{ -} - -void impl_bundle::reload() -{ -    { -        QMutexLocker l(&mtx); -        saved = group(group_name); -        transient = saved; -    } -    emit reloading(); -} - -void impl_bundle::store_kv(const QString& name, const QVariant& datum) -{ -    QMutexLocker l(&mtx); - -    transient.put(name, datum); -} - -bool impl_bundle::contains(const QString &name) const -{ -    QMutexLocker l(const_cast<QMutex*>(&mtx)); -    return transient.contains(name); -} - -void impl_bundle::save() -{ -    bool modified_ = false; - -    { -        QMutexLocker l(&mtx); -        if (saved != transient) -        { -            qDebug() << "bundle" << group_name << "changed, saving"; -            modified_ = true; -            saved = transient; -            saved.save(); -        } -    } - -    if (modified_) -        emit saving(); -} - -bool impl_bundle::modifiedp() const // XXX unused -{ -    QMutexLocker l(const_cast<QMutex*>(&mtx)); -    return transient != saved; -} - -base_value::base_value(pbundle b, const QString &name) : -    b(b), -    self_name(name) -{ -} - -opts::~opts() -{ -    b->reload(); -} - -opts::opts(const QString &name) : b(bundle(name)) -{ -} - -detail::custom_type_initializer::custom_type_initializer() -{ -    qRegisterMetaTypeStreamOperators<slider_value>("slider_value"); -    QMetaType::registerDebugStreamOperator<slider_value>(); -} - -detail::custom_type_initializer detail::custom_type_initializer::singleton; - -namespace detail { - -opt_bundle::opt_bundle(const QString& group_name) -    : impl_bundle(group_name) -{ -} - -opt_bundle::~opt_bundle() -{ -    detail::singleton().bundle_decf(group_name); -} - -void opt_singleton::bundle_decf(const opt_singleton::k& key) -{ -    QMutexLocker l(&implsgl_mtx); - -    if (--std::get<0>(implsgl_data[key]) == 0) -    { -        qDebug() << "bundle -" << key; - -        implsgl_data.erase(key); -    } -} - -void opt_singleton::after_profile_changed_() -{ -    QMutexLocker l(&implsgl_mtx); - -    for (auto& kv : implsgl_data) -    { -        tt& tuple = kv.second; -        std::weak_ptr<v>& bundle = std::get<1>(tuple); - -        mem<v> bundle_ = bundle.lock(); -        if (bundle_) -        { -            qDebug() << "bundle: reverting" << kv.first << "due to profile change"; -            bundle_->reload(); -        } -    } -} - -void opt_singleton::refresh_all_bundles() -{ -    singleton().after_profile_changed_(); -} - -opt_singleton::opt_singleton() : implsgl_mtx(QMutex::Recursive) -{ -} - -opt_singleton::~opt_singleton() -{ -    qDebug() << "exit: bundle singleton"; -} - -pbundle opt_singleton::bundle(const opt_singleton::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 +" << key; - -    auto shr = std::make_shared<v>(key); -    implsgl_data[key] = tt(1, shr); -    return shr; -} - -OPENTRACK_COMPAT_EXPORT opt_singleton& singleton() -{ -    static opt_singleton ret; -    return ret; -} - - -} // end options::detail - -} // end options - diff --git a/compat/options.hpp b/compat/options.hpp deleted file mode 100644 index 7b004ba6..00000000 --- a/compat/options.hpp +++ /dev/null @@ -1,491 +0,0 @@ -/* Copyright (c) 2013-2016 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 <cinttypes> -#include <vector> -#include <memory> - -#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 <QApplication> - -#include <QMetaType> -#include <QDataStream> - -#include <QDebug> - -#include "export.hpp" -#include "slider.hpp" - -#include "util.hpp" - -#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" -#define OPENTRACK_DEFAULT_CONFIG "default.ini" -#define OPENTRACK_ORG "opentrack-2.3" - -namespace options { -namespace detail { -class custom_type_initializer -{ -    custom_type_initializer(); -    static custom_type_initializer singleton; -}; -} - -template<typename k, typename v> using map = std::map<k, v>; - -// snapshot of qsettings group at given time -class OPENTRACK_COMPAT_EXPORT group -{ -private: -    map<QString, QVariant> kvs; -    QString name; -public: -    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 const QStringList ini_list(); -    static const mem<QSettings> ini_file(); -    bool operator==(const group& other) const; -    bool operator!=(const group& other) const { return !(*this == other); } - -    template<typename t> -    t get(const QString& k) const -    { -        auto value = kvs.find(k); -        if (value != kvs.cend()) -            return value->second.value<t>(); -        return t(); -    } -}; - -class OPENTRACK_COMPAT_EXPORT impl_bundle : public QObject -{ -    Q_OBJECT -protected: -    QMutex mtx; -    const QString group_name; -    group saved; -    group transient; -    impl_bundle(const impl_bundle&) = delete; -    impl_bundle& operator=(const impl_bundle&) = delete; -signals: -    void reloading(); -    void saving() const; -public: -    impl_bundle(const QString& group_name); -    QString name() { return group_name; } -    void reload(); -    void store_kv(const QString& name, const QVariant& datum); -    bool contains(const QString& name) const; -    void save(); -    bool modifiedp() const; - -    template<typename t> -    t get(const QString& name) const -    { -        QMutexLocker l(const_cast<QMutex*>(&mtx)); -        return transient.get<t>(name); -    } -}; - -namespace detail -{ -class OPENTRACK_COMPAT_EXPORT opt_bundle final : public impl_bundle -{ -public: -    opt_bundle(const QString& group_name); -    ~opt_bundle(); -}; - -struct OPENTRACK_COMPAT_EXPORT opt_singleton -{ -public: -    using k = QString; -    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; -    void after_profile_changed_(); -public: -    opt_singleton(); -    ~opt_singleton(); -    pbundle bundle(const k& key); -    void bundle_decf(const k& key); -    static void refresh_all_bundles(); -}; - -OPENTRACK_COMPAT_EXPORT opt_singleton& singleton(); -} - -using pbundle = std::shared_ptr<detail::opt_bundle>; - -inline pbundle bundle(const QString& name) -{ -    return detail::singleton().bundle(name); -} - -#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); } -#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t) - -namespace detail { -template<typename t> struct value_type_traits { using type = t;}; -template<> struct value_type_traits<QString> { using type = const QString&; }; -template<> struct value_type_traits<slider_value> { using type = const slider_value&; }; -template<typename t> using value_type_t = typename value_type_traits<t>::type; -} - -class OPENTRACK_COMPAT_EXPORT base_value : public QObject -{ -    Q_OBJECT -public: -    QString name() const { return self_name; } -    base_value(pbundle b, const QString& name); -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&); -protected: -    pbundle b; -    QString self_name; - -    template<typename t> -    void store(const t& datum) -    { -        b->store_kv(self_name, QVariant::fromValue(datum)); -        emit valueChanged(static_cast<detail::value_type_t<t>>(datum)); -    } -    void store(float datum) -    { -        store(double(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&) -    public slots: -        virtual void reload() = 0; -}; - -namespace detail { -template<typename t> -struct value_get_traits -{ -    static inline t get(const t& val, const t&) -    { -        return val; -    } -}; - -template<> -struct value_get_traits<slider_value> -{ -    using t = slider_value; -    static inline t get(const t& val, const t& def) -    { -        return t(val.cur(), def.min(), def.max()); -    } -}; -} - -template<typename t_> -class value : public base_value -{ -    template<typename t__, typename Enable = void> -    struct get_t -    { using t = t__; }; - -    // Qt uses int a lot in slots so use it for all enums -    template<typename t__> -    struct get_t<t__, typename std::enable_if<std::is_enum<t__>::value>::type> -            //{ using t = typename std::underlying_type<t__>::type; }; -    { using t = int; }; - -    using t = t_; -public: -    using underlying_t = typename get_t<t_>::t; - -    t operator=(const t& datum) -    { -        store(static_cast<underlying_t>(datum)); -        return datum; -    } - -    static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::AutoConnection; -    static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection; - -    value(pbundle b, const QString& 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 char* name, t def) : value(b, QString(name), def) -    { -    } - -    t get() const -    { -        t val = b->contains(self_name) -                ? static_cast<t>(b->get<underlying_t>(self_name)) -                : def; -        return detail::value_get_traits<t>::get(val, def); -    } - -    operator t() const { return get(); } - -    void reload() override -    { -        *this = static_cast<t>(*this); -    } - -private: -    t def; -}; - -struct OPENTRACK_COMPAT_EXPORT opts -{ -    pbundle b; -    opts(const QString& name); -    opts& operator=(const opts&) = delete; -    opts(const opts&) = delete; -    ~opts(); -}; - -template<typename t, typename q> -inline void tie_setting(value<t>&, q*); - -template<typename t> -inline -typename std::enable_if<std::is_enum<t>::value>::type -tie_setting(value<t>& v, QComboBox* cb) -{ -    cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v))); -    v = static_cast<t>(cb->currentData().toInt()); - -    // QObject::connect plays badly with std::bind of std::shared_ptr. Data seems to get freed. -    // Direct accesses of cb->currentData within arbitrary thread context cause crashes as well. -    // Hence we go for a verbose implementation. - -    std::vector<int> enum_cases; -    enum_cases.reserve(unsigned(cb->count())); - -    for (int i = 0; i < cb->count(); i++) -        enum_cases.push_back(cb->itemData(i).toInt()); - -    struct fn1 -    { -        value<t>& v; -        QComboBox* cb; -        std::vector<int> enum_cases; - -        fn1(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) : -            v(v), -            cb(cb), -            enum_cases(enum_cases) -        { -        } - -        void operator()(int idx) -        { -            if (idx < 0 || idx >= (int)enum_cases.size()) -                v = static_cast<t>(-1); -            else -                v = static_cast<t>(t(std::intptr_t(enum_cases[idx]))); -        } -    }; - -    struct fn2 -    { -        value<t>& v; -        QComboBox* cb; -        std::vector<int> enum_cases; - -        fn2(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) : -            v(v), -            cb(cb), -            enum_cases(enum_cases) -        { -        } - -        void operator()(int val) -        { -            for (unsigned i = 0; i < enum_cases.size(); i++) -            { -                if (val == enum_cases[i]) -                { -                    cb->setCurrentIndex(i); -                    return; -                } -            } -            cb->setCurrentIndex(-1); -        } -    }; - -    base_value::connect(cb, -                        static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), -                        &v, -                        fn1(v, cb, enum_cases), -                        v.DIRECT_CONNTYPE); -    base_value::connect(&v, -                        static_cast<void (base_value::*)(int)>(&base_value::valueChanged), -                        cb, -                        fn2(v, cb, enum_cases), -                        v.DIRECT_CONNTYPE); -} - -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.DIRECT_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); -} - -template<> -inline void tie_setting(value<slider_value>& v, QSlider* w) -{ -    // we can't get these at runtime since signals cross threads -    const int q_min = w->minimum(); -    const int q_max = w->maximum(); -    const int q_diff = q_max - q_min; - -    slider_value sv(v); - -    const double sv_max = sv.max(); -    const double sv_min = sv.min(); -    const double sv_c = sv_max - sv_min; - -    w->setValue(int((sv.cur() - sv_min) / sv_c * q_diff + q_min)); -    v = slider_value(q_diff <= 0 ? 0 : (w->value() - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max); - -    base_value::connect(w, -                        &QSlider::valueChanged, -                        &v, -                        [=, &v](int pos) -> void -    { -        if (q_diff <= 0 || pos <= 0) -            v = slider_value(sv_min, sv_min, sv_max); -        else -            v = slider_value((pos - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max); -    }, -    v.DIRECT_CONNTYPE); -    base_value::connect(&v, -                        static_cast<void(base_value::*)(double)>(&base_value::valueChanged), -                        w, -                        [=](double value) -> void -    { -        w->setValue(int(value * q_diff) + q_min); -    }, -    v.SAFE_CONNTYPE); -} -} - diff --git a/compat/singleton.cpp b/compat/singleton.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/compat/singleton.cpp diff --git a/compat/singleton.hpp b/compat/singleton.hpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/compat/singleton.hpp diff --git a/filter-accela/ftnoir_filter_accela.h b/filter-accela/ftnoir_filter_accela.h index 828b78a0..d3156429 100644 --- a/filter-accela/ftnoir_filter_accela.h +++ b/filter-accela/ftnoir_filter_accela.h @@ -12,7 +12,7 @@  #include <QMutex>  #include <QTimer> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  #include "compat/timer.hpp" diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h index e7fdd49e..8680971b 100644 --- a/filter-ewma2/ftnoir_filter_ewma2.h +++ b/filter-ewma2/ftnoir_filter_ewma2.h @@ -4,7 +4,7 @@  #include "ui_ftnoir_ewma_filtercontrols.h"  #include <QWidget>  #include <QMutex> -#include "compat/options.hpp" +#include "options/options.hpp"  #include "compat/timer.hpp"  using namespace options; diff --git a/filter-kalman/kalman.h b/filter-kalman/kalman.h index d28557c6..d2bfaee5 100644 --- a/filter-kalman/kalman.h +++ b/filter-kalman/kalman.h @@ -8,7 +8,7 @@  #include "ui_ftnoir_kalman_filtercontrols.h"  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  #include "compat/timer.hpp" diff --git a/gui/main-window.cpp b/gui/main-window.cpp index 2f856b11..9cad0ef4 100644 --- a/gui/main-window.cpp +++ b/gui/main-window.cpp @@ -8,7 +8,7 @@  #include "main-window.hpp"  #include "logic/tracker.h" -#include "compat/options.hpp" +#include "options/options.hpp"  #include "opentrack-library-path.h"  #include "new_file_dialog.h"  #include <QFile> diff --git a/gui/main-window.hpp b/gui/main-window.hpp index 42e8425c..a74575c9 100644 --- a/gui/main-window.hpp +++ b/gui/main-window.hpp @@ -17,7 +17,7 @@  #include "logic/shortcuts.h"  #include "logic/work.hpp"  #include "logic/state.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include <QMainWindow>  #include <QKeySequence> diff --git a/gui/main.cpp b/gui/main.cpp index 43fd4539..fd41cbd3 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -9,7 +9,7 @@  #endif  #include "main-window.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include "compat/win32-com.hpp"  using namespace options;  #include <QApplication> diff --git a/gui/new_file_dialog.h b/gui/new_file_dialog.h index 8cb971c3..6ba3ede3 100644 --- a/gui/new_file_dialog.h +++ b/gui/new_file_dialog.h @@ -1,7 +1,7 @@  #pragma once  #include "ui_new_config.h" -#include "compat/options.hpp" +#include "options/options.hpp"  #include <QFile>  #include <QRegExp>  #include <QString> diff --git a/gui/process_detector.h b/gui/process_detector.h index e081fa27..a659c178 100644 --- a/gui/process_detector.h +++ b/gui/process_detector.h @@ -13,7 +13,7 @@  #include <QTableWidget>  #include <QResizeEvent> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  class FancyTable : public QTableWidget diff --git a/logic/main-settings.hpp b/logic/main-settings.hpp index 26313a26..d96a833b 100644 --- a/logic/main-settings.hpp +++ b/logic/main-settings.hpp @@ -9,7 +9,7 @@  #pragma once  #include <QString> -#include "compat/options.hpp" +#include "options/options.hpp"  #include "api/plugin-api.hpp"  using namespace options; diff --git a/logic/mappings.hpp b/logic/mappings.hpp index 0b9373c0..038b7560 100644 --- a/logic/mappings.hpp +++ b/logic/mappings.hpp @@ -7,8 +7,7 @@  #pragma once -#include <QSettings> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  #include "spline-widget/spline.hpp"  #include "main-settings.hpp" diff --git a/logic/shortcuts.h b/logic/shortcuts.h index c81b01f6..52b9979a 100644 --- a/logic/shortcuts.h +++ b/logic/shortcuts.h @@ -15,7 +15,7 @@  #include "export.hpp"  #include "qxt-mini/QxtGlobalShortcut" -#include "compat/options.hpp" +#include "options/options.hpp"  #include "main-settings.hpp"  #ifdef _WIN32 diff --git a/logic/state.hpp b/logic/state.hpp index 4c3bb7a0..4e021949 100644 --- a/logic/state.hpp +++ b/logic/state.hpp @@ -8,7 +8,7 @@  #pragma once -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  #include "api/plugin-support.hpp"  #include "main-settings.hpp" diff --git a/logic/tracker.h b/logic/tracker.h index 15fb9701..d3c2660b 100644 --- a/logic/tracker.h +++ b/logic/tracker.h @@ -19,7 +19,7 @@  #include "spline-widget/spline.hpp"  #include "main-settings.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include "tracklogger.hpp"  #include <QMutex> diff --git a/logic/tracklogger.hpp b/logic/tracklogger.hpp index 52ab35bc..19e16fef 100644 --- a/logic/tracklogger.hpp +++ b/logic/tracklogger.hpp @@ -1,6 +1,6 @@  #pragma once  #include "main-settings.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include "compat/timer.hpp"  #include <fstream> diff --git a/options/CMakeLists.txt b/options/CMakeLists.txt new file mode 100644 index 00000000..25749bf5 --- /dev/null +++ b/options/CMakeLists.txt @@ -0,0 +1,4 @@ +opentrack_boilerplate(opentrack-options NO-COMPAT BIN) +if(NOT WIN32 AND NOT APPLE) +    target_link_libraries(opentrack-options rt) +endif() diff --git a/options/bundle.cpp b/options/bundle.cpp new file mode 100644 index 00000000..a60810c5 --- /dev/null +++ b/options/bundle.cpp @@ -0,0 +1,140 @@ +#include "bundle.hpp" + +namespace options +{ + +namespace detail { + +bundle::bundle(const QString& group_name) +    : +      mtx(QMutex::Recursive), +      group_name(group_name), +      saved(group_name), +      transient(saved) +{ +} + +void bundle::reload() +{ +    { +        QMutexLocker l(&mtx); +        saved = group(group_name); +        transient = saved; +    } +    emit reloading(); +} + +void bundle::store_kv(const QString& name, const QVariant& datum) +{ +    QMutexLocker l(&mtx); + +    transient.put(name, datum); +} + +bool bundle::contains(const QString &name) const +{ +    QMutexLocker l(const_cast<QMutex*>(&mtx)); +    return transient.contains(name); +} + +void bundle::save() +{ +    if (group_name == "") +        return; + +    bool modified_ = false; + +    { +        QMutexLocker l(&mtx); +        if (saved != transient) +        { +            qDebug() << "bundle" << group_name << "changed, saving"; +            modified_ = true; +            saved = transient; +            saved.save(); +        } +    } + +    if (modified_) +        emit saving(); +} + +bool bundle::modifiedp() const // XXX unused +{ +    QMutexLocker l(const_cast<QMutex*>(&mtx)); +    return transient != saved; +} + +void bundler::bundle_decf(const bundler::k& key) +{ +    QMutexLocker l(&implsgl_mtx); + +    if (--std::get<0>(implsgl_data[key]) == 0) +    { +        qDebug() << "bundle -" << key; + +        implsgl_data.erase(key); +    } +} + +void bundler::after_profile_changed_() +{ +    QMutexLocker l(&implsgl_mtx); + +    for (auto& kv : implsgl_data) +    { +        tt& tuple = kv.second; +        std::weak_ptr<v>& bundle = std::get<1>(tuple); + +        mem<v> bundle_ = bundle.lock(); +        if (bundle_) +        { +            qDebug() << "bundle: reverting" << kv.first << "due to profile change"; +            bundle_->reload(); +        } +    } +} + +void bundler::refresh_all_bundles() +{ +    singleton().after_profile_changed_(); +} + +bundler::bundler() : implsgl_mtx(QMutex::Recursive) +{ +} + +bundler::~bundler() +{ +    qDebug() << "exit: bundle singleton"; +} + +std::shared_ptr<bundler::v> bundler::make_bundle(const bundler::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 +" << key; + +    std::shared_ptr<v> shr(new v(key), [this](v* val) { bundle_decf(val->name()); }); + +    implsgl_data[key] = tt(1, shr); +    return shr; +} + +OPENTRACK_OPTIONS_EXPORT bundler& singleton() +{ +    static bundler ret; +    return ret; +} + +} // end options::detail + +} // end options + diff --git a/options/bundle.hpp b/options/bundle.hpp new file mode 100644 index 00000000..56d48f60 --- /dev/null +++ b/options/bundle.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include <memory> +#include <tuple> +#include <map> +#include <memory> + +#include <QObject> +#include <QString> +#include <QVariant> +#include <QMutex> +#include <QMutexLocker> + +#include <QDebug> + +#include "group.hpp" +#include "compat/util.hpp" +#include "export.hpp" + +namespace options { + +namespace detail { + +class OPENTRACK_OPTIONS_EXPORT bundle final : public QObject +{ +    Q_OBJECT +protected: +    QMutex mtx; +    const QString group_name; +    group saved; +    group transient; +    bundle(const bundle&) = delete; +    bundle& operator=(const bundle&) = delete; +signals: +    void reloading(); +    void saving() const; +public: +    bundle(const QString& group_name); +    QString name() { return group_name; } +    void reload(); +    void store_kv(const QString& name, const QVariant& datum); +    bool contains(const QString& name) const; +    void save(); +    bool modifiedp() const; + +    template<typename t> +    t get(const QString& name) const +    { +        QMutexLocker l(const_cast<QMutex*>(&mtx)); +        return transient.get<t>(name); +    } +}; + +struct OPENTRACK_OPTIONS_EXPORT bundler +{ +public: +    using k = QString; +    using v = bundle; +    using cnt = int; +    using tt = std::tuple<cnt, std::weak_ptr<v>>; +private: +    QMutex implsgl_mtx; +    std::map<k, tt> implsgl_data; +    void after_profile_changed_(); +public: +    bundler(); +    ~bundler(); +    std::shared_ptr<v> make_bundle(const k& key); +    void bundle_decf(const k& key); +    static void refresh_all_bundles(); +}; + +OPENTRACK_OPTIONS_EXPORT bundler& singleton(); +} + +using bundle_type = detail::bundle; +using bundle = std::shared_ptr<bundle_type>; + +inline bundle make_bundle(const QString& name) +{ +    return detail::singleton().make_bundle(name); +} + +} diff --git a/options/defs.hpp b/options/defs.hpp new file mode 100644 index 00000000..8464c784 --- /dev/null +++ b/options/defs.hpp @@ -0,0 +1,5 @@ +#pragma once + +#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" +#define OPENTRACK_DEFAULT_CONFIG "default.ini" +#define OPENTRACK_ORG "opentrack-2.3" diff --git a/options/export.hpp b/options/export.hpp new file mode 100644 index 00000000..c430540f --- /dev/null +++ b/options/export.hpp @@ -0,0 +1,27 @@ +#pragma once + +#ifdef BUILD_options +#   ifdef _WIN32 +#       define OPENTRACK_OPTIONS_LINKAGE __declspec(dllexport) +#   else +#       define OPENTRACK_OPTIONS_LINKAGE +#   endif + +#   ifndef _MSC_VER +#       define OPENTRACK_OPTIONS_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_OPTIONS_LINKAGE +#   else +#       define OPENTRACK_OPTIONS_EXPORT OPENTRACK_OPTIONS_LINKAGE +#   endif +#else +    #ifdef _WIN32 +    #    define OPENTRACK_OPTIONS_LINKAGE __declspec(dllimport) +    #else +    #    define OPENTRACK_OPTIONS_LINKAGE +    #endif + +    #ifndef _MSC_VER +    #    define OPENTRACK_OPTIONS_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_OPTIONS_LINKAGE +    #else +    #    define OPENTRACK_OPTIONS_EXPORT OPENTRACK_OPTIONS_LINKAGE +    #endif +#endif diff --git a/options/group.cpp b/options/group.cpp new file mode 100644 index 00000000..8aa381fc --- /dev/null +++ b/options/group.cpp @@ -0,0 +1,114 @@ +#include "group.hpp" +#include "defs.hpp" +#include <QStandardPaths> +#include <QDir> +#include <QDebug> + +namespace options { + +group::group(const QString& name) : name(name) +{ +    if (name == "") +        return; + +    auto conf = ini_file(); +    conf->beginGroup(name); +    for (auto& k_ : conf->childKeys()) +    { +        auto tmp = k_.toUtf8(); +        QString k(tmp); +        kvs[k] = conf->value(k_); +    } +    conf->endGroup(); +} + +void group::save() const +{ +    if (name == "") +        return; + +    auto s = ini_file(); +    s->beginGroup(name); +    for (auto& i : kvs) +        s->setValue(i.first, i.second); +    s->endGroup(); +} + +void group::put(const QString &s, const QVariant &d) +{ +    kvs[s] = d; +} + +bool group::contains(const QString &s) const +{ +    return kvs.count(s) != 0; +} + +QString group::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 ""; +} + +QString group::ini_filename() +{ +    QSettings settings(OPENTRACK_ORG); +    return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); +} + +QString group::ini_pathname() +{ +    const auto dir = ini_directory(); +    if (dir == "") +        return ""; +    return dir + "/" + ini_filename(); +} + +const 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; +} + +const std::shared_ptr<QSettings> group::ini_file() +{ +    const auto pathname = ini_pathname(); +    if (pathname != "") +        return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat); +    return std::make_shared<QSettings>(); +} + +bool group::operator==(const group& other) const +{ +    for (const auto& kv : kvs) +    { +        const QVariant val = other.get<QVariant>(kv.first); +        if (!other.contains(kv.first) || kv.second != val) +        { +            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << val << "<>" << kv.second; +            return false; +        } +    } + +    for (const auto& kv : other.kvs) +    { +        const QVariant val = get<QVariant>(kv.first); +        if (!contains(kv.first) || kv.second != val) +        { +            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << kv.second << "<>" << val; +            return false; +        } +    } +    return true; +} + +} diff --git a/options/group.hpp b/options/group.hpp new file mode 100644 index 00000000..0deddfa8 --- /dev/null +++ b/options/group.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "export.hpp" +#include <map> +#include <memory> +#include <QString> +#include <QList> +#include <QVariant> +#include <QSettings> + +namespace options { + +// snapshot of qsettings group at given time +class OPENTRACK_OPTIONS_EXPORT group final +{ +private: +    std::map<QString, QVariant> kvs; +    QString name; +public: +    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 const QStringList ini_list(); +    static const std::shared_ptr<QSettings> ini_file(); +    bool operator==(const group& other) const; +    bool operator!=(const group& other) const { return !(*this == other); } + +    template<typename t> +    t get(const QString& k) const +    { +        auto value = kvs.find(k); +        if (value != kvs.cend()) +            return value->second.value<t>(); +        return t(); +    } +}; + +} diff --git a/options/metatype.cpp b/options/metatype.cpp new file mode 100644 index 00000000..8f92f385 --- /dev/null +++ b/options/metatype.cpp @@ -0,0 +1,23 @@ +#include "slider.hpp" +#include "metatype.hpp" + +#define OPENTRACK_REGISTER_METATYPE(t) ::options::detail::custom_type_initializer::declare_for_type<t>(#t) + +namespace options { +namespace detail { + +custom_type_initializer::custom_type_initializer() +{ +    OPENTRACK_REGISTER_METATYPE(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>); +} + +const custom_type_initializer custom_type_initializer::singleton; + +} +} diff --git a/options/metatype.hpp b/options/metatype.hpp new file mode 100644 index 00000000..68480b55 --- /dev/null +++ b/options/metatype.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <QMetaType> +#include <QList> +#include <QString> +#include <QPointF> +#include <QDataStream> +#include <QDebug> + +#include "export.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>) + +namespace options { +namespace detail { + +struct custom_type_initializer final +{ +    static const custom_type_initializer singleton; + +    custom_type_initializer(); + +    template<typename t> static inline void declare_for_type(const char* str) +    { +        qRegisterMetaType<t>(str); +        qRegisterMetaTypeStreamOperators<t>(); +    } +}; + +} +} diff --git a/options/options.hpp b/options/options.hpp new file mode 100644 index 00000000..ebc480f3 --- /dev/null +++ b/options/options.hpp @@ -0,0 +1,16 @@ +/* Copyright (c) 2013-2016 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 "defs.hpp" +#include "bundle.hpp" +#include "group.hpp" +#include "slider.hpp" +#include "value.hpp" +#include "tie.hpp" +#include "metatype.hpp" +#include "scoped.hpp" diff --git a/options/scoped.cpp b/options/scoped.cpp new file mode 100644 index 00000000..e1ddbae2 --- /dev/null +++ b/options/scoped.cpp @@ -0,0 +1,14 @@ +#include "scoped.hpp" + +namespace options { + +opts::~opts() +{ +    b->reload(); +} + +opts::opts(const QString &name) : b(make_bundle(name)) +{ +} + +} diff --git a/options/scoped.hpp b/options/scoped.hpp new file mode 100644 index 00000000..52ae383d --- /dev/null +++ b/options/scoped.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "bundle.hpp" +#include <QString> + +#include "export.hpp" + +namespace options { + +struct OPENTRACK_OPTIONS_EXPORT opts +{ +    bundle b; +    opts(const QString& name); +    opts& operator=(const opts&) = delete; +    opts(const opts&) = delete; +    ~opts(); +}; + +} diff --git a/compat/slider.cpp b/options/slider.cpp index be1aaeb8..8d46d51b 100644 --- a/compat/slider.cpp +++ b/options/slider.cpp @@ -3,7 +3,7 @@  namespace options { -slider_value::slider_value(double cur, double min, double max) : +slider_value::slider_value(float cur, float min, float max) :      cur_(cur),      min_(min),      max_(max) @@ -20,7 +20,9 @@ slider_value::slider_value(const slider_value& v) : slider_value(v.cur(), v.min(  {  } -slider_value::slider_value() : slider_value(0, 0, 0) + + +slider_value::slider_value() : slider_value(0.f, 0.f, 0.f)  {  } @@ -38,7 +40,7 @@ bool slider_value::operator==(const slider_value& v) const  {      using std::fabs; -    static constexpr double eps = 1e-3; +    static constexpr float eps = 1e-3f;      return (fabs(v.cur_ - cur_) < eps &&              fabs(v.min_ - min_) < eps && diff --git a/compat/slider.hpp b/options/slider.hpp index 7d54c650..1d02f17f 100644 --- a/compat/slider.hpp +++ b/options/slider.hpp @@ -14,19 +14,26 @@  namespace options  { -    class OPENTRACK_COMPAT_EXPORT slider_value final +    class OPENTRACK_OPTIONS_EXPORT slider_value final      { -        double cur_, min_, max_; +        float cur_, min_, max_;      public: -        slider_value(double cur, double min, double max); +        slider_value(float cur, float min, float max); + +        template<typename t, typename u, typename v> slider_value(t cur, u min, v max) : +            cur_(float(cur)), +            min_(float(min)), +            max_(float(max)) +        {} +          slider_value(const slider_value& v);          slider_value();          slider_value& operator=(const slider_value& v);          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_; } +        operator float() const { return cur_; } +        double cur() const { return double(cur_); } +        double min() const { return double(min_); } +        double max() const { return double(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;      }; @@ -41,15 +48,15 @@ inline QDebug operator << (QDebug dbg, const options::slider_value& val)  inline QDataStream& operator << (QDataStream& out, const options::slider_value& v)  { -    out << v.cur() -        << v.min() -        << v.max(); +    out << float(v.cur()) +        << float(v.min()) +        << float(v.max());      return out;  }  inline QDataStream& operator >> (QDataStream& in, options::slider_value& v)  { -    double cur, min, max; +    float cur, min, max;      in >> cur;      in >> min;      in >> max; diff --git a/options/tie.hpp b/options/tie.hpp new file mode 100644 index 00000000..2969f8d4 --- /dev/null +++ b/options/tie.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include "value.hpp" + +#include <QComboBox> +#include <QCheckBox> +#include <QDoubleSpinBox> +#include <QSpinBox> +#include <QSlider> +#include <QLineEdit> +#include <QLabel> +#include <QTabWidget> + +#include <cmath> + +namespace options { + +template<typename t, typename q> +inline void tie_setting(value<t>&, q*); + +template<typename t> +inline +typename std::enable_if<std::is_enum<t>::value>::type +tie_setting(value<t>& v, QComboBox* cb) +{ +    cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v))); +    v = static_cast<t>(cb->currentData().toInt()); + +    // QObject::connect plays badly with std::bind of std::shared_ptr. Data seems to get freed. +    // Direct accesses of cb->currentData within arbitrary thread context cause crashes as well. +    // Hence we go for a verbose implementation. + +    std::vector<int> enum_cases; +    enum_cases.reserve(unsigned(cb->count())); + +    for (int i = 0; i < cb->count(); i++) +        enum_cases.push_back(cb->itemData(i).toInt()); + +    struct fn1 +    { +        value<t>& v; +        QComboBox* cb; +        std::vector<int> enum_cases; + +        fn1(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) : +            v(v), +            cb(cb), +            enum_cases(enum_cases) +        { +        } + +        void operator()(int idx) +        { +            if (idx < 0 || idx >= (int)enum_cases.size()) +                v = static_cast<t>(-1); +            else +                v = static_cast<t>(t(std::intptr_t(enum_cases[idx]))); +        } +    }; + +    struct fn2 +    { +        value<t>& v; +        QComboBox* cb; +        std::vector<int> enum_cases; + +        fn2(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) : +            v(v), +            cb(cb), +            enum_cases(enum_cases) +        { +        } + +        void operator()(int val) +        { +            for (unsigned i = 0; i < enum_cases.size(); i++) +            { +                if (val == enum_cases[i]) +                { +                    cb->setCurrentIndex(i); +                    return; +                } +            } +            cb->setCurrentIndex(-1); +        } +    }; + +    base_value::connect(cb, +                        static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), +                        &v, +                        fn1(v, cb, enum_cases), +                        v.DIRECT_CONNTYPE); +    base_value::connect(&v, +                        static_cast<void (base_value::*)(int)>(&base_value::valueChanged), +                        cb, +                        fn2(v, cb, enum_cases), +                        v.DIRECT_CONNTYPE); +} + +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.DIRECT_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); +} + +template<> +inline void tie_setting(value<slider_value>& v, QSlider* w) +{ +    // we can't get these at runtime since signals cross threads +    const int q_min = w->minimum(); +    const int q_max = w->maximum(); +    const int q_diff = q_max - q_min; + +    slider_value sv(v); + +    const double sv_c = sv.max() - sv.min(); + +    using std::round; + +    w->setValue(int((sv.cur() - sv.min()) / sv_c * q_diff + q_min)); +    v = slider_value(q_diff <= 0 ? 0 : (w->value() - q_min) * sv_c / (double)q_diff + sv.min(), sv.min(), sv.max()); + +    base_value::connect(w, +                        &QSlider::valueChanged, +                        &v, +                        [=, &v](int pos) { +                                const int q_diff = q_max - q_min; +                                if (q_diff <= 0 || pos <= 0) +                                    v = slider_value(sv.min(), sv.min(), sv.max()); +                                else +                                { +                                    const float c = float(sv.max()-sv.min()); +                                    v = slider_value((pos - q_min) * c / (float)q_diff + float(sv.min()), sv.min(), sv.max()); +                                } +                        }, +                        v.DIRECT_CONNTYPE); +    base_value::connect(&v, +                        static_cast<void(base_value::*)(double)>(&base_value::valueChanged), +                        w, +                        [=](double value) { w->setValue(int(round(value * q_diff) + q_min)); }, +    v.SAFE_CONNTYPE); +} + +} diff --git a/options/value.hpp b/options/value.hpp new file mode 100644 index 00000000..4d8d69f8 --- /dev/null +++ b/options/value.hpp @@ -0,0 +1,167 @@ +#pragma once + +#include "bundle.hpp" +#include "slider.hpp" +#include <type_traits> +#include <QString> +#include <QPointF> +#include <QList> + +#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); } +#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t) +namespace options { + +namespace detail { +template<typename t> struct value_type_traits { using type = t;}; +template<> struct value_type_traits<QString> { using type = const QString&; }; +template<> struct value_type_traits<slider_value> { using type = const slider_value&; }; +template<typename u> struct value_type_traits<QList<u>> +{ +    using type = const QList<typename std::remove_const<typename std::remove_reference<u>::type>::type>&; +}; +template<typename t> using value_type_t = typename value_type_traits<t>::type; +} + +class OPENTRACK_OPTIONS_EXPORT base_value : public QObject +{ +    Q_OBJECT +public: +    QString name() const { return self_name; } +    base_value(bundle b, const QString& name) : b(b), self_name(name) {} +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 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>&); +protected: +    bundle b; +    QString self_name; + +    template<typename t> +    void store(const t& datum) +    { +        b->store_kv(self_name, QVariant::fromValue(datum)); +        emit valueChanged(static_cast<detail::value_type_t<t>>(datum)); +    } + +    void store(float datum) +    { +        store(double(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 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>&) +    public slots: +        virtual void reload() = 0; +}; + +namespace detail { +template<typename t> +struct value_get_traits +{ +    static inline t get(const t& val, const t&) +    { +        return val; +    } +}; + +template<> +struct value_get_traits<slider_value> +{ +    using t = slider_value; +    static inline t get(const t& val, const t& def) +    { +        return t(val.cur(), def.min(), def.max()); +    } +}; + +template<typename t, typename Enable = void> +struct value_element_type +{ +    using type = typename std::remove_reference<typename std::remove_cv<t>::type>::type; +}; + +// Qt uses int a lot in slots so use it for all enums +template<typename t> +struct value_element_type<t, typename std::enable_if<std::is_enum<t>::value>::type> +{ +    using type = int; +}; + +template<typename t> using value_element_type_t = typename value_element_type<t>::type; + +} + +template<typename t> +class value final : public base_value +{ +public: +    using element_type = detail::value_element_type_t<t>; + +    t operator=(const t& datum) +    { +        store(static_cast<element_type>(datum)); +        return datum; +    } + +    static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::AutoConnection; +    static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection; + +    value(bundle b, const QString& 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(bundle b, const char* name, t def) : value(b, QString(name), def) +    { +    } + +    t get() const +    { +        t val = b->contains(self_name) +                ? static_cast<t>(b->get<element_type>(self_name)) +                : def; +        return detail::value_get_traits<t>::get(val, def); +    } + +    operator t() const { return get(); } + +    void reload() override +    { +        *this = static_cast<t>(*this); +    } + +private: +    t def; +}; + + +} diff --git a/proto-fg/ftnoir_protocol_fg.h b/proto-fg/ftnoir_protocol_fg.h index 77cb73c8..3cacf1a5 100644 --- a/proto-fg/ftnoir_protocol_fg.h +++ b/proto-fg/ftnoir_protocol_fg.h @@ -15,7 +15,7 @@  #include <QUdpSocket>  #include <QMessageBox>  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc.h b/proto-fsuipc/ftnoir_protocol_fsuipc.h index 8207ac9f..d17527ae 100644 --- a/proto-fsuipc/ftnoir_protocol_fsuipc.h +++ b/proto-fsuipc/ftnoir_protocol_fsuipc.h @@ -24,7 +24,7 @@  #include <QDebug>  #include <QFile>  #include <QFileDialog> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  #define FSUIPC_FILENAME "C:\\Program Files\\Microsoft Games\\Flight Simulator 9\\Modules\\FSUIPC.dll" diff --git a/proto-ft/ftnoir_protocol_ft.h b/proto-ft/ftnoir_protocol_ft.h index 246a73c4..546cb0e5 100644 --- a/proto-ft/ftnoir_protocol_ft.h +++ b/proto-ft/ftnoir_protocol_ft.h @@ -19,7 +19,7 @@  #include <QMutex>  #include <QMutexLocker>  #include "compat/shm.h" -#include "compat/options.hpp" +#include "options/options.hpp"  #include "freetrackclient/fttypes.h"  #include <memory>  #include "mutex.hpp" diff --git a/proto-ftn/ftnoir_protocol_ftn.h b/proto-ftn/ftnoir_protocol_ftn.h index 9e647980..74120b87 100644 --- a/proto-ftn/ftnoir_protocol_ftn.h +++ b/proto-ftn/ftnoir_protocol_ftn.h @@ -16,7 +16,7 @@  #include <QMessageBox>  #include <cmath>  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/proto-mouse/ftnoir_protocol_mouse.h b/proto-mouse/ftnoir_protocol_mouse.h index 0781366c..f2880822 100644 --- a/proto-mouse/ftnoir_protocol_mouse.h +++ b/proto-mouse/ftnoir_protocol_mouse.h @@ -10,7 +10,7 @@  #include "ui_ftnoir_mousecontrols.h"  #include <QDebug>  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/proto-sc/ftnoir_protocol_sc.h b/proto-sc/ftnoir_protocol_sc.h index 0da6c242..1b3f5eb8 100644 --- a/proto-sc/ftnoir_protocol_sc.h +++ b/proto-sc/ftnoir_protocol_sc.h @@ -20,7 +20,7 @@  #include <QProcess>  #include <QDebug>  #include <QFile> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  #include <windows.h> diff --git a/spline-widget/CMakeLists.txt b/spline-widget/CMakeLists.txt index 9168fc45..8ef96cc6 100644 --- a/spline-widget/CMakeLists.txt +++ b/spline-widget/CMakeLists.txt @@ -1,2 +1,2 @@  opentrack_boilerplate(opentrack-spline-widget NO-COMPAT BIN) -target_link_libraries(opentrack-spline-widget opentrack-compat) +target_link_libraries(opentrack-spline-widget opentrack-options opentrack-compat) diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h index aeabd438..1dd08cb1 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.h +++ b/tracker-aruco/ftnoir_tracker_aruco.h @@ -26,7 +26,7 @@  #include <cinttypes> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h index 05228006..2fd4baa0 100644 --- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h +++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h @@ -10,7 +10,7 @@  #include <QThread>  #include "ui_freepie-udp-controls.h"  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/tracker-hatire/ftnoir_tracker_hat_settings.h b/tracker-hatire/ftnoir_tracker_hat_settings.h index fa24996e..7e9f7244 100644 --- a/tracker-hatire/ftnoir_tracker_hat_settings.h +++ b/tracker-hatire/ftnoir_tracker_hat_settings.h @@ -8,7 +8,7 @@  #pragma once  #include <QSerialPort> -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options; diff --git a/tracker-ht/ftnoir_tracker_ht.h b/tracker-ht/ftnoir_tracker_ht.h index 96fd258d..a29b3f20 100644 --- a/tracker-ht/ftnoir_tracker_ht.h +++ b/tracker-ht/ftnoir_tracker_ht.h @@ -12,7 +12,7 @@  #include "ht_video_widget.h"  #include "compat/shm.h"  #include <QObject> -#include "compat/options.hpp" +#include "options/options.hpp"  #include "api/plugin-api.hpp"  #include "api/opencv-camera-dialog.hpp" diff --git a/tracker-hydra/ftnoir_tracker_hydra.h b/tracker-hydra/ftnoir_tracker_hydra.h index d8fe6afb..efbcddd6 100644 --- a/tracker-hydra/ftnoir_tracker_hydra.h +++ b/tracker-hydra/ftnoir_tracker_hydra.h @@ -1,6 +1,6 @@  #include "ui_ftnoir_hydra_clientcontrols.h"  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/tracker-joystick/ftnoir_tracker_joystick.h b/tracker-joystick/ftnoir_tracker_joystick.h index 0cd26f36..0af1444e 100644 --- a/tracker-joystick/ftnoir_tracker_joystick.h +++ b/tracker-joystick/ftnoir_tracker_joystick.h @@ -18,7 +18,7 @@  #include "api/plugin-api.hpp"  #include "dinput/win32-joystick.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { diff --git a/tracker-pt/ftnoir_tracker_pt_settings.h b/tracker-pt/ftnoir_tracker_pt_settings.h index c076082a..8c86fc14 100644 --- a/tracker-pt/ftnoir_tracker_pt_settings.h +++ b/tracker-pt/ftnoir_tracker_pt_settings.h @@ -28,7 +28,7 @@ struct pt_types      using mat22 = mat<2, 2>;  }; -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings_pt : opts diff --git a/tracker-rift-025/ftnoir_tracker_rift_025.h b/tracker-rift-025/ftnoir_tracker_rift_025.h index 33f615ea..20b88a28 100644 --- a/tracker-rift-025/ftnoir_tracker_rift_025.h +++ b/tracker-rift-025/ftnoir_tracker_rift_025.h @@ -1,7 +1,7 @@  #pragma once  #include "ui_ftnoir_rift_clientcontrols_025.h"  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include <OVR.h>  #include <cmath>  #include <memory> diff --git a/tracker-rift-042/ftnoir_tracker_rift_042.h b/tracker-rift-042/ftnoir_tracker_rift_042.h index 55a59f18..384df4f9 100644 --- a/tracker-rift-042/ftnoir_tracker_rift_042.h +++ b/tracker-rift-042/ftnoir_tracker_rift_042.h @@ -1,7 +1,7 @@  #pragma once  #include "ui_ftnoir_rift_clientcontrols_042.h"  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include <OVR.h>  #include <QMessageBox>  #include <QWaitCondition> diff --git a/tracker-rift-080/ftnoir_tracker_rift_080.h b/tracker-rift-080/ftnoir_tracker_rift_080.h index 6adf6bcf..a7fa1c55 100644 --- a/tracker-rift-080/ftnoir_tracker_rift_080.h +++ b/tracker-rift-080/ftnoir_tracker_rift_080.h @@ -1,7 +1,7 @@  #pragma once  #include "ui_ftnoir_rift_clientcontrols_080.h"  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  #include <OVR.h>  #include <cmath>  #include <QMessageBox> diff --git a/tracker-udp/ftnoir_tracker_udp.h b/tracker-udp/ftnoir_tracker_udp.h index 292df1f5..dfebd866 100644 --- a/tracker-udp/ftnoir_tracker_udp.h +++ b/tracker-udp/ftnoir_tracker_udp.h @@ -4,7 +4,7 @@  #include <QThread>  #include <cmath>  #include "api/plugin-api.hpp" -#include "compat/options.hpp" +#include "options/options.hpp"  using namespace options;  struct settings : opts { | 
