diff options
author | Fabien Proriol <fabien.proriol@kazoe.org> | 2025-05-22 17:10:35 +0200 |
---|---|---|
committer | Fabien Proriol <fabien.proriol@kazoe.org> | 2025-05-26 10:48:46 +0200 |
commit | 2feba4447a482840e21fa2d3b33f1a5da12d09b7 (patch) | |
tree | 83a790b1ae5b5f32f5964350856a160dbed52e05 | |
parent | c842548fef050ac5f8b56a5fcb4f579820247434 (diff) |
qt: Add Qt Wrapper library and QML module
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 115 | ||||
-rw-r--r-- | cmake/CPack.cmake | 4 | ||||
-rw-r--r-- | cmake/Config.cmake.in | 6 | ||||
-rw-r--r-- | cmd/kzsettings.cpp (renamed from cmd/ksettings.cpp) | 6 | ||||
-rw-r--r-- | python/bindings.cpp | 38 | ||||
-rw-r--r-- | qt/cmake/Config.cmake.in | 9 | ||||
-rw-r--r-- | qt/kzqproperty.cpp | 64 | ||||
-rw-r--r-- | qt/kzqproperty.h | 38 | ||||
-rw-r--r-- | qt/kzqsettings.cpp | 246 | ||||
-rw-r--r-- | qt/kzqsettings.h | 34 | ||||
-rw-r--r-- | qt/kzqsettings_plugin.cpp | 12 | ||||
-rw-r--r-- | qt/kzqsettings_plugin.h | 15 | ||||
-rw-r--r-- | qt/qml/qmldir | 2 | ||||
-rw-r--r-- | src/changewatcher.cpp | 1 | ||||
-rw-r--r-- | src/kzsettings.cpp (renamed from src/settings.cpp) | 66 | ||||
-rw-r--r-- | src/kzsettings.h (renamed from src/settings.h) | 32 |
17 files changed, 589 insertions, 102 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3897b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build* +CMakeLists.txt.user* + diff --git a/CMakeLists.txt b/CMakeLists.txt index acb7c5d..9d06877 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,34 +1,38 @@ cmake_minimum_required(VERSION 3.14) -project(KaZoe-Settings VERSION 1.0.0 LANGUAGES CXX) +project(KzSettings VERSION 1.0.0 LANGUAGES CXX) -option(WITH_PYTHON "Create python binding" ON) +option(WITH_PYTHON "Create python binding" ON) +option(WITH_QT "Create Qt6 library and QML Module" ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +include(GNUInstallDirs) find_package (Threads REQUIRED) -add_library(KaZoeSettings SHARED - src/settings.h src/settings.cpp +add_library(KzSettings SHARED + src/kzsettings.h src/kzsettings.cpp src/changewatcher.h src/changewatcher.cpp + cmake/Config.cmake.in + cmake/CPack.cmake ) -set_target_properties(KaZoeSettings PROPERTIES VERSION ${CMAKE_PROJECT_VERSION} SOVERSION 1) -set_target_properties(KaZoeSettings PROPERTIES PUBLIC_HEADER "src/settings.h") -target_link_libraries(KaZoeSettings PRIVATE Threads::Threads) +set_target_properties(KzSettings PROPERTIES VERSION ${CMAKE_PROJECT_VERSION} SOVERSION 1) +set_target_properties(KzSettings PROPERTIES PUBLIC_HEADER "src/kzsettings.h") +target_link_libraries(KzSettings PRIVATE Threads::Threads) -add_executable(ksettings cmd/ksettings.cpp) -target_include_directories(ksettings PRIVATE src) -target_link_libraries(ksettings KaZoeSettings) +add_executable(kzsettings cmd/kzsettings.cpp) +target_include_directories(kzsettings PRIVATE src) +target_link_libraries(kzsettings KzSettings) -install(TARGETS KaZoeSettings - EXPORT "KaZoeSettingsTargets" +install(TARGETS KzSettings + EXPORT "KzSettingsTargets" LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_PREFIX}/include/KaZoe/" ) -install(TARGETS ksettings +install(TARGETS kzsettings LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) @@ -39,33 +43,94 @@ if(WITH_PYTHON) set(PYTHON_MODULE_EXTENSION ".so" CACHE INTERNAL "Cross python lib extension") find_package(pybind11 REQUIRED) include_directories(${Python3_INCLUDE_DIRS}) - pybind11_add_module(PyKaZoeSettings python/bindings.cpp) - set_target_properties(PyKaZoeSettings PROPERTIES OUTPUT_NAME "KaZoeSettings") - target_link_libraries(PyKaZoeSettings PUBLIC KaZoeSettings ${PYTHON_LIBRARY}) - install(TARGETS PyKaZoeSettings LIBRARY DESTINATION ${Python3_SITEARCH}) + pybind11_add_module(PyKzSettings python/bindings.cpp) + set_target_properties(PyKzSettings PROPERTIES OUTPUT_NAME "KzSettings") + target_link_libraries(PyKzSettings PUBLIC KzSettings ${PYTHON_LIBRARY}) + if(NOT PYTHONPATH) + set(PYTHONPATH ${Python3_SITEARCH}) + endif(NOT PYTHONPATH) + install(TARGETS PyKzSettings LIBRARY DESTINATION ${PYTHONPATH}) endif(WITH_PYTHON) -# CMake Module include(CMakePackageConfigHelpers) + +if(WITH_QT) + if(NOT DEFINED QML_MODULE_INSTALL_PATH) + if(DEFINED OE_QMAKE_PATH_QML) + set(QML_MODULE_INSTALL_PATH ${OE_QMAKE_PATH_QML} ) + else() + set(QML_MODULE_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/qml ) + endif() + endif() + message(STATUS "Build with Qt module in ${QML_MODULE_INSTALL_PATH}") + + find_package(Qt6 COMPONENTS Core Qml REQUIRED) + add_library(KzQSettings SHARED + qt/kzqsettings.h qt/kzqsettings.cpp + qt/kzqproperty.h qt/kzqproperty.cpp + qt/cmake/Config.cmake.in + ) + target_link_libraries(KzQSettings PUBLIC Qt6::Core KzSettings) + set_target_properties(KzQSettings PROPERTIES VERSION ${CMAKE_PROJECT_VERSION} SOVERSION 1) + set(KzQSettingsPublicHeader qt/kzqsettings.h qt/kzqproperty.h) + set_target_properties(KzQSettings PROPERTIES PUBLIC_HEADER "${KzQSettingsPublicHeader}") + install(TARGETS KzQSettings + EXPORT "KzQSettingsTargets" + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_PREFIX}/include/KaZoe/" + ) + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/generated/KzQSettingsConfigVersion.cmake" COMPATIBILITY SameMajorVersion + ) + + configure_package_config_file( + "qt/cmake/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/generated/KzQSettingsConfig.cmake" + INSTALL_DESTINATION "lib/cmake/KzQSettings" + ) + + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/KzQSettingsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/generated/KzQSettingsConfigVersion.cmake" + DESTINATION "lib/cmake/KzQSettings" + ) + + install( + EXPORT "KzQSettingsTargets" + DESTINATION "lib/cmake/KzQSettings" + ) + + set(PLUGIN_SOURCES + qt/kzqsettings_plugin.h qt/kzqsettings_plugin.cpp + qt/qml/qmldir + ) + + add_library(KzQSettingsPlugin SHARED ${PLUGIN_SOURCES}) + target_link_libraries(KzQSettingsPlugin PRIVATE Qt6::Qml KzQSettings) + install(TARGETS KzQSettingsPlugin DESTINATION ${QML_MODULE_INSTALL_PATH}/org/kazoe/settings) + install(FILES qt/qml/qmldir DESTINATION ${QML_MODULE_INSTALL_PATH}/org/kazoe/settings) +endif(WITH_QT) + + write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/generated/KaZoeSettingsConfigVersion.cmake" COMPATIBILITY SameMajorVersion + "${CMAKE_CURRENT_BINARY_DIR}/generated/KzSettingsConfigVersion.cmake" COMPATIBILITY SameMajorVersion ) configure_package_config_file( "cmake/Config.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/generated/KaZoeSettingsConfig.cmake" - INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/generated/KzSettingsConfig.cmake" + INSTALL_DESTINATION "lib/cmake/KzSettings" ) install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/KaZoeSettingsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/generated/KaZoeSettingsConfigVersion.cmake" - DESTINATION "lib/cmake/${PROJECT_NAME}" + FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/KzSettingsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/generated/KzSettingsConfigVersion.cmake" + DESTINATION "lib/cmake/KzSettings" ) install( - EXPORT "KaZoeSettingsTargets" - DESTINATION "lib/cmake/${PROJECT_NAME}" + EXPORT "KzSettingsTargets" + DESTINATION "lib/cmake/KzSettings" ) include(cmake/CPack.cmake) diff --git a/cmake/CPack.cmake b/cmake/CPack.cmake index 33169b1..b8b9f75 100644 --- a/cmake/CPack.cmake +++ b/cmake/CPack.cmake @@ -1,8 +1,8 @@ set(CPACK_GENERATOR "DEB") -set(CPACK_PACKAGE_NAME "KaZoe-Settings") +set(CPACK_PACKAGE_NAME "KzSettings") set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3") set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) set(CPACK_PACKAGE_VERSION_PATCH "${CMAKE_PROJECT_VERSION_PATCH}") -set(CPACK_PACKAGE_DESCRIPTION "KaZoe library") +set(CPACK_PACKAGE_DESCRIPTION "KaZoe Settings library") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Fabien Proriol <fabien.proriol@kazoe.org>") include(CPack) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index efbb7cc..cc295f1 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -1,7 +1,7 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/KaZoeSettingsTargets.cmake") -set(LIBKAZOESETTINGS_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include/@PROJECT_NAME@") -set(LIBKAZOESETTINGS_LIBRARIES "KaZoeSettings" ) +include("${CMAKE_CURRENT_LIST_DIR}/KzSettingsTargets.cmake") +set(LIBKZSETTINGS_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include/KaZoe") +set(LIBKZSETTINGS_LIBRARIES "KzSettings" ) check_required_components("@PROJECT_NAME@") diff --git a/cmd/ksettings.cpp b/cmd/kzsettings.cpp index 0d29dab..2484e1d 100644 --- a/cmd/ksettings.cpp +++ b/cmd/kzsettings.cpp @@ -1,17 +1,17 @@ #include <iostream> -#include <settings.h> +#include <kzsettings.h> using namespace std; int main(int argc, char *argv[]) { - KaZoe::Settings settings; + KaZoe::KzSettings settings; if(argc < 2) { std::cout << "Current settings:" << std::endl; - settings.forEach([](const SettingKey& key, const SettingValue& value) { + settings.forEach([](const KzSettingKey& key, const KzSettingValue& value) { std::string head; if(!key.first.empty()) { diff --git a/python/bindings.cpp b/python/bindings.cpp index 5c816f6..acce65b 100644 --- a/python/bindings.cpp +++ b/python/bindings.cpp @@ -1,65 +1,65 @@ #include <pybind11/pybind11.h> #include <pybind11/stl.h> -#include "../src/settings.h" +#include "../src/kzsettings.h" namespace py = pybind11; using namespace pybind11::literals; -class PyKaZoeSettings { - KaZoe::Settings m_settings; +class KzPySettings { + KaZoe::KzSettings m_settings; public: - PyKaZoeSettings(); + KzPySettings(); std::string __repr__(); static void add_python_binding(pybind11::module &m); - SettingValue get(std::string key, std::string category, SettingValue def); - SettingValue set(std::string key, SettingValue value, std::string category); + KzSettingValue get(std::string key, std::string category, KzSettingValue def); + KzSettingValue set(std::string key, KzSettingValue value, std::string category); }; -PyKaZoeSettings::PyKaZoeSettings() { +KzPySettings::KzPySettings() { py::module sys = py::module::import("sys"); py::list argv = sys.attr("argv"); m_settings.setId(argv[0].cast<std::string>()); } -SettingValue PyKaZoeSettings::get(std::string key, std::string category, SettingValue def) { +KzSettingValue KzPySettings::get(std::string key, std::string category, KzSettingValue def) { return m_settings.get(key, category, def); } -SettingValue PyKaZoeSettings::set(std::string key, SettingValue value, std::string category) { +KzSettingValue KzPySettings::set(std::string key, KzSettingValue value, std::string category) { return m_settings.set(key, value, category); } -std::string PyKaZoeSettings::__repr__() { +std::string KzPySettings::__repr__() { std::string result = "KaZoeSettings"; return result; } -void PyKaZoeSettings::add_python_binding(pybind11::module &m) +void KzPySettings::add_python_binding(pybind11::module &m) { - py::class_<PyKaZoeSettings>(m, "KaZoeSettings") + py::class_<KzPySettings>(m, "KzSettings") .def(py::init<>()) - .def("get", [] (PyKaZoeSettings &m, std::string key, std::string category = "", SettingValue def = SettingValue()) { + .def("get", [] (KzPySettings &m, std::string key, std::string category = "", KzSettingValue def = KzSettingValue()) { return m.get(key, category, def); }, py::arg("key"), py::arg("category") = "", - py::arg("default") = SettingValue()) - .def("set", [] (PyKaZoeSettings &m, std::string key, SettingValue value, std::string category = "") { + py::arg("default") = KzSettingValue()) + .def("set", [] (KzPySettings &m, std::string key, KzSettingValue value, std::string category = "") { return m.set(key, value, category); }, py::arg("key"), py::arg("value"), py::arg("category") = "") - .def("__repr__", &PyKaZoeSettings::__repr__); + .def("__repr__", &KzPySettings::__repr__); } -PYBIND11_MODULE(KaZoeSettings, m) { +PYBIND11_MODULE(KzSettings, m) { m.doc() = R"pbdoc( - Python bindings for KaZoeSettings + Python bindings for KzSettings )pbdoc"; - PyKaZoeSettings::add_python_binding(m); + KzPySettings::add_python_binding(m); } diff --git a/qt/cmake/Config.cmake.in b/qt/cmake/Config.cmake.in new file mode 100644 index 0000000..a0d89fc --- /dev/null +++ b/qt/cmake/Config.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") +set(LIBKZQSETTINGS_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/KaZoe") +find_package(Qt6 COMPONENTS Core REQUIRED) +find_package(KzSettings REQUIRED) +set(LIBKZQSETTINGS_LIBRARIES "KzQSettings" "Qt6::Core" "${LIBKZSETTINGS_LIBRARIES}") + +check_required_components("@PROJECT_NAME@") diff --git a/qt/kzqproperty.cpp b/qt/kzqproperty.cpp new file mode 100644 index 0000000..4290e8c --- /dev/null +++ b/qt/kzqproperty.cpp @@ -0,0 +1,64 @@ +#include "kzqproperty.h" +#include "kzqsettings.h" + +namespace KaZoe { +class KzQPropertyPrivate +{ + Q_DISABLE_COPY(KzQPropertyPrivate) + Q_DECLARE_PUBLIC(KaZoe::KzQProperty) + + KaZoe::KzQProperty * const q_ptr; + QString m_key; + QVariant m_value; + KaZoe::KzQSettings m_settings; + + KzQPropertyPrivate(KaZoe::KzQProperty* systemprop): q_ptr(systemprop){} +}; +}; + +KaZoe::KzQProperty::KzQProperty(QObject *parent) + : QObject{parent} + , d_ptr(new KaZoe::KzQPropertyPrivate(this)) +{ + Q_D(KzQProperty); + QObject::connect(&d->m_settings, &KaZoe::KzQSettings::valueChanged, [this](QString id, QVariant value){ + Q_D(KzQProperty); + if(id == d->m_key) + { + setValue(value); + } + }); +} + +KaZoe::KzQProperty::~KzQProperty() = default; + +QString KaZoe::KzQProperty::key() const +{ + Q_D(const KzQProperty); + return d->m_key; +} + +void KaZoe::KzQProperty::setKey(const QString &newKey) +{ + Q_D(KzQProperty); + if (d->m_key == newKey) + return; + d->m_key = newKey; + setValue(d->m_settings.get(d->m_key)); + emit keyChanged(); +} + +QVariant KaZoe::KzQProperty::value() const +{ + Q_D(const KzQProperty); + return d->m_value; +} + +void KaZoe::KzQProperty::setValue(const QVariant &newValue) +{ + Q_D(KzQProperty); + if (d->m_value == newValue) + return; + d->m_value = newValue; + emit valueChanged(); +} diff --git a/qt/kzqproperty.h b/qt/kzqproperty.h new file mode 100644 index 0000000..8bd1b08 --- /dev/null +++ b/qt/kzqproperty.h @@ -0,0 +1,38 @@ +#ifndef KZQPROPERTY_H +#define KZQPROPERTY_H + +#include <QObject> +#include <QVariant> +#include <QScopedPointer> + +namespace KaZoe { + +class KzQPropertyPrivate; +class KzQProperty : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit KzQProperty(QObject *parent = nullptr); + ~KzQProperty(); + + QString key() const; + void setKey(const QString &newKey); + + QVariant value() const; + void setValue(const QVariant &newValue); + +signals: + void keyChanged(); + void valueChanged(); + +private: + Q_DISABLE_COPY(KzQProperty) + Q_DECLARE_PRIVATE(KzQProperty) + QScopedPointer<KzQPropertyPrivate> d_ptr; +}; +}; + +#endif // KZQPROPERTY_H diff --git a/qt/kzqsettings.cpp b/qt/kzqsettings.cpp new file mode 100644 index 0000000..9ad4674 --- /dev/null +++ b/qt/kzqsettings.cpp @@ -0,0 +1,246 @@ +#include "kzqsettings.h" +#include "../src/kzsettings.h" +#include <qdebug.h> + +class QFileSystemWatcher; + + +QVariant toQVariant(const KzSettingValue &value, QVariant defvalue = QVariant()) +{ + if(std::holds_alternative<std::string>(value)) + { + return QString::fromStdString(std::get<std::string>(value)); + } + if(std::holds_alternative<int>(value)) + { + return std::get<int>(value); + } + if(std::holds_alternative<bool>(value)) + { + return std::get<bool>(value); + } + if(std::holds_alternative<double>(value)) + { + return std::get<double>(value); + } + return QVariant(defvalue); +} + +KzSettingValue fromQVariant(const QVariant &value) +{ + if(value.isNull()) + return KzSettingValue(); + + switch (value.userType()) + { + case QMetaType::Int: + return value.value<int>(); + + case QMetaType::QString: + return value.toString().toStdString(); + + case QMetaType::Bool: + return value.value<bool>(); + + case QMetaType::Double: + return value.value<double>(); + + default: + return false; + } +} + +QString toKeyCombined(const std::string& category, const std::string& key) +{ + QString keysig; + if(category.size()) + { + keysig.append("["); + keysig.append(QString::fromStdString(category)); + keysig.append("]"); + } + keysig.append(QString::fromStdString(key)); + return keysig; +} + +KaZoe::KzQSettings::KzQSettings(QObject *parent) + : QObject(parent) + , m_settings(new KaZoe::KzSettings()) +{ + m_settings->setNotifier([this](const std::string& category, const std::string& key, KzSettingValue value) { + emit valueChanged(toKeyCombined(category, key), toQVariant(value)); + }); +} + +KaZoe::KzQSettings::~KzQSettings() +{ +} + +QVariant KaZoe::KzQSettings::getValue(const QString &id, QVariant defvalue) +{ + KaZoe::KzSettings settings; + + QString category; + QString key = id; + if(id.contains("/")) + { + qWarning() << "OLD Syntax used for " << id; + QStringList tab = id.split("/"); + category = tab[0]; + tab.removeFirst(); + key = tab.join("/"); + } + + KzSettingValue value = settings.get(key.toStdString(), category.toStdString(), fromQVariant(defvalue)); + return toQVariant(value); +} + +bool KaZoe::KzQSettings::setValue(const QString &id, const QVariant &value) +{ + KaZoe::KzSettings settings; + bool ret; + QString key = id; + QString category; + + if(id.contains("/")) + { + qWarning() << "OLD Syntax used for " << id; + QStringList tab = id.split("/"); + category = tab[0]; + tab.removeFirst(); + key = tab.join("/"); + } + + switch (value.userType()) + { + case QMetaType::Int: + ret = settings.set(key.toStdString(), value.value<int>(), category.toStdString()); + break; + + case QMetaType::QString: + ret = settings.set(key.toStdString(), value.toString().toStdString(), category.toStdString()); + break; + + case QMetaType::Bool: + ret = settings.set(key.toStdString(), value.value<bool>(), category.toStdString()); + break; + + case QMetaType::Double: + ret = settings.set(key.toStdString(), value.value<double>(), category.toStdString()); + break; + default: + ret = false; + break; + } + + if(!ret) + { + qWarning() << "Settings ERROR: Write access not permitted for" << id << " with " << QString::fromStdString(settings.id()); + return false; + } + return true; +} + + +int KaZoe::KzQSettings::count() const +{ + if(m_settings) return m_settings->size(); + qWarning() << "Invalid object state for count"; + return -1; +} + +QVariant KaZoe::KzQSettings::get(const QString &id, const QVariant defvalue) const +{ + if(!m_settings) + { + qWarning() << "Invalid object state for get"; + return QVariant(); + } + + QString category; + QString key = id; + if(id.contains("/")) + { + qWarning() << "OLD Syntax used for " << id; + QStringList tab = id.split("/"); + category = tab[0]; + tab.removeFirst(); + key = tab.join("/"); + } + + KzSettingValue value = m_settings->get(key.toStdString(), category.toStdString(), fromQVariant(defvalue)); + return toQVariant(value); +} + +bool KaZoe::KzQSettings::set(const QString &id, const QVariant &value) +{ + if(!m_settings) + { + qWarning() << "Invalid object state for set"; + return false; + } + bool ret; + QString key = id; + QString category; + + if(id.contains("/")) + { + qWarning() << "OLD Syntax used for " << id; + QStringList tab = id.split("/"); + category = tab[0]; + tab.removeFirst(); + key = tab.join("/"); + } + + switch (value.userType()) + { + case QMetaType::Int: + ret = m_settings->set(key.toStdString(), value.value<int>(), category.toStdString()); + break; + + case QMetaType::QString: + ret = m_settings->set(key.toStdString(), value.toString().toStdString(), category.toStdString()); + break; + + case QMetaType::Bool: + ret = m_settings->set(key.toStdString(), value.value<bool>(), category.toStdString()); + break; + + case QMetaType::Double: + ret = m_settings->set(key.toStdString(), value.value<double>(), category.toStdString()); + break; + default: + ret = false; + break; + } + + if(!ret) + { + qWarning() << "Settings ERROR: Write access not permitted for" << id << " with " << QString::fromStdString(m_settings->id()); + return false; + } + + emit valueChanged(toKeyCombined(category.toStdString(), key.toStdString()), value); + return true; +} + +QStringList KaZoe::KzQSettings::keys() const +{ + if(!m_settings) + { + qWarning() << "Invalid object state for keys"; + return QStringList(); + } + + QStringList keys; + m_settings->forEach([&keys](const KzSettingKey& key, const KzSettingValue& value) { + std::string combined; + if(!key.first.empty()) + { + combined = "[" + key.first + "]"; + } + combined.append(key.second); + keys.append(QString::fromStdString(combined)); + }); + return keys; +} diff --git a/qt/kzqsettings.h b/qt/kzqsettings.h new file mode 100644 index 0000000..6ab48a0 --- /dev/null +++ b/qt/kzqsettings.h @@ -0,0 +1,34 @@ +#ifndef KZQSETTINGS_H +#define KZQSETTINGS_H + +#include <QObject> +#include <QVariant> + +namespace KaZoe { + +class KzSettings; + +class KzQSettings : public QObject +{ + Q_OBJECT + QScopedPointer<KaZoe::KzSettings> m_settings; + +public: + explicit KzQSettings(QObject *parent = nullptr); + virtual ~KzQSettings(); + + static QVariant getValue(const QString &id, QVariant defvalue = QVariant()); + static bool setValue(const QString &id, const QVariant &value); + +public slots: + int count() const; + QVariant get(const QString &id, const QVariant defvalue = QVariant()) const; + bool set(const QString &id, const QVariant &value); + QStringList keys() const; + +signals: + void valueChanged(QString id, QVariant value); +}; +}; + +#endif // KZQSETTINGS_H diff --git a/qt/kzqsettings_plugin.cpp b/qt/kzqsettings_plugin.cpp new file mode 100644 index 0000000..170324e --- /dev/null +++ b/qt/kzqsettings_plugin.cpp @@ -0,0 +1,12 @@ +#include "kzqsettings_plugin.h" +#include "kzqsettings.h" +#include "kzqproperty.h" + +#include <qqml.h> + +void KzSettingsPlugin::registerTypes(const char *uri) +{ + // @uri kazoe + qmlRegisterType<KaZoe::KzQSettings>(uri, 1, 0, "KzSettings"); + qmlRegisterType<KaZoe::KzQProperty>(uri, 1, 0, "KzProperty"); +} diff --git a/qt/kzqsettings_plugin.h b/qt/kzqsettings_plugin.h new file mode 100644 index 0000000..dcaf808 --- /dev/null +++ b/qt/kzqsettings_plugin.h @@ -0,0 +1,15 @@ +#ifndef KZSETTINGS_PLUGIN_H +#define KZSETTINGS_PLUGIN_H + +#include <QQmlExtensionPlugin> + +class KzSettingsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri) override; +}; + +#endif // SYSTEMSETTINGS_PLUGIN_H diff --git a/qt/qml/qmldir b/qt/qml/qmldir new file mode 100644 index 0000000..d15e7a3 --- /dev/null +++ b/qt/qml/qmldir @@ -0,0 +1,2 @@ +module org.kazoe.settings +plugin KzQSettingsPlugin diff --git a/src/changewatcher.cpp b/src/changewatcher.cpp index 4762ebb..ef54019 100644 --- a/src/changewatcher.cpp +++ b/src/changewatcher.cpp @@ -1,5 +1,4 @@ #include "changewatcher.h" -#include <iostream> #include <filesystem> #include <sys/inotify.h> #include <sys/eventfd.h> diff --git a/src/settings.cpp b/src/kzsettings.cpp index addaa32..5580a80 100644 --- a/src/settings.cpp +++ b/src/kzsettings.cpp @@ -1,4 +1,4 @@ -#include "settings.h" +#include "kzsettings.h" #include <iostream> #include <fstream> #include <map> @@ -23,29 +23,29 @@ static inline bool list_contains(std::vector<std::string> list, const std::strin } namespace KaZoe { -class SettingsPrivate { - friend class Settings; - Settings *m_parent; - std::map<SettingKey, SettingValue> m_data; +class KzSettingsPrivate { + friend class KzSettings; + KzSettings *m_parent; + std::map<KzSettingKey, KzSettingValue> m_data; ChangeWatcher m_watcher; - std::map<SettingKey, std::string> m_owner; + std::map<KzSettingKey, std::string> m_owner; std::string m_id; std::mutex m_mutex_data; std::mutex m_mutex_notifier; bool m_bypass {false}; - std::vector<std::function<void(const std::string&, const std::string&, SettingValue)>> m_notifier; + std::vector<std::function<void(const std::string&, const std::string&, KzSettingValue)>> m_notifier; public: - SettingsPrivate(Settings *parent) + KzSettingsPrivate(KzSettings *parent) : m_parent(parent) { char result[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); std::string binary = std::string(result, (count > 0) ? count : 0); std::filesystem::path bin = binary; m_id = bin.filename(); - if(m_id == "settings") + if(m_id == "kzsettings") { - // Only "settings cli tools can bypass the owner protection" + // Only "kzsettings cli tools can bypass the owner protection" m_bypass = true; } @@ -54,7 +54,7 @@ public: m_watcher.setFileWrited([this](const std::string path){ refreshFile(path); }); } - void parseFile(const std::string &filename, std::map<SettingKey, SettingValue> &data, bool watch = false) + void parseFile(const std::string &filename, std::map<KzSettingKey, KzSettingValue> &data, bool watch = false) { if(!std::filesystem::exists(filename)) { @@ -92,21 +92,21 @@ public: if(key.starts_with("@")) { - m_owner[SettingKey(category, key.substr(1))] = value; + m_owner[KzSettingKey(category, key.substr(1))] = value; continue; } if(key.ends_with("/owner")) { std::cerr << "WARNING: Legacy usage for owner: " << key << " should be @" << key.substr(0, key.size() - 6) << std::endl; - m_owner[SettingKey(category, key.substr(0, key.size() - 6))] = value; + m_owner[KzSettingKey(category, key.substr(0, key.size() - 6))] = value; continue; } - data[SettingKey(category, key)] = makeValue(value); + data[KzSettingKey(category, key)] = makeValue(value); } } - void parseDir(const std::string &dconf, std::map<SettingKey, SettingValue> &data, bool watch) + void parseDir(const std::string &dconf, std::map<KzSettingKey, KzSettingValue> &data, bool watch) { if(std::filesystem::exists(dconf) && std::filesystem::is_directory(dconf)) { @@ -118,7 +118,7 @@ public: } } - void parseConf(const std::string &fconf, std::map<SettingKey, SettingValue> &data, bool watch) + void parseConf(const std::string &fconf, std::map<KzSettingKey, KzSettingValue> &data, bool watch) { if(std::filesystem::exists(fconf)) { @@ -132,7 +132,7 @@ public: } } - void refreshData(const std::map<SettingKey, SettingValue> &cust_data) + void refreshData(const std::map<KzSettingKey, KzSettingValue> &cust_data) { std::lock_guard<std::mutex> lock(m_mutex_data); std::lock_guard<std::mutex> lock_notifier(m_mutex_notifier); @@ -152,21 +152,21 @@ public: void refreshFile(const std::string &file) { - std::map<SettingKey, SettingValue> cust_data; + std::map<KzSettingKey, KzSettingValue> cust_data; parseFile(file, cust_data); refreshData(cust_data); } void refreshDirectory(const std::string &dirname) { - std::map<SettingKey, SettingValue> cust_data; + std::map<KzSettingKey, KzSettingValue> cust_data; parseDir(dirname, cust_data, true); refreshData(cust_data); } void refreshAll() { - std::map<SettingKey, SettingValue> cust_data; + std::map<KzSettingKey, KzSettingValue> cust_data; parseConf("/etc/kazoe.conf", cust_data, false); parseConf("/var/kazoe.conf", cust_data, true); refreshData(cust_data); @@ -174,14 +174,14 @@ public: }; }; -Settings::Settings() - : m_ptr(std::make_unique<SettingsPrivate>(this)) +KzSettings::KzSettings() + : m_ptr(std::make_unique<KzSettingsPrivate>(this)) { m_ptr->parseConf("/etc/kazoe.conf", m_ptr->m_data, false); m_ptr->parseConf("/var/kazoe.conf", m_ptr->m_data, true); } -SettingValue Settings::get(const std::string &key, const std::string &category, SettingValue def) const +KzSettingValue KzSettings::get(const std::string &key, const std::string &category, KzSettingValue def) const { std::lock_guard<std::mutex> lock(m_ptr->m_mutex_data); std::string gkey = key; @@ -192,7 +192,7 @@ SettingValue Settings::get(const std::string &key, const std::string &category, gkey = gkey.substr(gkey.find(']') + 1); } - SettingKey pkey(gcategory, gkey); + KzSettingKey pkey(gcategory, gkey); if(m_ptr->m_data.count(pkey)) { @@ -201,11 +201,11 @@ SettingValue Settings::get(const std::string &key, const std::string &category, return def; } -bool Settings::set(const std::string &key, SettingValue value, const std::string &category) +bool KzSettings::set(const std::string &key, KzSettingValue value, const std::string &category) { // Check if variable is writable std::lock_guard<std::mutex> lock(m_ptr->m_mutex_data); - SettingKey pkey(category, key); + KzSettingKey pkey(category, key); std::string id; if(!m_ptr->m_owner.count(pkey)) @@ -229,7 +229,7 @@ bool Settings::set(const std::string &key, SettingValue value, const std::string } // Permission is OK, get old custom variable - std::map<SettingKey, SettingValue> cust_data; + std::map<KzSettingKey, KzSettingValue> cust_data; m_ptr->parseFile("/var/kazoe.conf.d/"+id+".conf", cust_data); cust_data[pkey] = value; std::string last_category = ""; @@ -248,30 +248,30 @@ bool Settings::set(const std::string &key, SettingValue value, const std::string return true; } -std::string Settings::id() const +std::string KzSettings::id() const { return m_ptr->m_id; } -void Settings::setId(const std::string &newid) +void KzSettings::setId(const std::string &newid) { m_ptr->m_id = newid; } -void Settings::setNotifier(const std::function<void (const std::string &, const std::string &, SettingValue)> &callback) +void KzSettings::setNotifier(const std::function<void (const std::string &, const std::string &, KzSettingValue)> &callback) { std::lock_guard<std::mutex> lock(m_ptr->m_mutex_notifier); m_ptr->m_notifier.push_back(callback); m_ptr->m_watcher.setEnable(true); } -std::size_t Settings::size() const +std::size_t KzSettings::size() const { std::lock_guard<std::mutex> lock(m_ptr->m_mutex_data); return m_ptr->m_data.size(); } -void Settings::forEach(const std::function<void (const SettingKey &, const SettingValue &)> &callback) const +void KzSettings::forEach(const std::function<void (const KzSettingKey &, const KzSettingValue &)> &callback) const { std::lock_guard<std::mutex> lock(m_ptr->m_mutex_data); for (const auto& [key, value] : m_ptr->m_data) { @@ -279,7 +279,7 @@ void Settings::forEach(const std::function<void (const SettingKey &, const Setti } } -Settings::~Settings() +KzSettings::~KzSettings() { m_ptr->m_watcher.setEnable(false); } diff --git a/src/settings.h b/src/kzsettings.h index 5dfe90b..9f8d35b 100644 --- a/src/settings.h +++ b/src/kzsettings.h @@ -1,5 +1,5 @@ -#ifndef KAZOESETTINGS_H -#define KAZOESETTINGS_H +#ifndef KZSETTINGS_H +#define KZSETTINGS_H #include <functional> #include <memory> @@ -8,15 +8,15 @@ #include <algorithm> /** @brief Key type for settings, composed of category and name */ -using SettingKey = std::pair<std::string, std::string>; +using KzSettingKey = std::pair<std::string, std::string>; /** @brief Value type that can hold string, int, double or boolean */ -using SettingValue = std::variant<std::string, int, double, bool>; +using KzSettingValue = std::variant<std::string, int, double, bool>; namespace KaZoe { -class SettingsPrivate; +class KzSettingsPrivate; /** * @brief Main class for managing KaZoe settings @@ -24,14 +24,14 @@ class SettingsPrivate; * This class provides a thread-safe interface for storing and retrieving * configuration settings with support for different value types. */ -class Settings +class KzSettings { public: /** @brief Constructs a new Settings instance */ - explicit Settings(); + explicit KzSettings(); /** @brief Virtual destructor */ - virtual ~Settings(); + virtual ~KzSettings(); /** * @brief Retrieves a setting value @@ -40,7 +40,7 @@ public: * @param def Default value if setting not found (optional) * @return The setting value or default if not found */ - SettingValue get(const std::string &key, const std::string &category = "", SettingValue def = SettingValue()) const; + KzSettingValue get(const std::string &key, const std::string &category = "", KzSettingValue def = KzSettingValue()) const; /** * @brief Sets a setting value @@ -49,7 +49,7 @@ public: * @param category The setting category (optional) * @return true if successful, false otherwise */ - bool set(const std::string &key, SettingValue value, const std::string &category = ""); + bool set(const std::string &key, KzSettingValue value, const std::string &category = ""); /** * @brief Gets the current instance identifier @@ -67,7 +67,7 @@ public: * @brief Sets a callback for setting changes notification * @param callback Function to call when settings change */ - void setNotifier(const std::function<void(const std::string&, const std::string&, SettingValue)>& callback); + void setNotifier(const std::function<void(const std::string&, const std::string&, KzSettingValue)>& callback); /** * @brief Gets the number of settings @@ -79,10 +79,10 @@ public: * @brief Iterates over all settings in a thread-safe manner * @param callback Function called for each key-value pair */ - void forEach(const std::function<void(const SettingKey&, const SettingValue&)>& callback) const; + void forEach(const std::function<void(const KzSettingKey&, const KzSettingValue&)>& callback) const; private: - std::unique_ptr<SettingsPrivate> m_ptr; ///< Private implementation pointer + std::unique_ptr<KzSettingsPrivate> m_ptr; ///< Private implementation pointer }; @@ -103,7 +103,7 @@ struct settingValueFunctor { * @param value Input SettingValue to convert * @return std::string containing the converted value */ -static inline std::string valueToStr(const SettingValue &value) +static inline std::string valueToStr(const KzSettingValue &value) { return std::visit(settingValueFunctor(), value); } @@ -133,7 +133,7 @@ static inline bool is_numeric(const std::string& str) { * - "false"/"off" -> boolean false * - Other -> string */ -static inline SettingValue makeValue(const std::string &value) +static inline KzSettingValue makeValue(const std::string &value) { if(value.starts_with('"') || value.starts_with('\'')) { @@ -177,4 +177,4 @@ static inline SettingValue makeValue(const std::string &value) }; -#endif // KAZOESETTINGS_H +#endif // KZSETTINGS_H |