mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2025-02-25 18:55:24 -06:00
+clang-format
This commit is contained in:
@@ -5,6 +5,6 @@
|
||||
namespace TuxClocker::Crypto {
|
||||
|
||||
std::string sha256(std::string s);
|
||||
std::string md5(std::string s);
|
||||
std::string md5(std::string s);
|
||||
|
||||
};
|
||||
}; // namespace TuxClocker::Crypto
|
||||
|
||||
@@ -11,18 +11,17 @@ namespace TC = TuxClocker;
|
||||
|
||||
namespace TuxClocker::DBus {
|
||||
|
||||
template <typename T>
|
||||
struct Result {
|
||||
template <typename T> struct Result {
|
||||
bool error;
|
||||
T value;
|
||||
|
||||
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const Result<T> r) {
|
||||
arg.beginStructure();
|
||||
arg << r.error << r.value;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Result<T> &r) {
|
||||
arg.beginStructure();
|
||||
arg >> r.error >> r.value;
|
||||
@@ -39,7 +38,7 @@ struct Range {
|
||||
QDBusVariant min, max;
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const Range r) {
|
||||
arg.beginStructure();
|
||||
arg << r.min<< r.max;
|
||||
arg << r.min << r.max;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
@@ -52,13 +51,14 @@ struct Range {
|
||||
TC::Device::AssignableInfo toAssignableInfo() {
|
||||
auto min_v = min.variant();
|
||||
auto max_v = max.variant();
|
||||
|
||||
|
||||
auto im = static_cast<QVariant::Type>(QMetaType::Int);
|
||||
auto dm = static_cast<QVariant::Type>(QMetaType::Double);
|
||||
if (min_v.type() == im && max_v.type() == im)
|
||||
return TC::Device::Range<int>(min_v.value<int>(), max_v.value<int>());
|
||||
if (min_v.type() == dm && max_v.type() == dm)
|
||||
return TC::Device::Range<double>(min_v.value<double>(), max_v.value<double>());
|
||||
return TC::Device::Range<double>(
|
||||
min_v.value<double>(), max_v.value<double>());
|
||||
// Should never reach here
|
||||
return TC::Device::Range<int>(0, 0);
|
||||
}
|
||||
@@ -107,8 +107,7 @@ struct DeviceNode {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FlatTreeNode {
|
||||
template <typename T> struct FlatTreeNode {
|
||||
T value;
|
||||
QVector<int> childIndices;
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const FlatTreeNode<T> f) {
|
||||
@@ -125,4 +124,4 @@ struct FlatTreeNode {
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
}; // namespace TuxClocker::DBus
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace TuxClocker {
|
||||
namespace Device {
|
||||
|
||||
|
||||
enum class AssignmentError {
|
||||
InvalidArgument,
|
||||
InvalidType,
|
||||
@@ -21,15 +21,17 @@ enum class ReadError {
|
||||
UnknownError
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Range {
|
||||
Range() {};
|
||||
Range(const T &min_, const T &max_) {min = min_, max = max_;}
|
||||
template <typename T> struct Range {
|
||||
Range(){};
|
||||
Range(const T &min_, const T &max_) { min = min_, max = max_; }
|
||||
T min, max;
|
||||
};
|
||||
|
||||
|
||||
struct Enumeration {
|
||||
Enumeration(const std::string &name_, const uint &key_) {name = name_; key = key_;}
|
||||
Enumeration(const std::string &name_, const uint &key_) {
|
||||
name = name_;
|
||||
key = key_;
|
||||
}
|
||||
std::string name;
|
||||
uint key;
|
||||
};
|
||||
@@ -43,10 +45,11 @@ using AssignableInfo = std::variant<RangeInfo, std::vector<Enumeration>>;
|
||||
|
||||
class Assignable {
|
||||
public:
|
||||
Assignable(const std::function<std::optional<AssignmentError>(AssignmentArgument)> assignmentFunc,
|
||||
AssignableInfo info,
|
||||
const std::function<std::optional<AssignmentArgument>()> currentValueFunc,
|
||||
std::optional<std::string> unit = std::nullopt) {
|
||||
Assignable(
|
||||
const std::function<std::optional<AssignmentError>(AssignmentArgument)> assignmentFunc,
|
||||
AssignableInfo info,
|
||||
const std::function<std::optional<AssignmentArgument>()> currentValueFunc,
|
||||
std::optional<std::string> unit = std::nullopt) {
|
||||
m_assignmentFunc = assignmentFunc;
|
||||
m_assignableInfo = info;
|
||||
m_currentValueFunc = currentValueFunc;
|
||||
@@ -56,9 +59,9 @@ public:
|
||||
return m_assignmentFunc(arg);
|
||||
}
|
||||
// What the Assignable is currently set to
|
||||
std::optional<AssignmentArgument> currentValue() {return m_currentValueFunc();}
|
||||
AssignableInfo assignableInfo() {return m_assignableInfo;}
|
||||
std::optional<std::string> unit() {return m_unit;}
|
||||
std::optional<AssignmentArgument> currentValue() { return m_currentValueFunc(); }
|
||||
AssignableInfo assignableInfo() { return m_assignableInfo; }
|
||||
std::optional<std::string> unit() { return m_unit; }
|
||||
private:
|
||||
AssignableInfo m_assignableInfo;
|
||||
std::function<std::optional<AssignmentError>(AssignmentArgument)> m_assignmentFunc;
|
||||
@@ -69,16 +72,13 @@ private:
|
||||
class DynamicReadable {
|
||||
public:
|
||||
DynamicReadable() {}
|
||||
DynamicReadable(const std::function<std::variant<ReadError,
|
||||
ReadableValue>()> readFunc,
|
||||
std::optional<std::string> unit = std::nullopt) {
|
||||
DynamicReadable(const std::function<std::variant<ReadError, ReadableValue>()> readFunc,
|
||||
std::optional<std::string> unit = std::nullopt) {
|
||||
m_readFunc = readFunc;
|
||||
m_unit = unit;
|
||||
}
|
||||
/*std::variant<ReadError, ReadableValue>*/ ReadResult read() {
|
||||
return m_readFunc();
|
||||
}
|
||||
auto unit() {return m_unit;}
|
||||
/*std::variant<ReadError, ReadableValue>*/ ReadResult read() { return m_readFunc(); }
|
||||
auto unit() { return m_unit; }
|
||||
private:
|
||||
std::function<std::variant<ReadError, ReadableValue>()> m_readFunc;
|
||||
std::optional<std::string> m_unit;
|
||||
@@ -90,8 +90,8 @@ public:
|
||||
m_value = value;
|
||||
m_unit = unit;
|
||||
}
|
||||
ReadableValue value() {return m_value;}
|
||||
std::optional<std::string> unit() {return m_unit;}
|
||||
ReadableValue value() { return m_value; }
|
||||
std::optional<std::string> unit() { return m_unit; }
|
||||
private:
|
||||
ReadableValue m_value;
|
||||
std::optional<std::string> m_unit;
|
||||
@@ -102,7 +102,6 @@ using ResetInfo = std::variant<std::function<void()>, AssignmentArgument>;
|
||||
|
||||
class Resettable {
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
/* DeviceNode has a name, and optionally implements one of
|
||||
@@ -115,5 +114,5 @@ struct DeviceNode {
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}; // namespace Device
|
||||
}; // namespace TuxClocker
|
||||
|
||||
@@ -5,28 +5,26 @@
|
||||
|
||||
namespace TuxClocker {
|
||||
|
||||
// Creates a function with no arguments from a function that takes some from inputs to the function and the function
|
||||
template<typename T, typename... Args>
|
||||
// Creates a function with no arguments from a function that takes some from inputs to the function
|
||||
// and the function
|
||||
template <typename T, typename... Args>
|
||||
auto specializeFunction(Args... args, std::function<T(Args...)> func) {
|
||||
return [args = std::make_tuple(std::move(args)...), &func]() {
|
||||
return std::apply([&func](auto ...args) {
|
||||
func(args...);
|
||||
}, std::move(args));
|
||||
};
|
||||
return [args = std::make_tuple(std::move(args)...), &func]() {
|
||||
return std::apply([&func](auto... args) { func(args...); }, std::move(args));
|
||||
};
|
||||
}
|
||||
|
||||
template<template<typename> typename List, typename In, typename Out>
|
||||
template <template <typename> typename List, typename In, typename Out>
|
||||
List<Out> map(List<In> list, std::function<Out(In)> func) {
|
||||
List<Out> retval;
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(retval), func);
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<typename List, typename T>
|
||||
List filter(List list, std::function<bool(T)> func) {
|
||||
template <typename List, typename T> List filter(List list, std::function<bool(T)> func) {
|
||||
List retval;
|
||||
std::copy_if(list.begin(), list.end(), std::back_inserter(retval), func);
|
||||
return retval;
|
||||
}
|
||||
|
||||
};
|
||||
}; // namespace TuxClocker
|
||||
|
||||
@@ -8,40 +8,41 @@
|
||||
#include "Device.hpp"
|
||||
#include "Tree.hpp"
|
||||
|
||||
#define TUXCLOCKER_PLUGIN_EXPORT(PluginType) \
|
||||
extern "C" BOOST_SYMBOL_EXPORT PluginType __plugin; \
|
||||
#define TUXCLOCKER_PLUGIN_EXPORT(PluginType) \
|
||||
extern "C" BOOST_SYMBOL_EXPORT PluginType __plugin; \
|
||||
PluginType __plugin;
|
||||
|
||||
#define TUXCLOCKER_PLUGIN_SYMBOL_NAME "__plugin"
|
||||
|
||||
|
||||
namespace TuxClocker {
|
||||
namespace Plugin {
|
||||
|
||||
|
||||
namespace dll = boost::dll;
|
||||
|
||||
|
||||
enum class InitializationError {
|
||||
UnknownError
|
||||
};
|
||||
|
||||
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
class Plugin {
|
||||
public:
|
||||
static std::string pluginDirName() {return "plugins";}
|
||||
static std::string pluginDirName() { return "plugins"; }
|
||||
// Full path is efined at compile time
|
||||
static std::string pluginPath();
|
||||
};
|
||||
|
||||
class DevicePlugin {
|
||||
public:
|
||||
// Communicate plugin initialization success in this way since constructors cannot communicate it.
|
||||
// Communicate plugin initialization success in this way since constructors cannot
|
||||
// communicate it.
|
||||
virtual std::optional<InitializationError> initializationError() = 0;
|
||||
virtual TreeNode<DeviceNode> deviceRootNode() = 0;
|
||||
virtual ~DevicePlugin() {}
|
||||
|
||||
|
||||
// Helper for loading all DevicePlugin's
|
||||
static std::optional<std::vector<boost::shared_ptr<DevicePlugin>>> loadPlugins();
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}; // namespace Plugin
|
||||
}; // namespace TuxClocker
|
||||
|
||||
@@ -6,26 +6,23 @@
|
||||
|
||||
namespace TuxClocker {
|
||||
|
||||
template <typename T>
|
||||
struct FlatTreeNode {
|
||||
template <typename T> struct FlatTreeNode {
|
||||
T value;
|
||||
std::vector<int> childIndices;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TreeNode;
|
||||
|
||||
template <typename T>
|
||||
struct FlatTree {
|
||||
template <typename T> class TreeNode;
|
||||
|
||||
template <typename T> struct FlatTree {
|
||||
std::vector<FlatTreeNode<T>> nodes;
|
||||
|
||||
|
||||
static TreeNode<T> toTree(FlatTree<T> flatTree) {
|
||||
std::function<void(TreeNode<T>*, uint)> recur;
|
||||
std::function<void(TreeNode<T> *, uint)> recur;
|
||||
recur = [&recur, flatTree](TreeNode<T> *node, uint i) {
|
||||
auto c_indices = flatTree.nodes[i].childIndices;
|
||||
for (auto index : c_indices)
|
||||
node->appendChild(TreeNode<T>(flatTree.nodes[index].value));
|
||||
|
||||
|
||||
uint j = 0;
|
||||
for (auto &c_node : *(node->childrenPtr())) {
|
||||
recur(&c_node, c_indices[j]);
|
||||
@@ -42,23 +39,22 @@ struct FlatTree {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TreeNode {
|
||||
template <typename T> class TreeNode {
|
||||
public:
|
||||
TreeNode() {};
|
||||
TreeNode(T value) {m_value = value;}
|
||||
void appendChild(T value) {m_children.push_back(TreeNode{value});}
|
||||
void appendChild(TreeNode<T> node) {m_children.push_back(node);}
|
||||
std::vector<TreeNode<T>> children() {return m_children;}
|
||||
TreeNode(){};
|
||||
TreeNode(T value) { m_value = value; }
|
||||
void appendChild(T value) { m_children.push_back(TreeNode{value}); }
|
||||
void appendChild(TreeNode<T> node) { m_children.push_back(node); }
|
||||
std::vector<TreeNode<T>> children() { return m_children; }
|
||||
// Needed for recursive tree construction
|
||||
std::vector<TreeNode<T>> *childrenPtr() {return &m_children;}
|
||||
std::vector<TreeNode<T>> *childrenPtr() { return &m_children; }
|
||||
static void preorder(const TreeNode<T> node, std::function<void(const T)> func) {
|
||||
func(node.m_value);
|
||||
for (const auto child : node.m_children) {
|
||||
preorder(child, func);
|
||||
}
|
||||
}
|
||||
T value() {return m_value;}
|
||||
T value() { return m_value; }
|
||||
// Convert tree to array
|
||||
FlatTree<T> toFlatTree() {
|
||||
std::vector<FlatTreeNode<T>> nodes;
|
||||
@@ -69,7 +65,7 @@ public:
|
||||
for (auto &c_node : *(n->childrenPtr())) {
|
||||
// Find the index of this child node
|
||||
int j = 0, index = 0;
|
||||
|
||||
|
||||
preorderByRef(node_ptr, [&j, &index, &c_node](TreeNode<T> *n) {
|
||||
if (n == &c_node)
|
||||
index = j;
|
||||
@@ -84,12 +80,11 @@ public:
|
||||
private:
|
||||
T m_value;
|
||||
std::vector<TreeNode<T>> m_children;
|
||||
static void preorderByRef(TreeNode<T> *node,
|
||||
std::function<void(TreeNode<T>*)> func) {
|
||||
static void preorderByRef(TreeNode<T> *node, std::function<void(TreeNode<T> *)> func) {
|
||||
func(node);
|
||||
for (auto &c_node : *(node->childrenPtr()))
|
||||
preorderByRef(&c_node, func);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
}; // namespace TuxClocker
|
||||
|
||||
@@ -12,63 +12,64 @@ extern "C" {
|
||||
It is a tree structure provided by a module. */
|
||||
|
||||
enum tc_assignable_value_category {
|
||||
TC_ASSIGNABLE_NONE,
|
||||
TC_ASSIGNABLE_RANGE,
|
||||
TC_ASSIGNABLE_ENUM
|
||||
TC_ASSIGNABLE_NONE,
|
||||
TC_ASSIGNABLE_RANGE,
|
||||
TC_ASSIGNABLE_ENUM
|
||||
};
|
||||
|
||||
// Is the range double or integer
|
||||
enum tc_assignable_range_data_type {
|
||||
TC_ASSIGNABLE_RANGE_INT,
|
||||
TC_ASSIGNABLE_RANGE_DOUBLE
|
||||
TC_ASSIGNABLE_RANGE_INT,
|
||||
TC_ASSIGNABLE_RANGE_DOUBLE
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
double min, max;
|
||||
double min, max;
|
||||
} tc_assignable_range_double_t;
|
||||
|
||||
typedef struct {
|
||||
int64_t min, max;
|
||||
int64_t min, max;
|
||||
} tc_assignable_range_int_t;
|
||||
|
||||
typedef struct {
|
||||
enum tc_assignable_range_data_type range_data_type;
|
||||
union {
|
||||
tc_assignable_range_double_t double_range;
|
||||
tc_assignable_range_int_t int_range;
|
||||
};
|
||||
enum tc_assignable_range_data_type range_data_type;
|
||||
union {
|
||||
tc_assignable_range_double_t double_range;
|
||||
tc_assignable_range_int_t int_range;
|
||||
};
|
||||
} tc_assignable_range_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t property_count;
|
||||
char **properties;
|
||||
uint16_t property_count;
|
||||
char **properties;
|
||||
} tc_assignable_enum_t;
|
||||
|
||||
typedef struct tc_assignable_node_t {
|
||||
// Assignable name eg. fan speed
|
||||
char *name;
|
||||
// Unit for assignable
|
||||
char *unit;
|
||||
// Assignable name eg. fan speed
|
||||
char *name;
|
||||
// Unit for assignable
|
||||
char *unit;
|
||||
|
||||
// Callback for assignment (use NULL for a placeholder node)
|
||||
int8_t (*assign_callback)(tc_variant_t value, const struct tc_assignable_node_t *node);
|
||||
// Callback for assignment (use NULL for a placeholder node)
|
||||
int8_t (*assign_callback)(tc_variant_t value, const struct tc_assignable_node_t *node);
|
||||
|
||||
// Possible values for tunables are either values from a range or enumerations
|
||||
enum tc_assignable_value_category value_category;
|
||||
union {
|
||||
tc_assignable_enum_t enum_info;
|
||||
tc_assignable_range_t range_info;
|
||||
};
|
||||
// Possible values for tunables are either values from a range or enumerations
|
||||
enum tc_assignable_value_category value_category;
|
||||
union {
|
||||
tc_assignable_enum_t enum_info;
|
||||
tc_assignable_range_t range_info;
|
||||
};
|
||||
|
||||
struct tc_assignable_node_t *parent;
|
||||
uint16_t children_count;
|
||||
struct tc_assignable_node_t **children_nodes;
|
||||
struct tc_assignable_node_t *parent;
|
||||
uint16_t children_count;
|
||||
struct tc_assignable_node_t **children_nodes;
|
||||
} tc_assignable_node_t;
|
||||
|
||||
// Master data structure loaded by module loader
|
||||
typedef struct {
|
||||
tc_assignable_node_t *root_node;
|
||||
const char *(*sha256_hash)(const tc_assignable_node_t*); // Callback to get a unique hash for a node
|
||||
const char *(*sha256_hash)(
|
||||
const tc_assignable_node_t *); // Callback to get a unique hash for a node
|
||||
} tc_assignable_module_data_t;
|
||||
|
||||
/* Utility functions for assignables */
|
||||
@@ -81,7 +82,8 @@ void tc_assignable_node_destroy(tc_assignable_node_t *node);
|
||||
int8_t tc_assignable_node_add_child(tc_assignable_node_t *node, tc_assignable_node_t *child);
|
||||
|
||||
/* Utility functions for range and property info*/
|
||||
void tc_assignable_node_set_data(tc_assignable_node_t *node, char *unit, char *name, int8_t (*assign_callback)(tc_variant_t, const tc_assignable_node_t*));
|
||||
void tc_assignable_node_set_data(tc_assignable_node_t *node, char *unit, char *name,
|
||||
int8_t (*assign_callback)(tc_variant_t, const tc_assignable_node_t *));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -5,43 +5,44 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// Common definitions for tuxclocker
|
||||
|
||||
// Error values
|
||||
#define TC_SUCCESS 0
|
||||
#define TC_EGENERIC (-1)
|
||||
#define TC_ENOMEM (-2)
|
||||
#define TC_EINVAL (-3) // Invalid argument
|
||||
#define TC_EINVAL (-3) // Invalid argument
|
||||
#define TC_ENOPERM (-4) // Insufficient permissions
|
||||
#define TC_EINVALPREREQ (-5) // Invalid prerequisite value (eg. trying to set fan speed with automatic mode active)
|
||||
#define TC_EINVALPREREQ \
|
||||
(-5) // Invalid prerequisite value (eg. trying to set fan speed with automatic mode active)
|
||||
|
||||
// Tagged union of data types for simulating function overloading
|
||||
enum tc_data_types {
|
||||
TC_TYPE_NONE,
|
||||
TC_TYPE_INT,
|
||||
TC_TYPE_UINT,
|
||||
TC_TYPE_DOUBLE,
|
||||
TC_TYPE_STRING,
|
||||
TC_TYPE_STRING_ARR
|
||||
TC_TYPE_NONE,
|
||||
TC_TYPE_INT,
|
||||
TC_TYPE_UINT,
|
||||
TC_TYPE_DOUBLE,
|
||||
TC_TYPE_STRING,
|
||||
TC_TYPE_STRING_ARR
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum tc_data_types data_type;
|
||||
union {
|
||||
int64_t int_value;
|
||||
uint64_t uint_value;
|
||||
double double_value;
|
||||
char *string_value;
|
||||
};
|
||||
enum tc_data_types data_type;
|
||||
union {
|
||||
int64_t int_value;
|
||||
uint64_t uint_value;
|
||||
double double_value;
|
||||
char *string_value;
|
||||
};
|
||||
} tc_variant_t;
|
||||
|
||||
typedef struct {
|
||||
enum tc_data_types arg_type;
|
||||
union {
|
||||
int int_arg;
|
||||
char **string_arr_arg;
|
||||
};
|
||||
enum tc_data_types arg_type;
|
||||
union {
|
||||
int int_arg;
|
||||
char **string_arr_arg;
|
||||
};
|
||||
} tc_arg_t;
|
||||
|
||||
// Utility functions
|
||||
@@ -52,15 +53,15 @@ void tc_str_arr_free(uint16_t str_count, char **strings);
|
||||
|
||||
// Binary search tree whose left node contains the smaller value
|
||||
typedef struct tc_bin_node_ {
|
||||
void *key;
|
||||
void *value;
|
||||
|
||||
struct tc_bin_node_ *left;
|
||||
struct tc_bin_node_ *right;
|
||||
void *key;
|
||||
void *value;
|
||||
|
||||
struct tc_bin_node_ *left;
|
||||
struct tc_bin_node_ *right;
|
||||
} tc_bin_node_t;
|
||||
|
||||
// Create a new node with key and data in the appropriate position
|
||||
tc_bin_node_t *tc_bin_node_insert(tc_bin_node_t* node, void *key, void *value);
|
||||
tc_bin_node_t *tc_bin_node_insert(tc_bin_node_t *node, void *key, void *value);
|
||||
// Find the value associated with the key
|
||||
void *tc_bin_node_find_value(tc_bin_node_t *node, const void *key);
|
||||
// Destroy a node and its children
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Get filename list from a directory. Return value needs to be freed.
|
||||
char **tc_fs_dir_filenames(const char *dir_name, uint16_t *file_count);
|
||||
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Bitmask values for module categories
|
||||
#define TC_ASSIGNABLE (1)
|
||||
#define TC_READABLE (1 << 1)
|
||||
@@ -18,12 +18,12 @@ extern "C" {
|
||||
|
||||
#define TC_READABLE_DYNAMIC (1)
|
||||
#define TC_READABLE_STATIC (1 << 1)
|
||||
|
||||
|
||||
// Categories for modules.
|
||||
enum tc_module_category {
|
||||
TC_CATEGORY_ASSIGNABLE,
|
||||
TC_CATEGORY_READABLE,
|
||||
TC_CATEGORY_INTERFACE
|
||||
TC_CATEGORY_ASSIGNABLE,
|
||||
TC_CATEGORY_READABLE,
|
||||
TC_CATEGORY_INTERFACE
|
||||
};
|
||||
|
||||
// Maximum amount of modules loaded at once
|
||||
@@ -31,7 +31,7 @@ enum tc_module_category {
|
||||
|
||||
// Default module path in case not defined
|
||||
#ifndef TC_MODULE_PATH
|
||||
#define TC_MODULE_PATH "/usr/lib/tuxclocker/modules"
|
||||
#define TC_MODULE_PATH "/usr/lib/tuxclocker/modules"
|
||||
#endif
|
||||
|
||||
#define TC_MODULE_DATABASE_NAME "tc_modules.json"
|
||||
@@ -59,7 +59,8 @@ typedef struct {
|
||||
int8_t (*init)();
|
||||
int8_t (*close)();
|
||||
uint64_t category;
|
||||
// Since category specific data might be generated after calling module's 'init' callback, use function pointers to fetch it.
|
||||
// Since category specific data might be generated after calling module's 'init' callback,
|
||||
// use function pointers to fetch it.
|
||||
union {
|
||||
tc_readable_module_data_t (*readable_data)();
|
||||
tc_assignable_module_data_t (*assignable_data)();
|
||||
@@ -90,19 +91,23 @@ typedef struct tc_module_t {
|
||||
|
||||
// Callback for category specific main data structure of the module
|
||||
void *(*category_data_callback)();
|
||||
|
||||
|
||||
tc_module_category_info_t category_info;
|
||||
} tc_module_t;
|
||||
|
||||
// Try to return the module handle matching the category and name. If it doesn't exist or there was a problem loading the module, returns NULL.
|
||||
// Try to return the module handle matching the category and name. If it doesn't exist or there was
|
||||
// a problem loading the module, returns NULL.
|
||||
tc_module_t *tc_module_find(enum tc_module_category category, const char *name);
|
||||
// Try to return all module handles matching 'category'. The return value needs to be freed in addition to tc_module_close()
|
||||
// Try to return all module handles matching 'category'. The return value needs to be freed in
|
||||
// addition to tc_module_close()
|
||||
tc_module_t **tc_module_find_all_from_category(enum tc_module_category category, uint16_t *count);
|
||||
|
||||
// Convenience functions
|
||||
tc_module_category_info_t tc_module_category_info_create(uint64_t mask, uint16_t num_categories, const tc_module_category_data_t *categories);
|
||||
tc_module_category_info_t tc_module_category_info_create(
|
||||
uint64_t mask, uint16_t num_categories, const tc_module_category_data_t *categories);
|
||||
|
||||
tc_module_category_data_t tc_module_category_data_create(uint64_t category, union module_data_callback_t u);
|
||||
tc_module_category_data_t tc_module_category_data_create(
|
||||
uint64_t category, union module_data_callback_t u);
|
||||
|
||||
// Close the module after successful find
|
||||
void tc_module_close(tc_module_t *module);
|
||||
|
||||
@@ -11,27 +11,26 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
// Return value type for querying values
|
||||
typedef struct {
|
||||
bool valid;
|
||||
tc_variant_t data;
|
||||
bool valid;
|
||||
tc_variant_t data;
|
||||
} tc_readable_result_t;
|
||||
|
||||
|
||||
typedef struct tc_readable_node_t_ {
|
||||
char *name;
|
||||
char *unit;
|
||||
|
||||
// Is the value of this node constant
|
||||
bool constant;
|
||||
// Instead of an update callback store the value for constants
|
||||
union {
|
||||
tc_readable_result_t (*value_callback)(const struct tc_readable_node_t_*);
|
||||
tc_variant_t data;
|
||||
};
|
||||
|
||||
uint16_t children_count;
|
||||
struct tc_readable_node_t_ **children_nodes;
|
||||
char *name;
|
||||
char *unit;
|
||||
|
||||
// Is the value of this node constant
|
||||
bool constant;
|
||||
// Instead of an update callback store the value for constants
|
||||
union {
|
||||
tc_readable_result_t (*value_callback)(const struct tc_readable_node_t_ *);
|
||||
tc_variant_t data;
|
||||
};
|
||||
|
||||
uint16_t children_count;
|
||||
struct tc_readable_node_t_ **children_nodes;
|
||||
} tc_readable_node_t;
|
||||
|
||||
// Master data structure loaded by module loader
|
||||
@@ -39,7 +38,8 @@ typedef struct {
|
||||
uint64_t category_mask; // Which types of readable (dynamic or static) are implemented
|
||||
tc_readable_node_t *root_static_node;
|
||||
tc_readable_node_t *root_node;
|
||||
const char *(*sha256_hash)(const tc_readable_node_t*); // Callback to get a unique hash for a node
|
||||
const char *(*sha256_hash)(
|
||||
const tc_readable_node_t *); // Callback to get a unique hash for a node
|
||||
} tc_readable_module_data_t;
|
||||
|
||||
// Utility functions
|
||||
@@ -58,7 +58,8 @@ tc_readable_node_t *tc_readable_node_add_new_child(tc_readable_node_t *parent);
|
||||
// Set node data
|
||||
void tc_readable_node_set_data(tc_readable_node_t *node, const char *name, const char *unit);
|
||||
|
||||
// Create a tc_readable_result from data, data type and validity. Avoids boilerplate in returning values from readable nodes.
|
||||
// Create a tc_readable_result from data, data type and validity. Avoids boilerplate in returning
|
||||
// values from readable nodes.
|
||||
tc_readable_result_t tc_readable_result_create(enum tc_data_types type, void *data, bool valid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
namespace TuxClocker::Crypto {
|
||||
|
||||
std::string sha256(std::string s) {
|
||||
auto d = SHA256(reinterpret_cast<const unsigned char*>(s.c_str()), s.size(), 0);
|
||||
auto d = SHA256(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(), 0);
|
||||
|
||||
char out[(SHA256_DIGEST_LENGTH * 2) + 1];
|
||||
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
|
||||
sprintf(out + (i * 2), "%02x", d[i]);
|
||||
sprintf(out + (i * 2), "%02x", d[i]);
|
||||
|
||||
out[SHA256_DIGEST_LENGTH * 2] = '\0';
|
||||
return std::string(out);
|
||||
@@ -18,15 +18,15 @@ std::string sha256(std::string s) {
|
||||
|
||||
std::string md5(std::string s) {
|
||||
unsigned char data[MD5_DIGEST_LENGTH];
|
||||
MD5(reinterpret_cast<const unsigned char*>(s.c_str()), s.size(), data);
|
||||
MD5(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(), data);
|
||||
|
||||
char out[(MD5_DIGEST_LENGTH * 2) + 1];
|
||||
|
||||
for (int i = 0; i < MD5_DIGEST_LENGTH; i++)
|
||||
sprintf(out + (i * 2), "%02x", data[i]);
|
||||
sprintf(out + (i * 2), "%02x", data[i]);
|
||||
|
||||
out[MD5_DIGEST_LENGTH * 2] = '\0';
|
||||
return std::string(out);
|
||||
}
|
||||
|
||||
};
|
||||
}; // namespace TuxClocker::Crypto
|
||||
|
||||
@@ -7,23 +7,22 @@ using namespace TuxClocker::Plugin;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::string Plugin::pluginPath() {
|
||||
return TC_PLUGIN_PATH;
|
||||
}
|
||||
std::string Plugin::pluginPath() { return TC_PLUGIN_PATH; }
|
||||
|
||||
std::optional<std::vector<boost::shared_ptr<DevicePlugin>>> DevicePlugin::loadPlugins() {
|
||||
std::vector<boost::shared_ptr<DevicePlugin>> retval;
|
||||
//std::cout << pluginPath();
|
||||
// std::cout << pluginPath();
|
||||
for (const fs::directory_entry &entry : fs::directory_iterator(Plugin::pluginPath())) {
|
||||
// Bleh, have to catch this unless I do more manual checks
|
||||
try {
|
||||
auto plugin = boost::dll::import_symbol<DevicePlugin>(entry.path().string(),
|
||||
TUXCLOCKER_PLUGIN_SYMBOL_NAME);
|
||||
auto plugin = boost::dll::import_symbol<DevicePlugin>(
|
||||
entry.path().string(), TUXCLOCKER_PLUGIN_SYMBOL_NAME);
|
||||
retval.push_back(plugin);
|
||||
} catch (boost::system::system_error &e) {
|
||||
}
|
||||
catch (boost::system::system_error &e) {}
|
||||
}
|
||||
|
||||
if (retval.empty()) return std::nullopt;
|
||||
|
||||
if (retval.empty())
|
||||
return std::nullopt;
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ struct AMDGPUInfo {
|
||||
class AMDPlugin : public DevicePlugin {
|
||||
public:
|
||||
AMDPlugin();
|
||||
std::optional<InitializationError> initializationError() {return std::nullopt;}
|
||||
void foo() {std::cout << "hi from AMD plugin\n";}
|
||||
std::optional<InitializationError> initializationError() { return std::nullopt; }
|
||||
void foo() { std::cout << "hi from AMD plugin\n"; }
|
||||
TreeNode<DeviceNode> deviceRootNode();
|
||||
~AMDPlugin();
|
||||
private:
|
||||
@@ -48,9 +48,7 @@ private:
|
||||
std::vector<AMDGPUInfo> m_GPUInfoVec;
|
||||
template <typename T>
|
||||
static std::variant<ReadError, ReadableValue> libdrmRead(amdgpu_device_handle dev,
|
||||
uint query,
|
||||
std::optional<std::function<T(T)>> transformFunc = std::nullopt);
|
||||
|
||||
uint query, std::optional<std::function<T(T)>> transformFunc = std::nullopt);
|
||||
};
|
||||
|
||||
AMDPlugin::~AMDPlugin() {
|
||||
@@ -60,14 +58,15 @@ AMDPlugin::~AMDPlugin() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::variant<ReadError, ReadableValue>AMDPlugin::libdrmRead(amdgpu_device_handle dev,
|
||||
uint query,
|
||||
std::optional<std::function<T(T)>> transformFunc) {
|
||||
std::variant<ReadError, ReadableValue> AMDPlugin::libdrmRead(
|
||||
amdgpu_device_handle dev, uint query, std::optional<std::function<T(T)>> transformFunc) {
|
||||
T value;
|
||||
auto retval = amdgpu_query_sensor_info(dev, query, sizeof(T), &value);
|
||||
if (retval != 0) return ReadError::UnknownError;
|
||||
|
||||
if (!transformFunc.has_value()) return value;
|
||||
if (retval != 0)
|
||||
return ReadError::UnknownError;
|
||||
|
||||
if (!transformFunc.has_value())
|
||||
return value;
|
||||
return transformFunc.value()(value);
|
||||
}
|
||||
|
||||
@@ -76,95 +75,89 @@ AMDPlugin::AMDPlugin() {
|
||||
std::string path;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
|
||||
std::vector<FSInfo> infoVec;
|
||||
// Iterate through files in GPU device folder and find which ones have amdgpu loaded
|
||||
for (const auto &entry : fs::directory_iterator(DRM_DIR_NAME)) {
|
||||
// Check if entry is renderD file
|
||||
if (entry.path().string().find(DRM_RENDER_MINOR_NAME) != std::string::npos) {
|
||||
infoVec.push_back(FSInfo{entry.path().string(), entry.path().filename().string()});
|
||||
infoVec.push_back(
|
||||
FSInfo{entry.path().string(), entry.path().filename().string()});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &info : infoVec) {
|
||||
auto fd = open(info.path.c_str(), O_RDONLY);
|
||||
auto v_ptr = drmGetVersion(fd);
|
||||
amdgpu_device_handle dev;
|
||||
uint32_t m, n;
|
||||
int devInitRetval;
|
||||
if (fd > 0 &&
|
||||
v_ptr &&
|
||||
if (fd > 0 && v_ptr &&
|
||||
std::string(v_ptr->name).find(_AMDGPU_NAME) != std::string::npos &&
|
||||
(devInitRetval = amdgpu_device_initialize(fd, &m, &n, &dev)) == 0) {
|
||||
// Device uses amdgpu
|
||||
// Find hwmon path if available
|
||||
std::ostringstream stream;
|
||||
stream << "/sys/class/drm/" << info.filename << "/device/hwmon";
|
||||
|
||||
std::optional<std::string> hwmonPath = std::nullopt;
|
||||
try {
|
||||
for (const auto &entry : fs::directory_iterator(stream.str())) {
|
||||
if (entry.path().filename().string().find("hwmon") != std::string::npos) {
|
||||
hwmonPath = entry.path().string();
|
||||
break;
|
||||
}
|
||||
// Device uses amdgpu
|
||||
// Find hwmon path if available
|
||||
std::ostringstream stream;
|
||||
stream << "/sys/class/drm/" << info.filename << "/device/hwmon";
|
||||
|
||||
std::optional<std::string> hwmonPath = std::nullopt;
|
||||
try {
|
||||
for (const auto &entry : fs::directory_iterator(stream.str())) {
|
||||
if (entry.path().filename().string().find("hwmon") !=
|
||||
std::string::npos) {
|
||||
hwmonPath = entry.path().string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// This exception is only abnormal to get on Linux
|
||||
catch (fs::filesystem_error &e) {}
|
||||
m_GPUInfoVec.push_back(AMDGPUInfo{hwmonPath, dev});
|
||||
continue;
|
||||
}
|
||||
// This exception is only abnormal to get on Linux
|
||||
catch (fs::filesystem_error &e) {
|
||||
}
|
||||
m_GPUInfoVec.push_back(AMDGPUInfo{hwmonPath, dev});
|
||||
continue;
|
||||
}
|
||||
//if (devInitRetval == 0) amdgpu_device_deinitialize(dev);
|
||||
// if (devInitRetval == 0) amdgpu_device_deinitialize(dev);
|
||||
close(fd);
|
||||
drmFreeVersion(v_ptr);
|
||||
}
|
||||
|
||||
|
||||
// Add nodes with successful queries to device node
|
||||
|
||||
|
||||
// Data structure for mapping to DynamicReadables
|
||||
struct UnspecializedReadable {
|
||||
std::function<std::variant<ReadError, ReadableValue>(AMDGPUInfo)> func;
|
||||
std::optional<std::string> unit;
|
||||
std::string nodeName;
|
||||
};
|
||||
|
||||
|
||||
// List of query functions for DynamicReadables
|
||||
std::vector<UnspecializedReadable> rawNodes = {
|
||||
{
|
||||
[&](AMDGPUInfo info) {
|
||||
return libdrmRead<uint>(info.devHandle,
|
||||
AMDGPU_INFO_SENSOR_GPU_TEMP,
|
||||
std::function<uint(uint)>([](uint val) {return val / 1000;}));
|
||||
},
|
||||
std::nullopt,
|
||||
"Temperature"
|
||||
}
|
||||
};
|
||||
|
||||
{[&](AMDGPUInfo info) {
|
||||
return libdrmRead<uint>(info.devHandle, AMDGPU_INFO_SENSOR_GPU_TEMP,
|
||||
std::function<uint(uint)>([](uint val) { return val / 1000; }));
|
||||
},
|
||||
std::nullopt, "Temperature"}};
|
||||
|
||||
for (auto info : m_GPUInfoVec) {
|
||||
auto availableReadables = filter(rawNodes,
|
||||
std::function<bool(UnspecializedReadable)>([info](UnspecializedReadable ur) {
|
||||
auto result = ur.func(info);
|
||||
// Check if this node returns an error or not
|
||||
match(result)(pattern(as<ReadError>(_)) = []{return false;},
|
||||
pattern(_) = []{return true;});
|
||||
return true;
|
||||
}));
|
||||
std::function<bool(UnspecializedReadable)>([info](UnspecializedReadable ur) {
|
||||
auto result = ur.func(info);
|
||||
// Check if this node returns an error or not
|
||||
match(result)(
|
||||
pattern(as<ReadError>(_)) = [] { return false; },
|
||||
pattern(_) = [] { return true; });
|
||||
return true;
|
||||
}));
|
||||
auto specializedNodes = map(availableReadables,
|
||||
std::function<DynamicReadable(UnspecializedReadable)>(
|
||||
[info] (UnspecializedReadable ur) {
|
||||
//auto specializedFunc = specializeFunction<std::variant<ReadError,
|
||||
//ReadableValue>, AMDGPUInfo>(info, ur.func);
|
||||
return DynamicReadable([info, ur]() {
|
||||
return ur.func(info);
|
||||
});
|
||||
std::function<DynamicReadable(UnspecializedReadable)>(
|
||||
[info](UnspecializedReadable ur) {
|
||||
// auto specializedFunc = specializeFunction<std::variant<ReadError,
|
||||
// ReadableValue>, AMDGPUInfo>(info, ur.func);
|
||||
return DynamicReadable([info, ur]() { return ur.func(info); });
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() {
|
||||
return m_rootNode;
|
||||
}
|
||||
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() { return m_rootNode; }
|
||||
|
||||
TUXCLOCKER_PLUGIN_EXPORT(AMDPlugin)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,31 +33,29 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
||||
|
||||
auto conn = QDBusConnection::systemBus();
|
||||
QDBusInterface tuxclockerd("org.tuxclocker", "/", "org.tuxclocker", conn);
|
||||
|
||||
QDBusReply<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>> reply =
|
||||
tuxclockerd.call("flatDeviceTree");
|
||||
|
||||
|
||||
QDBusReply<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>> reply =
|
||||
tuxclockerd.call("flatDeviceTree");
|
||||
|
||||
// Convert to non-dbus flat tree
|
||||
FlatTree<TCDBus::DeviceNode> flatTree;
|
||||
if (reply.isValid()) {
|
||||
auto dbusFlatTree = reply.value();
|
||||
for (auto &f_node : dbusFlatTree) {
|
||||
//qDebug() << f_node.value.interface << f_node.value.path;
|
||||
// qDebug() << f_node.value.interface << f_node.value.path;
|
||||
FlatTreeNode<TCDBus::DeviceNode> node{
|
||||
f_node.value,
|
||||
f_node.childIndices.toStdVector()
|
||||
};
|
||||
f_node.value, f_node.childIndices.toStdVector()};
|
||||
flatTree.nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto root = flatTree.toTree(flatTree);
|
||||
|
||||
|
||||
/*auto view = new QTreeView;
|
||||
view->setItemDelegate(new DeviceModelDelegate);
|
||||
view->setModel(model);
|
||||
setCentralWidget(view);*/
|
||||
|
||||
|
||||
auto model = new DeviceModel(root);
|
||||
auto browser = new DeviceBrowser(*model);
|
||||
setCentralWidget(browser);
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
|
||||
class AbstractAssignableConnection {
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
@@ -10,17 +10,16 @@
|
||||
// Forgive me for the sin of multiple inheritance
|
||||
class AssignableItem : public QObject, public QStandardItem {
|
||||
public:
|
||||
AssignableItem(QObject *parent = nullptr) : QObject(parent), QStandardItem() {
|
||||
}
|
||||
bool committal() {return m_committed;}
|
||||
AssignableItem(QObject *parent = nullptr) : QObject(parent), QStandardItem() {}
|
||||
bool committal() { return m_committed; }
|
||||
// Whether or not the set value shall be applied. Doesn't reset or change it.
|
||||
void setCommittal(bool on) {m_committed = on;}
|
||||
void setCommittal(bool on) { m_committed = on; }
|
||||
void setData(const QVariant &v, int role = Qt::UserRole + 1);
|
||||
signals:
|
||||
void assignableDataChanged(QVariant value);
|
||||
void committalChanged(bool on);
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
bool m_committed = false;
|
||||
};
|
||||
|
||||
@@ -7,18 +7,19 @@ namespace TC = TuxClocker;
|
||||
|
||||
class AssignableItemData {
|
||||
public:
|
||||
AssignableItemData() {m_enabled = false;}
|
||||
AssignableItemData(TC::Device::AssignableInfo info, std::optional<QString> unit) : m_info(info) {
|
||||
AssignableItemData() { m_enabled = false; }
|
||||
AssignableItemData(TC::Device::AssignableInfo info, std::optional<QString> unit)
|
||||
: m_info(info) {
|
||||
m_unit = unit;
|
||||
m_enabled = false;
|
||||
}
|
||||
TC::Device::AssignableInfo assignableInfo() {return m_info;}
|
||||
bool committal() {return m_enabled;}
|
||||
TC::Device::AssignableInfo assignableInfo() { return m_info; }
|
||||
bool committal() { return m_enabled; }
|
||||
// Whether or not the set value shall be applied. Doesn't reset or change it.
|
||||
void setCommittal(bool on) {m_enabled = on;}
|
||||
void setValue(QVariant v) {m_targetValue = v;}
|
||||
QVariant value() {return m_targetValue;}
|
||||
std::optional<QString> unit() {return m_unit;}
|
||||
void setCommittal(bool on) { m_enabled = on; }
|
||||
void setValue(QVariant v) { m_targetValue = v; }
|
||||
QVariant value() { return m_targetValue; }
|
||||
std::optional<QString> unit() { return m_unit; }
|
||||
private:
|
||||
bool m_enabled;
|
||||
TC::Device::AssignableInfo m_info;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "AssignableProxy.hpp"
|
||||
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusMessage>
|
||||
@@ -13,22 +12,22 @@ Q_DECLARE_METATYPE(TCD::Result<int>)
|
||||
Q_DECLARE_METATYPE(TCD::Result<QString>)
|
||||
Q_DECLARE_METATYPE(TCD::Result<QDBusVariant>)
|
||||
|
||||
AssignableProxy::AssignableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent) : QObject(parent) {
|
||||
AssignableProxy::AssignableProxy(QString path, QDBusConnection conn, QObject *parent)
|
||||
: QObject(parent) {
|
||||
qDBusRegisterMetaType<TCD::Result<int>>();
|
||||
qDBusRegisterMetaType<TCD::Result<QString>>();
|
||||
qDBusRegisterMetaType<TCD::Result<QDBusVariant>>();
|
||||
m_iface = new QDBusInterface("org.tuxclocker",
|
||||
path, "org.tuxclocker.Assignable", conn, this);
|
||||
m_iface =
|
||||
new QDBusInterface("org.tuxclocker", path, "org.tuxclocker.Assignable", conn, this);
|
||||
}
|
||||
|
||||
std::optional<AssignmentError> AssignableProxy::doApply(const QVariant &v) {
|
||||
//qDebug() << v;
|
||||
// qDebug() << v;
|
||||
QDBusReply<TCD::Result<int>> reply = m_iface->call("assign", v);
|
||||
if (reply.isValid()) {
|
||||
TCD::Result<AssignmentError> ar{reply.value().error,
|
||||
static_cast<AssignmentError>(reply.value().value)};
|
||||
//qDebug("Success!");
|
||||
TCD::Result<AssignmentError> ar{
|
||||
reply.value().error, static_cast<AssignmentError>(reply.value().value)};
|
||||
// qDebug("Success!");
|
||||
return ar.toOptional();
|
||||
}
|
||||
// TODO: indicate dbus error
|
||||
@@ -39,7 +38,7 @@ void AssignableProxy::apply() {
|
||||
// A value hasn't been set yet
|
||||
if (m_value.isNull())
|
||||
return;
|
||||
|
||||
|
||||
// Use QDBusVariant since otherwise tries to call with the wrong signature
|
||||
QDBusVariant dv(m_value);
|
||||
QVariant v;
|
||||
@@ -51,13 +50,14 @@ void AssignableProxy::apply() {
|
||||
|
||||
void AssignableProxy::startConnection(std::shared_ptr<AssignableConnection> conn) {
|
||||
m_assignableConnection = conn;
|
||||
connect(conn.get(), &AssignableConnection::targetValueChanged, [this](auto targetValue, auto text) {
|
||||
auto err = doApply(targetValue);
|
||||
if (err.has_value())
|
||||
emit connectionValueChanged(err.value(), text);
|
||||
else
|
||||
emit connectionValueChanged(targetValue, text);
|
||||
});
|
||||
connect(conn.get(), &AssignableConnection::targetValueChanged,
|
||||
[this](auto targetValue, auto text) {
|
||||
auto err = doApply(targetValue);
|
||||
if (err.has_value())
|
||||
emit connectionValueChanged(err.value(), text);
|
||||
else
|
||||
emit connectionValueChanged(targetValue, text);
|
||||
});
|
||||
// Emit started signal in case a connection emits a new value right away
|
||||
emit connectionStarted();
|
||||
m_assignableConnection->start();
|
||||
@@ -70,18 +70,17 @@ std::optional<AssignmentArgument> toAssignmentArgument(TCD::Result<QDBusVariant>
|
||||
|
||||
auto type = static_cast<QMetaType::Type>(res.value.variant().type());
|
||||
auto v = res.value.variant();
|
||||
|
||||
switch (type) {
|
||||
case QMetaType::Int:
|
||||
return v.value<int>();
|
||||
case QMetaType::UInt:
|
||||
return v.value<uint>();
|
||||
case QMetaType::Double:
|
||||
return v.value<double>();
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case QMetaType::Int:
|
||||
return v.value<int>();
|
||||
case QMetaType::UInt:
|
||||
return v.value<uint>();
|
||||
case QMetaType::Double:
|
||||
return v.value<double>();
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<AssignmentArgument> AssignableProxy::currentValue() {
|
||||
|
||||
@@ -15,23 +15,22 @@ namespace TC = TuxClocker;
|
||||
|
||||
class AssignableProxy : public QObject {
|
||||
public:
|
||||
AssignableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent = nullptr);
|
||||
AssignableProxy(QString path, QDBusConnection conn, QObject *parent = nullptr);
|
||||
void apply();
|
||||
void startConnection(std::shared_ptr<AssignableConnection> conn);
|
||||
// Stop connection and clear current connection
|
||||
void stopConnection();
|
||||
void setValue(QVariant v) {m_value = v;}
|
||||
void setValue(QVariant v) { m_value = v; }
|
||||
std::optional<TC::Device::AssignmentArgument> currentValue();
|
||||
signals:
|
||||
void applied(std::optional<TC::Device::AssignmentError>);
|
||||
void connectionValueChanged(std::variant<QVariant, TC::Device::AssignmentError>,
|
||||
QString text);
|
||||
void connectionValueChanged(
|
||||
std::variant<QVariant, TC::Device::AssignmentError>, QString text);
|
||||
void connectionStarted();
|
||||
void connectionStopped();
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
QVariant m_value;
|
||||
QDBusInterface *m_iface;
|
||||
// This is a bit of a peril but not sure if we can store interfaces any better...
|
||||
|
||||
@@ -17,15 +17,15 @@ using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||
Q_DECLARE_METATYPE(DynamicReadableProxy*)
|
||||
Q_DECLARE_METATYPE(AssignableProxy *)
|
||||
Q_DECLARE_METATYPE(DynamicReadableProxy *)
|
||||
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
||||
Q_DECLARE_METATYPE(TCDBus::Range)
|
||||
Q_DECLARE_METATYPE(EnumerationVec)
|
||||
Q_DECLARE_METATYPE(TCDBus::Result<QString>)
|
||||
|
||||
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent) :
|
||||
QStandardItemModel(parent) {
|
||||
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
||||
: QStandardItemModel(parent) {
|
||||
qDBusRegisterMetaType<TCDBus::Enumeration>();
|
||||
qDBusRegisterMetaType<QVector<TCDBus::Enumeration>>();
|
||||
qDBusRegisterMetaType<TCDBus::Range>();
|
||||
@@ -33,111 +33,111 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
||||
|
||||
/* Data storage:
|
||||
- Interface column should store assignable info for editors
|
||||
- Name colums should store the interface type for filtering
|
||||
- Parametrization/connection data, where? */
|
||||
|
||||
- Name colums should store the interface type for filtering
|
||||
- Parametrization/connection data, where? */
|
||||
|
||||
setColumnCount(2);
|
||||
|
||||
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||
QStandardItem*)> traverse;
|
||||
|
||||
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node, QStandardItem *)> traverse;
|
||||
traverse = [&traverse, this](auto node, auto item) {
|
||||
auto conn = QDBusConnection::systemBus();
|
||||
QDBusInterface nodeIface("org.tuxclocker",
|
||||
node.value().path,"org.tuxclocker.Node", conn);
|
||||
QDBusInterface nodeIface(
|
||||
"org.tuxclocker", node.value().path, "org.tuxclocker.Node", conn);
|
||||
auto nodeName = nodeIface.property("name").toString();
|
||||
|
||||
QList<QStandardItem*> rowItems;
|
||||
|
||||
QList<QStandardItem *> rowItems;
|
||||
auto nameItem = new QStandardItem;
|
||||
nameItem->setText(nodeName);
|
||||
rowItems.append(nameItem);
|
||||
|
||||
p::match(node.value().interface) (
|
||||
pattern("org.tuxclocker.Assignable") = [=, &rowItems]{
|
||||
if_let(pattern(some(arg)) = setupAssignable(node, conn))
|
||||
= [&](auto item) {
|
||||
|
||||
p::match(node.value().interface)(
|
||||
pattern("org.tuxclocker.Assignable") =
|
||||
[=, &rowItems] {
|
||||
if_let(pattern(some(arg)) =
|
||||
setupAssignable(node, conn)) = [&](auto item) {
|
||||
nameItem->setData(Assignable, InterfaceTypeRole);
|
||||
auto icon = assignableIcon();
|
||||
nameItem->setData(icon, Qt::DecorationRole);
|
||||
rowItems.append(item);
|
||||
};
|
||||
},
|
||||
pattern("org.tuxclocker.DynamicReadable") = [=, &rowItems] {
|
||||
if_let(pattern(some(arg)) = setupDynReadable(node, conn))
|
||||
= [&](auto item) {
|
||||
pattern("org.tuxclocker.DynamicReadable") =
|
||||
[=, &rowItems] {
|
||||
if_let(pattern(some(arg)) =
|
||||
setupDynReadable(node, conn)) = [&](auto item) {
|
||||
auto icon = dynamicReadableIcon();
|
||||
nameItem->setData(icon, Qt::DecorationRole);
|
||||
|
||||
nameItem->setData(DeviceModel::DynamicReadable,
|
||||
InterfaceTypeRole);
|
||||
nameItem->setData(icon, Qt::DecorationRole);
|
||||
|
||||
nameItem->setData(
|
||||
DeviceModel::DynamicReadable, InterfaceTypeRole);
|
||||
rowItems.append(item);
|
||||
//qDebug() << item->data(DynamicReadableProxyRole);
|
||||
// qDebug() << item->data(DynamicReadableProxyRole);
|
||||
};
|
||||
},
|
||||
pattern("org.tuxclocker.StaticReadable") = [=, &rowItems] {
|
||||
if_let(pattern(some(arg)) = setupStaticReadable(node, conn))
|
||||
= [&](auto item) {
|
||||
pattern("org.tuxclocker.StaticReadable") =
|
||||
[=, &rowItems] {
|
||||
if_let(pattern(some(arg)) =
|
||||
setupStaticReadable(node, conn)) = [&](auto item) {
|
||||
auto icon = staticReadableIcon();
|
||||
nameItem->setData(icon, Qt::DecorationRole);
|
||||
nameItem->setData(DeviceModel::StaticReadable,
|
||||
InterfaceTypeRole);
|
||||
nameItem->setData(
|
||||
DeviceModel::StaticReadable, InterfaceTypeRole);
|
||||
rowItems.append(item);
|
||||
};
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);
|
||||
pattern(_) = [] {});
|
||||
item->appendRow(rowItems);
|
||||
|
||||
|
||||
for (auto c_node : node.children())
|
||||
traverse(c_node, nameItem);
|
||||
};
|
||||
auto rootItem = invisibleRootItem();
|
||||
|
||||
|
||||
for (auto &node : root.children())
|
||||
traverse(node, rootItem);
|
||||
}
|
||||
|
||||
EnumerationVec toEnumVec(QVector<TCDBus::Enumeration> enums) {
|
||||
std::vector<TCDBus::Enumeration> stdEnumVec(enums.begin(), enums.end());
|
||||
return transform([](auto e) {
|
||||
return Enumeration{e.name.toStdString(), e.key};
|
||||
}, stdEnumVec);
|
||||
return transform(
|
||||
[](auto e) {
|
||||
return Enumeration{e.name.toStdString(), e.key};
|
||||
},
|
||||
stdEnumVec);
|
||||
}
|
||||
|
||||
std::optional<const AssignableProxy*>
|
||||
DeviceModel::assignableProxyFromItem(QStandardItem *item) {
|
||||
return (m_assignableProxyHash.contains(item)) ?
|
||||
std::optional(m_assignableProxyHash.value(item)) :
|
||||
std::nullopt;
|
||||
std::optional<const AssignableProxy *> DeviceModel::assignableProxyFromItem(QStandardItem *item) {
|
||||
return (m_assignableProxyHash.contains(item))
|
||||
? std::optional(m_assignableProxyHash.value(item))
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
QString fromAssignmentArgument(AssignmentArgument a_arg) {
|
||||
return p::match(a_arg) (
|
||||
pattern(as<int>(arg)) = [](auto i) {return QString::number(i);},
|
||||
pattern(as<uint>(arg)) = [](auto u) {return QString::number(u);},
|
||||
pattern(as<double>(arg)) = [](auto d) {return QString::number(d);},
|
||||
pattern(_) = [] {return QString("");}
|
||||
);
|
||||
return p::match(a_arg)(
|
||||
pattern(as<int>(arg)) = [](auto i) { return QString::number(i); },
|
||||
pattern(as<uint>(arg)) = [](auto u) { return QString::number(u); },
|
||||
pattern(as<double>(arg)) = [](auto d) { return QString::number(d); },
|
||||
pattern(_) = [] { return QString(""); });
|
||||
}
|
||||
|
||||
QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||
QDBusConnection conn, AssignableItemData itemData) {
|
||||
QStandardItem *DeviceModel::createAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn, AssignableItemData itemData) {
|
||||
auto ifaceItem = new AssignableItem(this);
|
||||
auto proxy = new AssignableProxy(node.value().path, conn, this);
|
||||
|
||||
connect(proxy, &AssignableProxy::connectionValueChanged, [=] (auto result, auto text) {
|
||||
p::match(result) (
|
||||
pattern(as<QVariant>(arg)) = [=](auto v) {
|
||||
|
||||
connect(proxy, &AssignableProxy::connectionValueChanged, [=](auto result, auto text) {
|
||||
p::match(result)(
|
||||
pattern(as<QVariant>(arg)) =
|
||||
[=](auto v) {
|
||||
QVariant data;
|
||||
data.setValue(connectionColor());
|
||||
ifaceItem->setData(data, Qt::BackgroundRole);
|
||||
ifaceItem->setText(text);
|
||||
//qDebug() << text;
|
||||
// qDebug() << text;
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);
|
||||
pattern(_) = [] {});
|
||||
});
|
||||
|
||||
|
||||
|
||||
QVariant pv;
|
||||
pv.setValue(proxy);
|
||||
ifaceItem->setData(pv, AssignableProxyRole);
|
||||
@@ -151,11 +151,12 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
|
||||
auto currentValue = proxy->currentValue();
|
||||
|
||||
if (currentValue.has_value()) {
|
||||
p::match(itemData.assignableInfo()) (
|
||||
pattern(as<EnumerationVec>(arg)) = [&](auto e_vec) {
|
||||
/* Find index from EnumVec (O(n) doesn't matter since we only search once here)
|
||||
This should never be anything other than an uint but in theory it could be
|
||||
whatever at this point */
|
||||
p::match(itemData.assignableInfo())(
|
||||
pattern(as<EnumerationVec>(arg)) =
|
||||
[&](auto e_vec) {
|
||||
/* Find index from EnumVec (O(n) doesn't matter since we only search
|
||||
once here) This should never be anything other than an uint but
|
||||
in theory it could be whatever at this point */
|
||||
try {
|
||||
auto index = std::get<uint>(currentValue.value());
|
||||
for (auto &e : e_vec) {
|
||||
@@ -164,58 +165,58 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (std::bad_variant_access &e) {}
|
||||
} catch (std::bad_variant_access &e) {
|
||||
}
|
||||
|
||||
//text = QString::fromStdString(e_vec[std::get<uint>(currentValue.value())].name);
|
||||
// text =
|
||||
// QString::fromStdString(e_vec[std::get<uint>(currentValue.value())].name);
|
||||
},
|
||||
pattern(as<RangeInfo>(_)) = [&]() {
|
||||
pattern(as<RangeInfo>(_)) =
|
||||
[&]() {
|
||||
auto base = fromAssignmentArgument(currentValue.value());
|
||||
if (unit.has_value())
|
||||
text = QString("%1 %2").arg(base, unit.value());
|
||||
else
|
||||
text = base;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
ifaceItem->setText(text);
|
||||
|
||||
connect(ifaceItem, &AssignableItem::assignableDataChanged,
|
||||
[=](QVariant v) {
|
||||
connect(ifaceItem, &AssignableItem::assignableDataChanged, [=](QVariant v) {
|
||||
// Only show checkbox when value has been changed
|
||||
ifaceItem->setCheckable(true);
|
||||
ifaceItem->setCheckState(Qt::Checked);
|
||||
proxy->setValue(v);
|
||||
ifaceItem->setData(unappliedColor(), Qt::BackgroundRole);
|
||||
});
|
||||
|
||||
|
||||
connect(ifaceItem, &AssignableItem::committalChanged, [=](bool on) {
|
||||
QVariant colorData = (on) ? unappliedColor() : QVariant();
|
||||
ifaceItem->setData(colorData, Qt::BackgroundRole);
|
||||
});
|
||||
|
||||
|
||||
connect(proxy, &AssignableProxy::applied, [=](auto err) {
|
||||
// Fade out result color
|
||||
auto startColor = (err.has_value()) ? errorColor()
|
||||
: successColor();
|
||||
auto startColor = (err.has_value()) ? errorColor() : successColor();
|
||||
auto anim = new QVariantAnimation;
|
||||
anim->setDuration(fadeOutTime());
|
||||
anim->setStartValue(startColor);
|
||||
anim->setEndValue(QPalette().color(QPalette::Base));
|
||||
|
||||
|
||||
connect(anim, &QVariantAnimation::valueChanged, [=](QVariant v) {
|
||||
QVariant iv;
|
||||
iv.setValue(v.value<QColor>());
|
||||
ifaceItem->setData(iv, Qt::BackgroundRole);
|
||||
});
|
||||
|
||||
|
||||
connect(anim, &QVariantAnimation::finished, [=] {
|
||||
// Set invalid color to 'reset' the color
|
||||
ifaceItem->setData(QVariant(), Qt::BackgroundRole);
|
||||
});
|
||||
|
||||
|
||||
anim->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
});
|
||||
|
||||
|
||||
connect(this, &DeviceModel::changesApplied, [=] {
|
||||
// Don't apply if unchecked
|
||||
if (ifaceItem->checkState() == Qt::Checked) {
|
||||
@@ -232,13 +233,11 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
|
||||
QVariant DeviceModel::data(const QModelIndex &index, int role) const {
|
||||
if (index.row() != DeviceModel::NameColumn && role == DeviceModel::NodeNameRole) {
|
||||
// Get name from adjacent column
|
||||
auto nameIndex =
|
||||
this->index(index.row(), DeviceModel::NameColumn, index.parent());
|
||||
auto nameIndex = this->index(index.row(), DeviceModel::NameColumn, index.parent());
|
||||
return nameIndex.data(Qt::DisplayRole);
|
||||
}
|
||||
if (index.column() != InterfaceColumn && role == DynamicReadableProxyRole) {
|
||||
auto idx =
|
||||
this->index(index.row(), DeviceModel::InterfaceColumn, index.parent());
|
||||
auto idx = this->index(index.row(), DeviceModel::InterfaceColumn, index.parent());
|
||||
return idx.data(DynamicReadableProxyRole);
|
||||
}
|
||||
return QStandardItemModel::data(index, role);
|
||||
@@ -250,44 +249,38 @@ std::optional<QString> fromDBusResult(TCDBus::Result<QString> res) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
std::optional<QStandardItem*> DeviceModel::setupAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
|
||||
"org.tuxclocker.Assignable", conn);
|
||||
std::optional<QStandardItem *> DeviceModel::setupAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
QDBusInterface ifaceNode(
|
||||
"org.tuxclocker", node.value().path, "org.tuxclocker.Assignable", conn);
|
||||
|
||||
// Calling QObject::property used to work for getting (some) DBus properties but now we have to do this..
|
||||
QDBusInterface propIface{"org.tuxclocker", node.value().path,
|
||||
"org.freedesktop.DBus.Properties", conn};
|
||||
QDBusReply<QDBusVariant> a_reply = propIface.call("Get", "org.tuxclocker.Assignable", "assignableInfo");
|
||||
QDBusReply<QDBusVariant> u_reply = propIface.call("Get", "org.tuxclocker.Assignable", "unit");
|
||||
// Calling QObject::property used to work for getting (some) DBus properties but now we have
|
||||
// to do this..
|
||||
QDBusInterface propIface{
|
||||
"org.tuxclocker", node.value().path, "org.freedesktop.DBus.Properties", conn};
|
||||
QDBusReply<QDBusVariant> a_reply =
|
||||
propIface.call("Get", "org.tuxclocker.Assignable", "assignableInfo");
|
||||
QDBusReply<QDBusVariant> u_reply =
|
||||
propIface.call("Get", "org.tuxclocker.Assignable", "unit");
|
||||
|
||||
if (!a_reply.isValid() || !u_reply.isValid()) {
|
||||
// This code path shouldn't be reached
|
||||
// The DBus path is contained in the error message
|
||||
qWarning("Could not get assignableInfo or unit for Assignable "
|
||||
"due to error(s) \"%s\" and \"%s\"",
|
||||
qPrintable(a_reply.error().message()),
|
||||
qPrintable(u_reply.error().message()));
|
||||
"due to error(s) \"%s\" and \"%s\"",
|
||||
qPrintable(a_reply.error().message()), qPrintable(u_reply.error().message()));
|
||||
return std::nullopt;
|
||||
}
|
||||
// Calling QDBusReply::value() should ge safe here
|
||||
// Need to serialize manually into the proper types
|
||||
auto unit =
|
||||
fromDBusResult(
|
||||
qdbus_cast<TCDBus::Result<QString>>(u_reply
|
||||
.value()
|
||||
.variant()
|
||||
.value<QDBusArgument>()));
|
||||
auto unit = fromDBusResult(
|
||||
qdbus_cast<TCDBus::Result<QString>>(u_reply.value().variant().value<QDBusArgument>()));
|
||||
|
||||
// What a mess...
|
||||
auto assInfoRaw = a_reply
|
||||
.value()
|
||||
.variant()
|
||||
.value<QDBusVariant>()
|
||||
.variant()
|
||||
.value<QDBusArgument>();
|
||||
auto assInfoRaw =
|
||||
a_reply.value().variant().value<QDBusVariant>().variant().value<QDBusArgument>();
|
||||
// TODO: get initial value
|
||||
|
||||
|
||||
// Use DBus signature to know the type of Assignable
|
||||
if (assInfoRaw.currentSignature() == "(vv)") {
|
||||
// RangeInfo
|
||||
@@ -304,69 +297,62 @@ std::optional<QStandardItem*> DeviceModel::setupAssignable(
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void updateReadItemText(QStandardItem *item, T value,
|
||||
std::optional<QString> unit) {
|
||||
void updateReadItemText(QStandardItem *item, T value, std::optional<QString> unit) {
|
||||
// TODO: this can be made a lot (around 3x) faster by using direct copying
|
||||
// Form a string of the form "1000 MHz" if has unit
|
||||
auto text = (unit.has_value()) ?
|
||||
QString("%1 %2").arg(value).arg(unit.value()) :
|
||||
QString("%1").arg(value);
|
||||
auto text = (unit.has_value()) ? QString("%1 %2").arg(value).arg(unit.value())
|
||||
: QString("%1").arg(value);
|
||||
item->setText(text);
|
||||
//qDebug() << item->data(DeviceModel::DynamicReadableProxyRole);
|
||||
// qDebug() << item->data(DeviceModel::DynamicReadableProxyRole);
|
||||
}
|
||||
|
||||
std::optional<QStandardItem*> DeviceModel::setupDynReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
std::optional<QStandardItem *> DeviceModel::setupDynReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
auto item = new QStandardItem;
|
||||
auto proxy = new DynamicReadableProxy(node.value().path, conn, this);
|
||||
QVariant v;
|
||||
v.setValue(proxy);
|
||||
item->setData(v, DynamicReadableProxyRole);
|
||||
auto unit = proxy->unit();
|
||||
|
||||
auto unit = proxy->unit();
|
||||
|
||||
connect(proxy, &DynamicReadableProxy::valueChanged, [=](ReadResult res) {
|
||||
p::match(res)(
|
||||
pattern(as<ReadableValue>(arg)) = [=](auto rv) {
|
||||
pattern(as<ReadableValue>(arg)) =
|
||||
[=](auto rv) {
|
||||
p::match(rv)(
|
||||
pattern(as<double>(arg)) = [=](auto d) {
|
||||
updateReadItemText(item, d, unit);
|
||||
},
|
||||
pattern(as<int>(arg)) = [=](auto i) {
|
||||
updateReadItemText(item, i, unit);
|
||||
},
|
||||
pattern(as<uint>(arg)) = [=](auto u) {
|
||||
updateReadItemText(item, u, unit);
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);
|
||||
pattern(as<double>(arg)) =
|
||||
[=](auto d) { updateReadItemText(item, d, unit); },
|
||||
pattern(as<int>(arg)) =
|
||||
[=](auto i) { updateReadItemText(item, i, unit); },
|
||||
pattern(as<uint>(arg)) =
|
||||
[=](auto u) { updateReadItemText(item, u, unit); },
|
||||
pattern(_) = [] {});
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);
|
||||
pattern(_) = [] {});
|
||||
});
|
||||
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
std::optional<QStandardItem*> DeviceModel::setupStaticReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
QDBusInterface staticIface("org.tuxclocker", node.value().path,
|
||||
"org.tuxclocker.StaticReadable", conn);
|
||||
auto value = staticIface.property("value")
|
||||
.value<QDBusVariant>().variant().toString();
|
||||
std::optional<QStandardItem *> DeviceModel::setupStaticReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
QDBusInterface staticIface(
|
||||
"org.tuxclocker", node.value().path, "org.tuxclocker.StaticReadable", conn);
|
||||
auto value = staticIface.property("value").value<QDBusVariant>().variant().toString();
|
||||
// Workaround from DynamicReadableProxy for getting property with custom type
|
||||
QDBusInterface propIface("org.tuxclocker", node.value().path,
|
||||
"org.freedesktop.DBus.Properties", conn);
|
||||
QDBusInterface propIface(
|
||||
"org.tuxclocker", node.value().path, "org.freedesktop.DBus.Properties", conn);
|
||||
QDBusReply<QDBusVariant> reply =
|
||||
propIface.call("Get", "org.tuxclocker.StaticReadable", "unit");
|
||||
propIface.call("Get", "org.tuxclocker.StaticReadable", "unit");
|
||||
if (!reply.isValid())
|
||||
return std::nullopt;
|
||||
auto arg = reply.value().variant().value<QDBusArgument>();
|
||||
TCDBus::Result<QString> unit;
|
||||
arg >> unit;
|
||||
|
||||
|
||||
if (!unit.error)
|
||||
value += " " + unit.value;
|
||||
|
||||
|
||||
auto item = new QStandardItem;
|
||||
item->setData(value, Qt::DisplayRole);
|
||||
return item;
|
||||
|
||||
@@ -27,19 +27,19 @@ class DeviceModel : public QStandardItemModel {
|
||||
public:
|
||||
DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent = nullptr);
|
||||
enum ColumnType {
|
||||
NameColumn = 0, // Node name
|
||||
NameColumn = 0, // Node name
|
||||
InterfaceColumn = 1 // Column for presenting interfaces
|
||||
};
|
||||
|
||||
|
||||
enum Role {
|
||||
AssignableRole = Qt::UserRole, // Holds the data about the assignable
|
||||
AssignableProxyRole,
|
||||
ConnectionRole, // Data about the connection
|
||||
DynamicReadableProxyRole,
|
||||
InterfaceTypeRole, // InterfaceType
|
||||
NodeNameRole //
|
||||
NodeNameRole //
|
||||
};
|
||||
|
||||
|
||||
enum InterfaceFlag {
|
||||
Assignable = 1,
|
||||
DynamicReadable = 2,
|
||||
@@ -49,36 +49,36 @@ public:
|
||||
typedef QFlags<InterfaceFlag> InterfaceFlags;
|
||||
|
||||
// For decoupling AssignableItems created in the model
|
||||
void applyChanges() {emit changesApplied();}
|
||||
void applyChanges() { emit changesApplied(); }
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
static QIcon assignableIcon() {return QIcon::fromTheme("edit-entry");}
|
||||
static QIcon assignableIcon() { return QIcon::fromTheme("edit-entry"); }
|
||||
// Get AssignableProxy of an item
|
||||
std::optional<const AssignableProxy*> assignableProxyFromItem(QStandardItem *item);
|
||||
std::optional<const AssignableProxy *> assignableProxyFromItem(QStandardItem *item);
|
||||
// AssignableProxies are associated with both row items
|
||||
//QVariant data(QModelIndex&);
|
||||
static QIcon dynamicReadableIcon() {return QIcon(":/ruler.svg");}
|
||||
static QIcon staticReadableIcon() {return QIcon::fromTheme("help-about");}
|
||||
// QVariant data(QModelIndex&);
|
||||
static QIcon dynamicReadableIcon() { return QIcon(":/ruler.svg"); }
|
||||
static QIcon staticReadableIcon() { return QIcon::fromTheme("help-about"); }
|
||||
signals:
|
||||
void changesApplied();
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
QHash<QStandardItem*, AssignableProxy*> m_assignableProxyHash;
|
||||
|
||||
|
||||
QHash<QStandardItem *, AssignableProxy *> m_assignableProxyHash;
|
||||
|
||||
// Separate handling interfaces since otherwise we run out of columns
|
||||
QStandardItem *createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||
QDBusConnection conn, AssignableItemData data);
|
||||
std::optional<QStandardItem*> setupAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
|
||||
std::optional<QStandardItem*> setupDynReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
|
||||
std::optional<QStandardItem*> setupStaticReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
|
||||
constexpr int fadeOutTime() {return 5000;} // milliseconds
|
||||
constexpr int transparency() {return 120;} // 0-255
|
||||
QStandardItem *createAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn, AssignableItemData data);
|
||||
std::optional<QStandardItem *> setupAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
|
||||
std::optional<QStandardItem *> setupDynReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
|
||||
std::optional<QStandardItem *> setupStaticReadable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
|
||||
constexpr int fadeOutTime() { return 5000; } // milliseconds
|
||||
constexpr int transparency() { return 120; } // 0-255
|
||||
// Colors for items
|
||||
QColor connectionColor() {return QColor(0, 0, 255, transparency());} // blue
|
||||
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
|
||||
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
|
||||
QColor successColor() {return QColor(0, 255, 0, transparency());} // green
|
||||
QColor connectionColor() { return QColor(0, 0, 255, transparency()); } // blue
|
||||
QColor errorColor() { return QColor(255, 0, 0, transparency()); } // red
|
||||
QColor unappliedColor() { return QColor(255, 255, 0, transparency()); } // yellow
|
||||
QColor successColor() { return QColor(0, 255, 0, transparency()); } // green
|
||||
};
|
||||
|
||||
@@ -13,66 +13,61 @@ using namespace mpark::patterns;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
|
||||
DeviceModelDelegate::DeviceModelDelegate(QObject* parent) :
|
||||
QStyledItemDelegate(parent) {}
|
||||
|
||||
QWidget *DeviceModelDelegate::createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem&, const QModelIndex &index) const {
|
||||
DeviceModelDelegate::DeviceModelDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
|
||||
|
||||
QWidget *DeviceModelDelegate::createEditor(
|
||||
QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const {
|
||||
auto v = index.data(DeviceModel::AssignableRole);
|
||||
QWidget *editor = nullptr;
|
||||
if (v.canConvert<AssignableItemData>()) {
|
||||
match(v.value<AssignableItemData>().assignableInfo())
|
||||
(pattern(as<RangeInfo>(arg)) = [&](auto r_info) {
|
||||
match(r_info)
|
||||
(pattern(as<Range<int>>(arg)) = [&](auto ir) {
|
||||
editor = new IntRangeEditor(ir, parent);
|
||||
},
|
||||
pattern(as<Range<double>>(arg)) = [&](auto dr) {
|
||||
match(v.value<AssignableItemData>().assignableInfo())(
|
||||
pattern(as<RangeInfo>(arg)) =
|
||||
[&](auto r_info) {
|
||||
match(r_info)(
|
||||
pattern(as<Range<int>>(arg)) =
|
||||
[&](auto ir) { editor = new IntRangeEditor(ir, parent); },
|
||||
pattern(as<Range<double>>(arg)) =
|
||||
[&](auto dr) {
|
||||
editor = new DoubleRangeEditor(dr, parent);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
pattern(as<EnumerationVec>(arg)) = [&](auto ev) {
|
||||
editor = new EnumEditor(ev, parent);
|
||||
}
|
||||
);
|
||||
pattern(as<EnumerationVec>(arg)) =
|
||||
[&](auto ev) { editor = new EnumEditor(ev, parent); });
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::setEditorData(QWidget *editor,
|
||||
const QModelIndex &index) const {
|
||||
void DeviceModelDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
|
||||
auto v = index.data(DeviceModel::AssignableRole);
|
||||
if (v.canConvert<AssignableItemData>()) {
|
||||
auto data = v.value<AssignableItemData>().value();
|
||||
auto a_editor = static_cast<AbstractAssignableEditor*>(editor);
|
||||
auto a_editor = static_cast<AbstractAssignableEditor *>(editor);
|
||||
a_editor->setAssignableData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::setModelData(QWidget *editor,
|
||||
QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
void DeviceModelDelegate::setModelData(
|
||||
QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
auto v = index.data(DeviceModel::AssignableRole);
|
||||
if (v.canConvert<AssignableItemData>()) {
|
||||
auto a_editor = static_cast<AbstractAssignableEditor*>(editor);
|
||||
auto data = index.data(DeviceModel::AssignableRole)
|
||||
.value<AssignableItemData>();
|
||||
auto a_editor = static_cast<AbstractAssignableEditor *>(editor);
|
||||
auto data = index.data(DeviceModel::AssignableRole).value<AssignableItemData>();
|
||||
data.setValue(a_editor->assignableData());
|
||||
QVariant v;
|
||||
v.setValue(data);
|
||||
// TODO: add unit
|
||||
|
||||
auto text = (data.unit().has_value()) ?
|
||||
QString("%1 %2").arg(a_editor->displayData(), data.unit().value()) :
|
||||
a_editor->displayData();
|
||||
auto text = (data.unit().has_value())
|
||||
? QString("%1 %2").arg(a_editor->displayData(), data.unit().value())
|
||||
: a_editor->displayData();
|
||||
|
||||
model->setData(index, text, Qt::DisplayRole);
|
||||
model->setData(index, v, DeviceModel::AssignableRole);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::updateEditorGeometry(QWidget *editor,
|
||||
const QStyleOptionViewItem &option, const QModelIndex&) const {
|
||||
void DeviceModelDelegate::updateEditorGeometry(
|
||||
QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const {
|
||||
// Why do I need to override this to perform such a basic task?
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
@@ -80,42 +75,36 @@ void DeviceModelDelegate::updateEditorGeometry(QWidget *editor,
|
||||
QColor alphaBlend(QColor top, QColor background) {
|
||||
auto alpha = top.alphaF();
|
||||
auto factor = 1 - alpha;
|
||||
return QColor(
|
||||
(top.red() * alpha) + (factor * background.red()),
|
||||
(top.green() * alpha) + (factor * background.green()),
|
||||
(top.blue() * alpha) + (factor * background.blue())
|
||||
);
|
||||
return QColor((top.red() * alpha) + (factor * background.red()),
|
||||
(top.green() * alpha) + (factor * background.green()),
|
||||
(top.blue() * alpha) + (factor * background.blue()));
|
||||
}
|
||||
|
||||
QColor filter(QColor filter, QColor color) {
|
||||
/* Dominant color lets 95% (in case of a theme using pure r/g/b) of that
|
||||
QColor filter(QColor filter, QColor color) {
|
||||
/* Dominant color lets 95% (in case of a theme using pure r/g/b) of that
|
||||
channel through, the rest 70% */
|
||||
auto factorR = filter.redF();
|
||||
auto factorG = filter.greenF();
|
||||
auto factorB = filter.blueF();
|
||||
|
||||
|
||||
Qt::GlobalColor dominant;
|
||||
|
||||
|
||||
if (factorR > qMax(factorG, factorB))
|
||||
dominant = Qt::red;
|
||||
if (factorG > qMax(factorR, factorB))
|
||||
dominant = Qt::green;
|
||||
if (factorB > qMax(factorR, factorG))
|
||||
dominant = Qt::blue;
|
||||
|
||||
|
||||
factorR = (dominant == Qt::red) ? 0.95 : 0.7;
|
||||
factorG = (dominant == Qt::green) ? 0.95 : 0.7;
|
||||
factorB = (dominant == Qt::blue) ? 0.95 : 0.7;
|
||||
|
||||
return QColor(
|
||||
color.red() * factorR,
|
||||
color.green() * factorG,
|
||||
color.blue() * factorB
|
||||
);
|
||||
|
||||
return QColor(color.red() * factorR, color.green() * factorG, color.blue() * factorB);
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const {
|
||||
void DeviceModelDelegate::paint(
|
||||
QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
auto optCpy = option;
|
||||
auto baseColor = index.data(Qt::BackgroundRole).value<QColor>();
|
||||
if (baseColor.isValid()) {
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
class DeviceModelDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
DeviceModelDelegate(QObject *parent = nullptr);
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const;
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const;
|
||||
QWidget *createEditor(
|
||||
QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
void updateEditorGeometry(
|
||||
QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||
const QModelIndex &index) const;
|
||||
void setModelData(
|
||||
QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
|
||||
protected:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
@@ -4,20 +4,18 @@
|
||||
|
||||
Q_DECLARE_METATYPE(DeviceModel::InterfaceFlag)
|
||||
|
||||
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent) :
|
||||
QSortFilterProxyModel(parent), m_disableFiltered(false),
|
||||
m_showIcons(true), m_showValueColumn(true) {
|
||||
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent)
|
||||
: QSortFilterProxyModel(parent), m_disableFiltered(false), m_showIcons(true),
|
||||
m_showValueColumn(true) {
|
||||
setSourceModel(&model);
|
||||
m_flags = DeviceModel::AllInterfaces;
|
||||
}
|
||||
|
||||
bool DeviceProxyModel::filterAcceptsRow(int sourceRow,
|
||||
const QModelIndex &sourceParent) const {
|
||||
bool DeviceProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
auto model = sourceModel();
|
||||
// Interface type is stored in the item with the name
|
||||
auto thisItem = model->index(sourceRow, DeviceModel::NameColumn,
|
||||
sourceParent);
|
||||
|
||||
auto thisItem = model->index(sourceRow, DeviceModel::NameColumn, sourceParent);
|
||||
|
||||
// Check recursively if a child item has the flag set that we want to show
|
||||
bool shouldHide = true;
|
||||
std::function<void(QModelIndex)> traverse;
|
||||
@@ -41,21 +39,18 @@ bool DeviceProxyModel::filterAcceptsRow(int sourceRow,
|
||||
Qt::ItemFlags DeviceProxyModel::flags(const QModelIndex &index) const {
|
||||
if (!m_disableFiltered)
|
||||
return QSortFilterProxyModel::flags(index);
|
||||
|
||||
auto iface =
|
||||
QSortFilterProxyModel::data(index, DeviceModel::InterfaceTypeRole);
|
||||
|
||||
auto iface = QSortFilterProxyModel::data(index, DeviceModel::InterfaceTypeRole);
|
||||
auto removedFlags = Qt::ItemIsSelectable;
|
||||
auto itemFlags = QSortFilterProxyModel::flags(index);
|
||||
if (!iface.isValid())
|
||||
// Remove selectable flag
|
||||
return itemFlags &(~removedFlags);
|
||||
return itemFlags & (~removedFlags);
|
||||
auto flags = iface.value<DeviceModel::InterfaceFlag>();
|
||||
return (flags & m_flags) ? itemFlags :
|
||||
itemFlags &(~removedFlags);
|
||||
return (flags & m_flags) ? itemFlags : itemFlags & (~removedFlags);
|
||||
}
|
||||
|
||||
bool DeviceProxyModel::lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const {
|
||||
bool DeviceProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
||||
// TODO: doesn't work as expected if sorted from interface column
|
||||
auto leftChildren = sourceModel()->rowCount(left);
|
||||
auto rightChildren = sourceModel()->rowCount(right);
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
class DeviceProxyModel : public QSortFilterProxyModel {
|
||||
public:
|
||||
DeviceProxyModel(DeviceModel &model, QObject *parent = nullptr);
|
||||
DeviceModel::InterfaceFlags filterFlags() {return m_flags;}
|
||||
DeviceModel::InterfaceFlags filterFlags() { return m_flags; }
|
||||
// Disable items that don't contain the interface in m_flags
|
||||
void setDisableFiltered(bool on) {m_disableFiltered = on;}
|
||||
void setDisableFiltered(bool on) { m_disableFiltered = on; }
|
||||
void setFlags(DeviceModel::InterfaceFlags flags) {
|
||||
m_flags = flags;
|
||||
invalidateFilter();
|
||||
@@ -27,16 +27,13 @@ public:
|
||||
invalidateFilter();
|
||||
}
|
||||
protected:
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole)
|
||||
const override {
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
|
||||
if (!m_showIcons && role == Qt::DecorationRole)
|
||||
return QVariant();
|
||||
return QSortFilterProxyModel::data(index, role);
|
||||
}
|
||||
bool filterAcceptsRow(int sourceRow,
|
||||
const QModelIndex &sourceParent) const override;
|
||||
bool filterAcceptsColumn(int sourceColumn, const QModelIndex&)
|
||||
const override {
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const override {
|
||||
return !(!m_showValueColumn && sourceColumn == DeviceModel::InterfaceColumn);
|
||||
}
|
||||
// Optionally disable selection of items that don't have the wanted interface
|
||||
|
||||
@@ -25,20 +25,19 @@ public:
|
||||
QString dynamicReadablePath;
|
||||
};
|
||||
|
||||
// TODO: make DynamicReadableProxy type parametrized so we can be sure to only get numeric values from it
|
||||
// Connection of an Assignable with a DynamicReadable
|
||||
// TODO: make DynamicReadableProxy type parametrized so we can be sure to only get numeric values
|
||||
// from it Connection of an Assignable with a DynamicReadable
|
||||
template <typename OutType> // Result of linear interpolation
|
||||
class DynamicReadableConnection : public AssignableConnection {
|
||||
public:
|
||||
DynamicReadableConnection(DynamicReadableProxy &proxy,
|
||||
QVector<QPointF> points, QObject *parent = nullptr)
|
||||
: AssignableConnection(parent), m_proxy(proxy), m_points(points) {
|
||||
auto sorted = sort_by([](auto point_l, auto point_r) {
|
||||
return point_l.x() < point_r.x();
|
||||
}, m_points);
|
||||
m_points = sorted;
|
||||
}
|
||||
virtual QVariant connectionData() override {return QVariant();}
|
||||
DynamicReadableConnection(
|
||||
DynamicReadableProxy &proxy, QVector<QPointF> points, QObject *parent = nullptr)
|
||||
: AssignableConnection(parent), m_proxy(proxy), m_points(points) {
|
||||
auto sorted = sort_by(
|
||||
[](auto point_l, auto point_r) { return point_l.x() < point_r.x(); }, m_points);
|
||||
m_points = sorted;
|
||||
}
|
||||
virtual QVariant connectionData() override { return QVariant(); }
|
||||
virtual void start() override {
|
||||
/*QObject::connect(&m_timer, &QTimer::timeout, [this] {
|
||||
int rand = QRandomGenerator::global()->bounded(0, 10);
|
||||
@@ -51,34 +50,27 @@ public:
|
||||
m_timer.start(1000);*/
|
||||
|
||||
connect(&m_proxy, &DynamicReadableProxy::valueChanged, [this](auto val) {
|
||||
match(val) (
|
||||
pattern(as<ReadableValue>(arg)) = [this](auto rv) {
|
||||
match(rv) (
|
||||
pattern(as<uint>(arg)) = [this](auto u) {
|
||||
emitTargetValue(u);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
match(val)(pattern(as<ReadableValue>(arg)) = [this](auto rv) {
|
||||
match(rv)(pattern(as<uint>(arg)) = [this](auto u) {
|
||||
emitTargetValue(u);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*QDBusVariant arg{QVariant{1}};
|
||||
QVariant v;
|
||||
v.setValue(arg);
|
||||
emit targetValueChanged(v, "1");*/
|
||||
|
||||
}
|
||||
virtual void stop() override {
|
||||
}
|
||||
virtual void stop() override {}
|
||||
private:
|
||||
DynamicReadableProxy &m_proxy;
|
||||
QTimer m_timer;
|
||||
QVector<QPointF> m_points;
|
||||
|
||||
template <typename U>
|
||||
void emitTargetValue(U reading) {
|
||||
template <typename U> void emitTargetValue(U reading) {
|
||||
// Find two points from the vector so that:
|
||||
// p[i].x < val < p[i + 1].x
|
||||
// p[i].x < val < p[i + 1].x
|
||||
std::optional<int> leftIndex = std::nullopt;
|
||||
for (int i = 0; i < m_points.length() - 1; i++) {
|
||||
if (m_points[i].x() < reading && reading < m_points[i + 1].x()) {
|
||||
@@ -93,14 +85,12 @@ private:
|
||||
double dx = m_points[li + 1].x() - m_points[li].x();
|
||||
double dvx = reading - m_points[li].x();
|
||||
double p = dvx / dx;
|
||||
OutType interp_y = lerp(m_points[li].y(),
|
||||
m_points[li + 1].y(), p);
|
||||
OutType interp_y = lerp(m_points[li].y(), m_points[li + 1].y(), p);
|
||||
QDBusVariant arg{QVariant{interp_y}};
|
||||
QVariant v;
|
||||
v.setValue(arg);
|
||||
emit targetValueChanged(v, QString::number(interp_y));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T lerp(T a, T b, double t) {return a + (t * (b - a));}
|
||||
template <typename T> T lerp(T a, T b, double t) { return a + (t * (b - a)); }
|
||||
};
|
||||
|
||||
@@ -14,38 +14,37 @@ Q_DECLARE_METATYPE(TCD::Result<QString>)
|
||||
ReadResult toTCResult(TCD::Result<QDBusVariant> res) {
|
||||
if (res.error)
|
||||
return static_cast<ReadError>(res.value.variant().toInt());
|
||||
|
||||
|
||||
auto type = static_cast<QMetaType::Type>(res.value.variant().type());
|
||||
auto v = res.value.variant();
|
||||
|
||||
|
||||
switch (type) {
|
||||
case QMetaType::Int:
|
||||
return v.value<int>();
|
||||
case QMetaType::UInt:
|
||||
return v.value<uint>();
|
||||
case QMetaType::Double:
|
||||
return v.value<double>();
|
||||
default:
|
||||
// TODO: indicate unhandled value
|
||||
return ReadError::UnknownError;
|
||||
case QMetaType::Int:
|
||||
return v.value<int>();
|
||||
case QMetaType::UInt:
|
||||
return v.value<uint>();
|
||||
case QMetaType::Double:
|
||||
return v.value<double>();
|
||||
default:
|
||||
// TODO: indicate unhandled value
|
||||
return ReadError::UnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
DynamicReadableProxy::DynamicReadableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent) : QObject(parent),
|
||||
m_iface("org.tuxclocker", path, "org.tuxclocker.DynamicReadable", conn) {
|
||||
DynamicReadableProxy::DynamicReadableProxy(QString path, QDBusConnection conn, QObject *parent)
|
||||
: QObject(parent), m_iface("org.tuxclocker", path, "org.tuxclocker.DynamicReadable", conn) {
|
||||
qDBusRegisterMetaType<TCD::Result<QDBusVariant>>();
|
||||
qDBusRegisterMetaType<TCD::Result<QString>>();
|
||||
|
||||
|
||||
m_timer.start(1000);
|
||||
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, [=] {
|
||||
QDBusReply<TCD::Result<QDBusVariant>> reply = m_iface.call("value");
|
||||
|
||||
|
||||
if (!reply.isValid())
|
||||
emit valueChanged(ReadError::UnknownError);
|
||||
else
|
||||
//qDebug() << QVariant::fromStdVariant(toTCResult(reply.value()));
|
||||
// qDebug() << QVariant::fromStdVariant(toTCResult(reply.value()));
|
||||
emit valueChanged(toTCResult(reply.value()));
|
||||
});
|
||||
}
|
||||
@@ -54,9 +53,9 @@ std::optional<QString> DynamicReadableProxy::unit() {
|
||||
/* Workaround for QVariant, or whatever errors out braindeath by calling
|
||||
the method instead */
|
||||
QDBusInterface propIface("org.tuxclocker", m_iface.path(),
|
||||
"org.freedesktop.DBus.Properties", m_iface.connection());
|
||||
"org.freedesktop.DBus.Properties", m_iface.connection());
|
||||
QDBusReply<QDBusVariant> reply =
|
||||
propIface.call("Get", "org.tuxclocker.DynamicReadable", "unit");
|
||||
propIface.call("Get", "org.tuxclocker.DynamicReadable", "unit");
|
||||
if (!reply.isValid()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
|
||||
class DynamicReadableProxy : public QObject {
|
||||
public:
|
||||
DynamicReadableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent = nullptr);
|
||||
DynamicReadableProxy(QString path, QDBusConnection conn, QObject *parent = nullptr);
|
||||
std::optional<QString> unit();
|
||||
signals:
|
||||
void valueChanged(TuxClocker::Device::ReadResult val);
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
QDBusInterface m_iface;
|
||||
QTimer m_timer; // Emits latest value
|
||||
};
|
||||
|
||||
@@ -7,6 +7,6 @@ int main(int argc, char **argv) {
|
||||
|
||||
MainWindow mw;
|
||||
mw.show();
|
||||
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||
Q_DECLARE_METATYPE(AssignableProxy *)
|
||||
|
||||
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
: QWidget(parent), m_deviceModel(model) {
|
||||
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
: QWidget(parent), m_deviceModel(model) {
|
||||
m_layout = new QGridLayout(this);
|
||||
m_proxyModel = new DeviceProxyModel(model, this);
|
||||
m_treeView = new DeviceTreeView;
|
||||
@@ -25,50 +25,36 @@ DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
m_flagLabel = new QLabel("Showing:");
|
||||
m_apply = new QPushButton("Apply changes");
|
||||
m_apply->setEnabled(true);
|
||||
|
||||
|
||||
m_flagEditor = new FlagEditor(
|
||||
QVector({
|
||||
std::tuple(
|
||||
QString("Assignables"),
|
||||
DeviceModel::assignableIcon(),
|
||||
DeviceModel::Assignable
|
||||
),
|
||||
std::tuple(
|
||||
QString("Dynamic Values"),
|
||||
DeviceModel::dynamicReadableIcon(),
|
||||
DeviceModel::DynamicReadable
|
||||
),
|
||||
std::tuple(
|
||||
QString("Static Values"),
|
||||
DeviceModel::staticReadableIcon(),
|
||||
DeviceModel::StaticReadable
|
||||
)
|
||||
}), this);
|
||||
|
||||
connect(m_apply, &QPushButton::pressed, &m_deviceModel,
|
||||
&DeviceModel::applyChanges);
|
||||
|
||||
QVector({std::tuple(QString("Assignables"), DeviceModel::assignableIcon(),
|
||||
DeviceModel::Assignable),
|
||||
std::tuple(QString("Dynamic Values"), DeviceModel::dynamicReadableIcon(),
|
||||
DeviceModel::DynamicReadable),
|
||||
std::tuple(QString("Static Values"), DeviceModel::staticReadableIcon(),
|
||||
DeviceModel::StaticReadable)}),
|
||||
this);
|
||||
|
||||
connect(m_apply, &QPushButton::pressed, &m_deviceModel, &DeviceModel::applyChanges);
|
||||
|
||||
m_treeView->functionEditorRequested.connect([this](QModelIndex &index) {
|
||||
auto a_data = index.data(DeviceModel::AssignableRole);
|
||||
if (!a_data.isValid())
|
||||
return;
|
||||
|
||||
auto a_info = a_data.value<AssignableItemData>();
|
||||
auto proxy = index.data(DeviceModel::AssignableProxyRole)
|
||||
.value<AssignableProxy*>();
|
||||
auto proxy =
|
||||
index.data(DeviceModel::AssignableProxyRole).value<AssignableProxy *>();
|
||||
auto name = index.data(DeviceModel::NodeNameRole).toString();
|
||||
match(a_info.assignableInfo()) (
|
||||
pattern(as<RangeInfo>(arg)) = [=](auto ri) {
|
||||
auto f_editor = new FunctionEditor{m_deviceModel,
|
||||
ri, *proxy, name};
|
||||
match(a_info.assignableInfo())(
|
||||
pattern(as<RangeInfo>(arg)) =
|
||||
[=](auto ri) {
|
||||
auto f_editor = new FunctionEditor{m_deviceModel, ri, *proxy, name};
|
||||
f_editor->show();
|
||||
f_editor->assignableConnectionChanged
|
||||
.connect([=](auto conn) {
|
||||
proxy->startConnection(conn);
|
||||
});
|
||||
f_editor->assignableConnectionChanged.connect(
|
||||
[=](auto conn) { proxy->startConnection(conn); });
|
||||
},
|
||||
pattern(_) = [] {}
|
||||
);
|
||||
pattern(_) = [] {});
|
||||
|
||||
/*auto f_editor = new FunctionEditor(m_deviceModel, rangeInfo, proxy);
|
||||
f_editor->show();
|
||||
@@ -78,17 +64,15 @@ DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
proxy.startConnection(assignableConnection);
|
||||
});*/
|
||||
});
|
||||
|
||||
|
||||
m_flagEditor->setFlags(DeviceModel::AllInterfaces);
|
||||
|
||||
m_flagEditor->flagsChanged.connect([=](auto flags) {
|
||||
m_proxyModel->setFlags(flags);
|
||||
});
|
||||
|
||||
|
||||
m_flagEditor->flagsChanged.connect([=](auto flags) { m_proxyModel->setFlags(flags); });
|
||||
|
||||
m_layout->addWidget(m_flagLabel, 0, 0);
|
||||
m_layout->addWidget(m_flagEditor, 0, 1);
|
||||
m_layout->addWidget(m_treeView, 1, 0, 1, 2);
|
||||
m_layout->addWidget(m_apply, 2, 0, 1, 2);
|
||||
|
||||
|
||||
setLayout(m_layout);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||
Q_DECLARE_METATYPE(AssignableProxy *)
|
||||
|
||||
DeviceTreeView::DeviceTreeView(QWidget *parent)
|
||||
: QTreeView(parent) {
|
||||
DeviceTreeView::DeviceTreeView(QWidget *parent) : QTreeView(parent) {
|
||||
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
setSortingEnabled(true);
|
||||
setEditTriggers(SelectedClicked | EditKeyPressed);
|
||||
@@ -30,12 +29,13 @@ DeviceTreeView::DeviceTreeView(QWidget *parent)
|
||||
enableConn->setDefaultWidget(&checkBox);
|
||||
menu.addActions({&editConn, enableConn});
|
||||
if (data.canConvert<AssignableItemData>() &&
|
||||
proxyData.canConvert<AssignableProxy*>()) {
|
||||
proxyData.canConvert<AssignableProxy *>()) {
|
||||
functionEditorRequested(index);
|
||||
/*auto a_data = data.value<AssignableItemData>();
|
||||
auto proxy = proxyData.value<AssignableProxy*>();
|
||||
match(a_data.assignableInfo()) (
|
||||
pattern(as<RangeInfo>(arg)) = [this, &menu, proxy, &editConn](auto ri) {
|
||||
pattern(as<RangeInfo>(arg)) = [this, &menu, proxy, &editConn](auto
|
||||
ri) {
|
||||
//functionEditorRequested(*proxy, ri);
|
||||
connect(&editConn, &QAction::triggered, [this, proxy, ri] {
|
||||
functionEditorRequested(*proxy, ri);
|
||||
@@ -44,10 +44,9 @@ DeviceTreeView::DeviceTreeView(QWidget *parent)
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);*/
|
||||
|
||||
}
|
||||
});
|
||||
m_delegate = new DeviceModelDelegate(this);
|
||||
|
||||
|
||||
setItemDelegate(m_delegate);
|
||||
}
|
||||
|
||||
@@ -15,25 +15,24 @@ class DeviceTreeView : public QTreeView {
|
||||
public:
|
||||
DeviceTreeView(QWidget *parent = nullptr);
|
||||
// Accessor method for connecting everything in the browser
|
||||
//const DeviceModel &deviceModel() {return m_deviceModel;}
|
||||
// const DeviceModel &deviceModel() {return m_deviceModel;}
|
||||
// TODO: make this more generalized
|
||||
// Defers the complexity to DeviceBrowser
|
||||
// TODO: this can be handled in the delegate with QAbstractItemDelegate::editorEvent
|
||||
boost::signals2::signal<void(QModelIndex&)> functionEditorRequested;
|
||||
boost::signals2::signal<void(QModelIndex &)> functionEditorRequested;
|
||||
protected:
|
||||
/* Workaround for the retarded behavior of waiting for a double click,
|
||||
you can't even disable it! */
|
||||
bool edit(const QModelIndex &index, QAbstractItemView::EditTrigger trigger,
|
||||
QEvent *event) {
|
||||
bool edit(const QModelIndex &index, QAbstractItemView::EditTrigger trigger, QEvent *event) {
|
||||
return QTreeView::edit(index,
|
||||
trigger == QAbstractItemView::SelectedClicked ?
|
||||
QAbstractItemView::AllEditTriggers : trigger, event);
|
||||
trigger == QAbstractItemView::SelectedClicked
|
||||
? QAbstractItemView::AllEditTriggers
|
||||
: trigger,
|
||||
event);
|
||||
}
|
||||
// TODO: allow to start editing with the keyboard
|
||||
EditTriggers editTriggers() {
|
||||
return QAbstractItemView::AllEditTriggers;
|
||||
}
|
||||
EditTriggers editTriggers() { return QAbstractItemView::AllEditTriggers; }
|
||||
private:
|
||||
//DeviceModel &m_deviceModel;
|
||||
// DeviceModel &m_deviceModel;
|
||||
DeviceModelDelegate *m_delegate;
|
||||
};
|
||||
|
||||
@@ -12,37 +12,31 @@ namespace TC = TuxClocker;
|
||||
|
||||
class DoubleRangeEditor : public AbstractAssignableEditor {
|
||||
public:
|
||||
DoubleRangeEditor(QWidget *parent = nullptr)
|
||||
: AbstractAssignableEditor(parent) {
|
||||
DoubleRangeEditor(QWidget *parent = nullptr) : AbstractAssignableEditor(parent) {
|
||||
setAutoFillBackground(true);
|
||||
m_layout = new QHBoxLayout(this);
|
||||
m_spinBox = new QDoubleSpinBox;
|
||||
m_slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
|
||||
connect(m_slider, &QSlider::rangeChanged, [this](int min, int max) {
|
||||
m_spinBox->setRange(toDouble(min), toDouble(max));
|
||||
});
|
||||
connect(m_spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
||||
[this](double val) {
|
||||
m_slider->setValue(toInt(val));
|
||||
});
|
||||
connect(m_slider, &QSlider::valueChanged, [this](int val) {
|
||||
m_spinBox->setValue(toDouble(val));
|
||||
});
|
||||
|
||||
[this](double val) { m_slider->setValue(toInt(val)); });
|
||||
connect(m_slider, &QSlider::valueChanged,
|
||||
[this](int val) { m_spinBox->setValue(toDouble(val)); });
|
||||
|
||||
m_layout->setMargin(0);
|
||||
m_layout->addWidget(m_slider);
|
||||
m_layout->addWidget(m_spinBox);
|
||||
setLayout(m_layout);
|
||||
}
|
||||
DoubleRangeEditor(TC::Device::Range<double> range, QWidget *parent = nullptr)
|
||||
: DoubleRangeEditor(parent) {
|
||||
: DoubleRangeEditor(parent) {
|
||||
setRange(range);
|
||||
}
|
||||
virtual QVariant assignableData() override {return m_spinBox->value();}
|
||||
virtual QString displayData() override {
|
||||
return QString::number(m_spinBox->value());
|
||||
}
|
||||
virtual QVariant assignableData() override { return m_spinBox->value(); }
|
||||
virtual QString displayData() override { return QString::number(m_spinBox->value()); }
|
||||
virtual void setAssignableData(QVariant data) override {
|
||||
m_spinBox->setValue(data.toDouble());
|
||||
}
|
||||
@@ -51,14 +45,10 @@ public:
|
||||
}
|
||||
private:
|
||||
// How many digits are displayed after the dot
|
||||
constexpr int doubleDecimals() {return 2;}
|
||||
double toDouble(int i) {
|
||||
return static_cast<double>(i) / pow(10, doubleDecimals());
|
||||
}
|
||||
int toInt(double d) {
|
||||
return static_cast<int>(d * pow(10, doubleDecimals()));
|
||||
}
|
||||
|
||||
constexpr int doubleDecimals() { return 2; }
|
||||
double toDouble(int i) { return static_cast<double>(i) / pow(10, doubleDecimals()); }
|
||||
int toInt(double d) { return static_cast<int>(d * pow(10, doubleDecimals())); }
|
||||
|
||||
QDoubleSpinBox *m_spinBox;
|
||||
QHBoxLayout *m_layout;
|
||||
QSlider *m_slider;
|
||||
|
||||
@@ -7,174 +7,167 @@
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
DragChartView::DragChartView(QWidget *parent) : QChartView(parent)
|
||||
{
|
||||
chart()->installEventFilter(this);
|
||||
|
||||
setRenderHint(QPainter::Antialiasing);
|
||||
DragChartView::DragChartView(QWidget *parent) : QChartView(parent) {
|
||||
chart()->installEventFilter(this);
|
||||
|
||||
m_toolTipLabel = new QLabel;
|
||||
m_toolTipLabel->setWindowFlag(Qt::ToolTip);
|
||||
setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
m_dragCanStart = false;
|
||||
|
||||
m_mouseInLimitArea = false;
|
||||
|
||||
m_scatterPressed = false;
|
||||
m_toolTipLabel = new QLabel;
|
||||
m_toolTipLabel->setWindowFlag(Qt::ToolTip);
|
||||
|
||||
m_leftLineFillerItem = new QGraphicsLineItem;
|
||||
m_rightLineFillerItem = new QGraphicsLineItem;
|
||||
m_dragCanStart = false;
|
||||
|
||||
m_mouseInLimitArea = false;
|
||||
|
||||
m_scatterPressed = false;
|
||||
|
||||
m_leftLineFillerItem = new QGraphicsLineItem;
|
||||
m_rightLineFillerItem = new QGraphicsLineItem;
|
||||
m_leftLineFillerItem->setPen(fillerLinePen());
|
||||
m_rightLineFillerItem->setPen(fillerLinePen());
|
||||
|
||||
chart()->scene()->addItem(m_leftLineFillerItem);
|
||||
chart()->scene()->addItem(m_rightLineFillerItem);
|
||||
chart()->scene()->addItem(m_leftLineFillerItem);
|
||||
chart()->scene()->addItem(m_rightLineFillerItem);
|
||||
chart()->legend()->setVisible(false);
|
||||
|
||||
m_chartMargin = m_series.markerSize() / 2;
|
||||
|
||||
// Resize axes by margin
|
||||
connect(&m_xAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
|
||||
m_limitRect.setLeft(min);
|
||||
m_limitRect.setRight(max);
|
||||
|
||||
m_limitRect.setTopLeft(QPointF(min, m_limitRect.top()));
|
||||
m_limitRect.setBottomRight(QPointF(max, m_limitRect.bottom()));
|
||||
|
||||
if (chart()->rect().isNull()) {
|
||||
return;
|
||||
}
|
||||
// Convert m_chartMargin to chart value
|
||||
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() - chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
|
||||
|
||||
m_xAxis.blockSignals(true); // Don't go to an infinite loop
|
||||
m_xAxis.setRange(min - valueDelta, max + valueDelta);
|
||||
m_xAxis.blockSignals(false);
|
||||
});
|
||||
|
||||
connect(&m_yAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
|
||||
// Update limit rect
|
||||
m_limitRect.setBottom(min);
|
||||
m_limitRect.setTop(max);
|
||||
|
||||
m_limitRect.setTopLeft(QPointF(m_limitRect.left(), max));
|
||||
m_limitRect.setBottomRight(QPointF(m_limitRect.right(), min));
|
||||
|
||||
if (chart()->rect().isNull()) {
|
||||
return;
|
||||
}
|
||||
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() - chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
|
||||
m_yAxis.blockSignals(true); // Don't go to an infinite loop
|
||||
m_yAxis.setRange(min - valueDelta, max + valueDelta);
|
||||
m_yAxis.blockSignals(false);
|
||||
});
|
||||
|
||||
// Delete filler items when points are removed
|
||||
connect(&m_series, &QScatterSeries::pointRemoved, [=]() {
|
||||
chart()->scene()->removeItem(m_lineFillerItems.last());
|
||||
delete m_lineFillerItems.last();
|
||||
m_lineFillerItems.pop_back();
|
||||
m_chartMargin = m_series.markerSize() / 2;
|
||||
|
||||
// Resize axes by margin
|
||||
connect(&m_xAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
|
||||
m_limitRect.setLeft(min);
|
||||
m_limitRect.setRight(max);
|
||||
|
||||
m_limitRect.setTopLeft(QPointF(min, m_limitRect.top()));
|
||||
m_limitRect.setBottomRight(QPointF(max, m_limitRect.bottom()));
|
||||
|
||||
if (chart()->rect().isNull()) {
|
||||
return;
|
||||
}
|
||||
// Convert m_chartMargin to chart value
|
||||
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() -
|
||||
chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
|
||||
|
||||
m_xAxis.blockSignals(true); // Don't go to an infinite loop
|
||||
m_xAxis.setRange(min - valueDelta, max + valueDelta);
|
||||
m_xAxis.blockSignals(false);
|
||||
});
|
||||
|
||||
connect(&m_yAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
|
||||
// Update limit rect
|
||||
m_limitRect.setBottom(min);
|
||||
m_limitRect.setTop(max);
|
||||
|
||||
m_limitRect.setTopLeft(QPointF(m_limitRect.left(), max));
|
||||
m_limitRect.setBottomRight(QPointF(m_limitRect.right(), min));
|
||||
|
||||
if (chart()->rect().isNull()) {
|
||||
return;
|
||||
}
|
||||
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() -
|
||||
chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
|
||||
m_yAxis.blockSignals(true); // Don't go to an infinite loop
|
||||
m_yAxis.setRange(min - valueDelta, max + valueDelta);
|
||||
m_yAxis.blockSignals(false);
|
||||
});
|
||||
|
||||
// Delete filler items when points are removed
|
||||
connect(&m_series, &QScatterSeries::pointRemoved, [=]() {
|
||||
chart()->scene()->removeItem(m_lineFillerItems.last());
|
||||
delete m_lineFillerItems.last();
|
||||
m_lineFillerItems.pop_back();
|
||||
drawFillerLines(&m_series);
|
||||
});
|
||||
|
||||
// Add filler item when point is added
|
||||
connect(&m_series, &QScatterSeries::pointAdded, [=]() {
|
||||
});
|
||||
|
||||
// Add filler item when point is added
|
||||
connect(&m_series, &QScatterSeries::pointAdded, [=]() {
|
||||
if (m_series.pointsVector().length() < 2)
|
||||
return;
|
||||
|
||||
auto item = new QGraphicsLineItem;
|
||||
item->setPen(fillerLinePen());
|
||||
m_lineFillerItems.append(item);
|
||||
chart()->scene()->addItem(item);
|
||||
|
||||
auto item = new QGraphicsLineItem;
|
||||
item->setPen(fillerLinePen());
|
||||
m_lineFillerItems.append(item);
|
||||
chart()->scene()->addItem(item);
|
||||
drawFillerLines(&m_series);
|
||||
});
|
||||
|
||||
connect(&m_series, &QScatterSeries::pointsReplaced, [=]() {
|
||||
// Delete filler items
|
||||
for (auto item : m_lineFillerItems) {
|
||||
delete item;
|
||||
}
|
||||
m_lineFillerItems.clear();
|
||||
// Create new ones
|
||||
for (int i = 0; i < m_series.pointsVector().length(); i++) {
|
||||
auto item = new QGraphicsLineItem;
|
||||
item->setPen(fillerLinePen());
|
||||
m_lineFillerItems.append(item);
|
||||
chart()->scene()->addItem(item);
|
||||
}
|
||||
drawFillerLines(&m_series);
|
||||
});
|
||||
|
||||
connect(&m_series, &QScatterSeries::pressed, [=](QPointF point) {
|
||||
m_dragCanStart = true;
|
||||
m_latestScatterPoint = point;
|
||||
m_scatterPressed = true;
|
||||
});
|
||||
|
||||
connect(&m_series, &QScatterSeries::clicked, [=](const QPointF point) {
|
||||
m_scatterPressed = false;
|
||||
if (m_dragActive) {
|
||||
m_dragActive = false;
|
||||
emit dragEnded(point);
|
||||
}
|
||||
else {
|
||||
// Just a click, delete point
|
||||
m_series.remove(point);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
chart()->addSeries(&m_series);
|
||||
|
||||
chart()->addAxis(&m_xAxis, Qt::AlignBottom);
|
||||
chart()->addAxis(&m_yAxis, Qt::AlignLeft);
|
||||
|
||||
m_series.attachAxis(&m_xAxis);
|
||||
m_series.attachAxis(&m_yAxis);
|
||||
|
||||
m_xAxis.setRange(0, 25);
|
||||
m_yAxis.setRange(0, 25);
|
||||
connect(&m_series, &QScatterSeries::pointsReplaced, [=]() {
|
||||
// Delete filler items
|
||||
for (auto item : m_lineFillerItems) {
|
||||
delete item;
|
||||
}
|
||||
m_lineFillerItems.clear();
|
||||
// Create new ones
|
||||
for (int i = 0; i < m_series.pointsVector().length(); i++) {
|
||||
auto item = new QGraphicsLineItem;
|
||||
item->setPen(fillerLinePen());
|
||||
m_lineFillerItems.append(item);
|
||||
chart()->scene()->addItem(item);
|
||||
}
|
||||
drawFillerLines(&m_series);
|
||||
});
|
||||
|
||||
chart()->setBackgroundRoundness(0);
|
||||
|
||||
// Set theme colors
|
||||
chart()->setBackgroundBrush(QBrush(QPalette().color(QPalette::Background)));
|
||||
|
||||
chart()->legend()->setLabelColor(QPalette().color(QPalette::Text));
|
||||
|
||||
m_yAxis.setLabelsColor(QPalette().color(QPalette::Text));
|
||||
m_xAxis.setLabelsColor(QPalette().color(QPalette::Text));
|
||||
|
||||
m_yAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
|
||||
m_xAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
|
||||
|
||||
// Set cursor to indicate dragging
|
||||
connect(this, &DragChartView::dragStarted, [=]() {
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
});
|
||||
|
||||
connect(this, &DragChartView::dragEnded, [=]() {
|
||||
if (m_mouseInLimitArea) {
|
||||
setCursor(Qt::CrossCursor);
|
||||
}
|
||||
else {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
});
|
||||
|
||||
connect(this, &DragChartView::limitAreaEntered, [=]() {
|
||||
setCursor(Qt::CrossCursor);
|
||||
});
|
||||
|
||||
connect(this, &DragChartView::limitAreaExited, [=]() {
|
||||
if (cursor().shape() != Qt::ClosedHandCursor) {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
});
|
||||
connect(&m_series, &QScatterSeries::pressed, [=](QPointF point) {
|
||||
m_dragCanStart = true;
|
||||
m_latestScatterPoint = point;
|
||||
m_scatterPressed = true;
|
||||
});
|
||||
|
||||
connect(&m_series, &QScatterSeries::clicked, [=](const QPointF point) {
|
||||
m_scatterPressed = false;
|
||||
if (m_dragActive) {
|
||||
m_dragActive = false;
|
||||
emit dragEnded(point);
|
||||
} else {
|
||||
// Just a click, delete point
|
||||
m_series.remove(point);
|
||||
}
|
||||
});
|
||||
|
||||
chart()->addSeries(&m_series);
|
||||
|
||||
chart()->addAxis(&m_xAxis, Qt::AlignBottom);
|
||||
chart()->addAxis(&m_yAxis, Qt::AlignLeft);
|
||||
|
||||
m_series.attachAxis(&m_xAxis);
|
||||
m_series.attachAxis(&m_yAxis);
|
||||
|
||||
m_xAxis.setRange(0, 25);
|
||||
m_yAxis.setRange(0, 25);
|
||||
|
||||
chart()->setBackgroundRoundness(0);
|
||||
|
||||
// Set theme colors
|
||||
chart()->setBackgroundBrush(QBrush(QPalette().color(QPalette::Background)));
|
||||
|
||||
chart()->legend()->setLabelColor(QPalette().color(QPalette::Text));
|
||||
|
||||
m_yAxis.setLabelsColor(QPalette().color(QPalette::Text));
|
||||
m_xAxis.setLabelsColor(QPalette().color(QPalette::Text));
|
||||
|
||||
m_yAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
|
||||
m_xAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
|
||||
|
||||
// Set cursor to indicate dragging
|
||||
connect(this, &DragChartView::dragStarted, [=]() { setCursor(Qt::ClosedHandCursor); });
|
||||
|
||||
connect(this, &DragChartView::dragEnded, [=]() {
|
||||
if (m_mouseInLimitArea) {
|
||||
setCursor(Qt::CrossCursor);
|
||||
} else {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
});
|
||||
|
||||
connect(this, &DragChartView::limitAreaEntered, [=]() { setCursor(Qt::CrossCursor); });
|
||||
|
||||
connect(this, &DragChartView::limitAreaExited, [=]() {
|
||||
if (cursor().shape() != Qt::ClosedHandCursor) {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DragChartView::setVector(const QVector <QPointF> vector) {
|
||||
m_series.replace(vector);
|
||||
}
|
||||
void DragChartView::setVector(const QVector<QPointF> vector) { m_series.replace(vector); }
|
||||
|
||||
void DragChartView::setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax) {
|
||||
m_series.clear();
|
||||
@@ -183,192 +176,195 @@ void DragChartView::setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax) {
|
||||
}
|
||||
|
||||
bool DragChartView::event(QEvent *event) {
|
||||
//qDebug() << event->type();
|
||||
// qDebug() << event->type();
|
||||
|
||||
if (event->type() == QEvent::Resize || event->type() == QEvent::UpdateLater) {
|
||||
// Chart has a geometry when this is true
|
||||
drawFillerLines(&m_series);
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::Leave && !m_dragActive) {
|
||||
// Set to normal cursor
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
if (event->type() == QEvent::Resize || event->type() == QEvent::UpdateLater) {
|
||||
// Chart has a geometry when this is true
|
||||
drawFillerLines(&m_series);
|
||||
}
|
||||
|
||||
return QChartView::event(event);
|
||||
if (event->type() == QEvent::Leave && !m_dragActive) {
|
||||
// Set to normal cursor
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
|
||||
return QChartView::event(event);
|
||||
}
|
||||
|
||||
void DragChartView::mousePressEvent(QMouseEvent *event) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
m_dragStartPosition = event->pos();
|
||||
}
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
m_dragStartPosition = event->pos();
|
||||
}
|
||||
|
||||
QChartView::mousePressEvent(event);
|
||||
QChartView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DragChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
if (m_limitRect.contains(chart()->mapToValue(event->pos())) && !m_mouseInLimitArea) {
|
||||
m_mouseInLimitArea = true;
|
||||
emit limitAreaEntered();
|
||||
}
|
||||
|
||||
if (!m_limitRect.contains(chart()->mapToValue(event->pos())) && m_mouseInLimitArea) {
|
||||
m_mouseInLimitArea = false;
|
||||
emit limitAreaExited();
|
||||
}
|
||||
|
||||
if (!(event->buttons() & Qt::LeftButton)) {
|
||||
return;
|
||||
}
|
||||
if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance() || !m_dragCanStart) {
|
||||
return;
|
||||
}
|
||||
void DragChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
if (m_limitRect.contains(chart()->mapToValue(event->pos())) && !m_mouseInLimitArea) {
|
||||
m_mouseInLimitArea = true;
|
||||
emit limitAreaEntered();
|
||||
}
|
||||
|
||||
// Start drag
|
||||
emit dragStarted(m_dragStartPosition);
|
||||
|
||||
m_dragActive = true;
|
||||
if (!m_limitRect.contains(chart()->mapToValue(event->pos())) && m_mouseInLimitArea) {
|
||||
m_mouseInLimitArea = false;
|
||||
emit limitAreaExited();
|
||||
}
|
||||
|
||||
if (m_toolTipLabel->isHidden()) {
|
||||
m_toolTipLabel->show();
|
||||
}
|
||||
if (!(event->buttons() & Qt::LeftButton)) {
|
||||
return;
|
||||
}
|
||||
if ((event->pos() - m_dragStartPosition).manhattanLength() <
|
||||
QApplication::startDragDistance() ||
|
||||
!m_dragCanStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_toolTipLabel->setText(QString("%1, %2")
|
||||
.arg(QString::number(m_latestScatterPoint.x()),
|
||||
QString::number(m_latestScatterPoint.y())));
|
||||
|
||||
// FIXME : doesn't work properly when screen is switched(?)
|
||||
m_toolTipLabel->move(event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
|
||||
|
||||
// Don't move point out of bounds
|
||||
if (m_limitRect.contains(chart()->mapToValue(event->pos()))) {
|
||||
replaceMovedPoint(m_latestScatterPoint, chart()->mapToValue(event->pos(), &m_series));
|
||||
}
|
||||
else {
|
||||
QPointF point(chart()->mapToValue(event->pos()));
|
||||
// Set the point value to the constraint where it exceeds it
|
||||
if (chart()->mapToValue(event->pos()).x() > m_limitRect.right()) {
|
||||
point.setX(m_limitRect.right());
|
||||
}
|
||||
if (chart()->mapToValue(event->pos()).x() < m_limitRect.left()) {
|
||||
point.setX(m_limitRect.left());
|
||||
}
|
||||
if (chart()->mapToValue(event->pos()).y() > m_limitRect.top()) {
|
||||
point.setY(m_limitRect.top());
|
||||
}
|
||||
if (chart()->mapToValue(event->pos()).y() < m_limitRect.bottom()) {
|
||||
point.setY(m_limitRect.bottom());
|
||||
}
|
||||
replaceMovedPoint(m_latestScatterPoint, point);
|
||||
}
|
||||
// Start drag
|
||||
emit dragStarted(m_dragStartPosition);
|
||||
|
||||
drawFillerLines(&m_series);
|
||||
m_dragActive = true;
|
||||
|
||||
if (m_toolTipLabel->isHidden()) {
|
||||
m_toolTipLabel->show();
|
||||
}
|
||||
|
||||
m_toolTipLabel->setText(QString("%1, %2").arg(
|
||||
QString::number(m_latestScatterPoint.x()), QString::number(m_latestScatterPoint.y())));
|
||||
|
||||
// FIXME : doesn't work properly when screen is switched(?)
|
||||
m_toolTipLabel->move(
|
||||
event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
|
||||
|
||||
// Don't move point out of bounds
|
||||
if (m_limitRect.contains(chart()->mapToValue(event->pos()))) {
|
||||
replaceMovedPoint(
|
||||
m_latestScatterPoint, chart()->mapToValue(event->pos(), &m_series));
|
||||
} else {
|
||||
QPointF point(chart()->mapToValue(event->pos()));
|
||||
// Set the point value to the constraint where it exceeds it
|
||||
if (chart()->mapToValue(event->pos()).x() > m_limitRect.right()) {
|
||||
point.setX(m_limitRect.right());
|
||||
}
|
||||
if (chart()->mapToValue(event->pos()).x() < m_limitRect.left()) {
|
||||
point.setX(m_limitRect.left());
|
||||
}
|
||||
if (chart()->mapToValue(event->pos()).y() > m_limitRect.top()) {
|
||||
point.setY(m_limitRect.top());
|
||||
}
|
||||
if (chart()->mapToValue(event->pos()).y() < m_limitRect.bottom()) {
|
||||
point.setY(m_limitRect.bottom());
|
||||
}
|
||||
replaceMovedPoint(m_latestScatterPoint, point);
|
||||
}
|
||||
|
||||
drawFillerLines(&m_series);
|
||||
}
|
||||
|
||||
void DragChartView::mouseReleaseEvent(QMouseEvent *event) {
|
||||
m_dragCanStart = false;
|
||||
m_dragCanStart = false;
|
||||
|
||||
if (!m_scatterPressed &&
|
||||
m_limitRect.contains(chart()->mapToValue(event->pos()))) {
|
||||
if (!m_scatterPressed && m_limitRect.contains(chart()->mapToValue(event->pos()))) {
|
||||
// Add a new point to series
|
||||
m_series.append(chart()->mapToValue(event->pos()));
|
||||
drawFillerLines(&m_series);
|
||||
}
|
||||
|
||||
m_toolTipLabel->hide();
|
||||
QChartView::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
m_toolTipLabel->hide();
|
||||
QChartView::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void DragChartView::wheelEvent(QWheelEvent *event) {
|
||||
qreal factor = event->angleDelta().y() > 0 ? 0.5 : 2.0;
|
||||
qreal factor = event->angleDelta().y() > 0 ? 0.5 : 2.0;
|
||||
|
||||
//chart()->zoom(factor);
|
||||
event->accept();
|
||||
// chart()->zoom(factor);
|
||||
event->accept();
|
||||
|
||||
drawFillerLines(&m_series);
|
||||
drawFillerLines(&m_series);
|
||||
|
||||
QChartView::wheelEvent(event);
|
||||
QChartView::wheelEvent(event);
|
||||
}
|
||||
|
||||
void DragChartView::replaceMovedPoint(const QPointF old, const QPointF new_) {
|
||||
m_series.replace(old, new_);
|
||||
m_series.replace(old, new_);
|
||||
|
||||
m_latestScatterPoint = new_;
|
||||
m_latestScatterPoint = new_;
|
||||
}
|
||||
|
||||
QVector <QPointF> DragChartView::sortPointFByAscendingX(const QVector<QPointF> points) {
|
||||
QVector <qreal> ascendingX;
|
||||
QVector <qreal> originalY;
|
||||
QVector<QPointF> DragChartView::sortPointFByAscendingX(const QVector<QPointF> points) {
|
||||
QVector<qreal> ascendingX;
|
||||
QVector<qreal> originalY;
|
||||
|
||||
for (QPointF point : points) {
|
||||
ascendingX.append(point.x());
|
||||
originalY.append(point.y());
|
||||
}
|
||||
for (QPointF point : points) {
|
||||
ascendingX.append(point.x());
|
||||
originalY.append(point.y());
|
||||
}
|
||||
|
||||
std::sort(ascendingX.begin(), ascendingX.end());
|
||||
std::sort(ascendingX.begin(), ascendingX.end());
|
||||
|
||||
QVector <QPointF> sorted;
|
||||
QVector<QPointF> sorted;
|
||||
|
||||
// Find the original y values for x values
|
||||
for (qreal x : ascendingX) {
|
||||
for (QPointF point : points) {
|
||||
if (qFuzzyCompare(x, point.x())) {
|
||||
sorted.append(QPointF(x, point.y()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Find the original y values for x values
|
||||
for (qreal x : ascendingX) {
|
||||
for (QPointF point : points) {
|
||||
if (qFuzzyCompare(x, point.x())) {
|
||||
sorted.append(QPointF(x, point.y()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sorted;
|
||||
return sorted;
|
||||
}
|
||||
|
||||
void DragChartView::drawFillerLines(QScatterSeries *series) {
|
||||
// TODO: line isn't drawn between points whose x is the same
|
||||
// TODO: line isn't drawn between points whose x is the same
|
||||
// Sort points by ascending x
|
||||
QVector <QPointF> sorted = sortPointFByAscendingX(series->pointsVector());
|
||||
QVector<QPointF> sorted = sortPointFByAscendingX(series->pointsVector());
|
||||
|
||||
if (sorted.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (sorted.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sorted.length() - 1; i++) {
|
||||
m_lineFillerItems[i]->setLine(QLineF(chart()->mapToPosition(sorted[i]),
|
||||
chart()->mapToPosition(sorted[i + 1])));
|
||||
}
|
||||
for (int i = 0; i < sorted.length() - 1; i++) {
|
||||
m_lineFillerItems[i]->setLine(QLineF(
|
||||
chart()->mapToPosition(sorted[i]), chart()->mapToPosition(sorted[i + 1])));
|
||||
}
|
||||
|
||||
m_leftLineFillerItem->setLine(
|
||||
QLineF(chart()->mapToPosition(QPointF(
|
||||
m_xAxis.min(), sorted[0].y())),
|
||||
m_leftLineFillerItem->setLine(
|
||||
QLineF(chart()->mapToPosition(QPointF(m_xAxis.min(), sorted[0].y())),
|
||||
chart()->mapToPosition(sorted[0])));
|
||||
|
||||
m_rightLineFillerItem->setLine(
|
||||
QLineF(chart()->mapToPosition(sorted.last()),
|
||||
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
|
||||
m_rightLineFillerItem->setLine(QLineF(chart()->mapToPosition(sorted.last()),
|
||||
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
|
||||
|
||||
chart()->update();
|
||||
chart()->update();
|
||||
}
|
||||
|
||||
bool DragChartView::eventFilter(QObject *obj, QEvent *event) {
|
||||
static bool resized = false;
|
||||
|
||||
if (obj == chart() && event->type() == QEvent::WindowActivate && !resized) {
|
||||
resized = true;
|
||||
emit m_xAxis.rangeChanged(m_xAxis.min(), m_xAxis.max());
|
||||
emit m_yAxis.rangeChanged(m_yAxis.min(), m_yAxis.max());
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
static bool resized = false;
|
||||
|
||||
if (obj == chart() && event->type() == QEvent::WindowActivate && !resized) {
|
||||
resized = true;
|
||||
emit m_xAxis.rangeChanged(m_xAxis.min(), m_xAxis.max());
|
||||
emit m_yAxis.rangeChanged(m_yAxis.min(), m_yAxis.max());
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
QPoint DragChartView::toolTipOffset(QWidget *widget, const QPoint windowCursorPos) {
|
||||
QRect screenRect = widget->window()->windowHandle()->screen()->geometry();
|
||||
|
||||
if (screenRect.width() > screenRect.height()) {
|
||||
// Use x offset for screens that are wider than high
|
||||
// Set the offset to the side that has more space
|
||||
int xOffset = (windowCursorPos.x() > widget->window()->rect().width() / 2) ? -qRound(toolTipMargin() * screenRect.width()) : qRound(toolTipMargin() * screenRect.width());
|
||||
return QPoint(xOffset, 0);
|
||||
}
|
||||
int yOffset = (windowCursorPos.y() > widget->window()->rect().height() / 2) ? -qRound(toolTipMargin() * screenRect.height()) :qRound(toolTipMargin() * screenRect.height());
|
||||
|
||||
return QPoint(0, yOffset);
|
||||
QRect screenRect = widget->window()->windowHandle()->screen()->geometry();
|
||||
|
||||
if (screenRect.width() > screenRect.height()) {
|
||||
// Use x offset for screens that are wider than high
|
||||
// Set the offset to the side that has more space
|
||||
int xOffset = (windowCursorPos.x() > widget->window()->rect().width() / 2)
|
||||
? -qRound(toolTipMargin() * screenRect.width())
|
||||
: qRound(toolTipMargin() * screenRect.width());
|
||||
return QPoint(xOffset, 0);
|
||||
}
|
||||
int yOffset = (windowCursorPos.y() > widget->window()->rect().height() / 2)
|
||||
? -qRound(toolTipMargin() * screenRect.height())
|
||||
: qRound(toolTipMargin() * screenRect.height());
|
||||
|
||||
return QPoint(0, yOffset);
|
||||
}
|
||||
|
||||
@@ -9,58 +9,58 @@ using namespace QtCharts;
|
||||
|
||||
class DragChartView : public QChartView {
|
||||
public:
|
||||
DragChartView(QWidget *parent = nullptr);
|
||||
QValueAxis &xAxis() {return m_xAxis;}
|
||||
QValueAxis &yAxis() {return m_yAxis;}
|
||||
|
||||
void setVector(const QVector <QPointF> vector);
|
||||
QVector <QPointF> vector() {return m_series.pointsVector();}
|
||||
// Clear the points and set new range
|
||||
void setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax);
|
||||
DragChartView(QWidget *parent = nullptr);
|
||||
QValueAxis &xAxis() { return m_xAxis; }
|
||||
QValueAxis &yAxis() { return m_yAxis; }
|
||||
|
||||
void setVector(const QVector<QPointF> vector);
|
||||
QVector<QPointF> vector() { return m_series.pointsVector(); }
|
||||
// Clear the points and set new range
|
||||
void setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax);
|
||||
protected:
|
||||
bool event(QEvent*);
|
||||
void mousePressEvent(QMouseEvent*);
|
||||
void mouseMoveEvent(QMouseEvent*);
|
||||
void mouseReleaseEvent(QMouseEvent*);
|
||||
void wheelEvent(QWheelEvent*);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event); // Get notified when chart has geometry
|
||||
bool event(QEvent *);
|
||||
void mousePressEvent(QMouseEvent *);
|
||||
void mouseMoveEvent(QMouseEvent *);
|
||||
void mouseReleaseEvent(QMouseEvent *);
|
||||
void wheelEvent(QWheelEvent *);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event); // Get notified when chart has geometry
|
||||
signals:
|
||||
void dragStarted(const QPointF point);
|
||||
void dragEnded(const QPointF point);
|
||||
|
||||
void limitAreaEntered();
|
||||
void limitAreaExited();
|
||||
void dragStarted(const QPointF point);
|
||||
void dragEnded(const QPointF point);
|
||||
|
||||
void limitAreaEntered();
|
||||
void limitAreaExited();
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
static QPoint toolTipOffset(QWidget *widget, const QPoint windowCursorPos); // Get tooltip offset based on current screen
|
||||
static double toolTipMargin() {return 0.02;}
|
||||
|
||||
QColor fillerLineColor() {
|
||||
return QPalette().color(QPalette::Highlight);
|
||||
}
|
||||
QPen fillerLinePen() {
|
||||
return QPen(fillerLineColor(), 3);
|
||||
}
|
||||
|
||||
qreal m_chartMargin; // Margin in pixels between a scatter point and axis edge. Add this value on either edge of the axes in order for chatter points to be unable to go partially out of the viewport.
|
||||
QRectF m_limitRect; // Scatter points can't be moved outside this rect
|
||||
bool m_mouseInLimitArea;
|
||||
|
||||
bool m_scatterPressed; // For detecting if mouse release should delete or add a new point
|
||||
|
||||
QVector <QPointF> sortPointFByAscendingX(const QVector <QPointF> points);
|
||||
void drawFillerLines(QScatterSeries *series);
|
||||
QLabel *m_toolTipLabel;
|
||||
QScatterSeries m_series;
|
||||
QPoint m_dragStartPosition;
|
||||
bool m_dragCanStart; // Was a point clicked and not released before drag should start
|
||||
bool m_dragActive;
|
||||
QPointF m_latestScatterPoint;
|
||||
void replaceMovedPoint(const QPointF old, const QPointF new_);
|
||||
QVector <QGraphicsLineItem*> m_lineFillerItems; // Filler lines between points whose amount is n - 1 for nonzero n
|
||||
QGraphicsLineItem *m_leftLineFillerItem;
|
||||
QGraphicsLineItem *m_rightLineFillerItem;
|
||||
QValueAxis m_xAxis, m_yAxis;
|
||||
Q_OBJECT
|
||||
|
||||
static QPoint toolTipOffset(QWidget *widget,
|
||||
const QPoint windowCursorPos); // Get tooltip offset based on current screen
|
||||
static double toolTipMargin() { return 0.02; }
|
||||
|
||||
QColor fillerLineColor() { return QPalette().color(QPalette::Highlight); }
|
||||
QPen fillerLinePen() { return QPen(fillerLineColor(), 3); }
|
||||
|
||||
qreal m_chartMargin; // Margin in pixels between a scatter point and axis edge. Add this
|
||||
// value on either edge of the axes in order for chatter points to be
|
||||
// unable to go partially out of the viewport.
|
||||
QRectF m_limitRect; // Scatter points can't be moved outside this rect
|
||||
bool m_mouseInLimitArea;
|
||||
|
||||
bool m_scatterPressed; // For detecting if mouse release should delete or add a new point
|
||||
|
||||
QVector<QPointF> sortPointFByAscendingX(const QVector<QPointF> points);
|
||||
void drawFillerLines(QScatterSeries *series);
|
||||
QLabel *m_toolTipLabel;
|
||||
QScatterSeries m_series;
|
||||
QPoint m_dragStartPosition;
|
||||
bool m_dragCanStart; // Was a point clicked and not released before drag should start
|
||||
bool m_dragActive;
|
||||
QPointF m_latestScatterPoint;
|
||||
void replaceMovedPoint(const QPointF old, const QPointF new_);
|
||||
QVector<QGraphicsLineItem *>
|
||||
m_lineFillerItems; // Filler lines between points whose amount is n - 1 for nonzero n
|
||||
QGraphicsLineItem *m_leftLineFillerItem;
|
||||
QGraphicsLineItem *m_rightLineFillerItem;
|
||||
QValueAxis m_xAxis, m_yAxis;
|
||||
};
|
||||
|
||||
@@ -15,36 +15,33 @@ EnumEditor::EnumEditor(QWidget *parent) : AbstractAssignableEditor(parent) {
|
||||
setAutoFillBackground(true);
|
||||
auto layout = new QHBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
|
||||
|
||||
m_comboBox = new QComboBox;
|
||||
layout->addWidget(m_comboBox);
|
||||
}
|
||||
|
||||
EnumEditor::EnumEditor(TuxClocker::Device::EnumerationVec enums, QWidget *parent)
|
||||
: EnumEditor(parent) {
|
||||
auto qStrings = QVector<QString>::fromStdVector(transform([](auto e) {
|
||||
return QString::fromStdString(e.name);
|
||||
}, enums));
|
||||
|
||||
: EnumEditor(parent) {
|
||||
auto qStrings = QVector<QString>::fromStdVector(
|
||||
transform([](auto e) { return QString::fromStdString(e.name); }, enums));
|
||||
|
||||
for (uint i = 0; i < qStrings.length(); i++) {
|
||||
auto item = new QStandardItem;
|
||||
item->setText(qStrings[i]);
|
||||
item->setData(i, KeyRole);
|
||||
m_model.appendRow(item);
|
||||
}
|
||||
|
||||
|
||||
m_model.sort(0);
|
||||
m_comboBox->setModel(&m_model);
|
||||
}
|
||||
|
||||
QVariant EnumEditor::assignableData() {
|
||||
auto r = m_model.index(m_comboBox->currentIndex(), 0)
|
||||
.data(KeyRole).toUInt();
|
||||
auto r = m_model.index(m_comboBox->currentIndex(), 0).data(KeyRole).toUInt();
|
||||
return r;
|
||||
}
|
||||
QString EnumEditor::displayData() {
|
||||
auto r = m_model.index(m_comboBox->currentIndex(), 0)
|
||||
.data(Qt::DisplayRole).toString();
|
||||
auto r = m_model.index(m_comboBox->currentIndex(), 0).data(Qt::DisplayRole).toString();
|
||||
return r;
|
||||
}
|
||||
void EnumEditor::setAssignableData(QVariant data) {
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#include "FlagEditor.hpp"
|
||||
|
||||
|
||||
@@ -16,26 +16,25 @@
|
||||
#include <QStandardItemModel>
|
||||
#include <QStylePainter>
|
||||
|
||||
template <typename T>
|
||||
class FlagEditor : public QComboBox {
|
||||
template <typename T> class FlagEditor : public QComboBox {
|
||||
public:
|
||||
// Need to do the templated stuff in the header because C++ is retarded
|
||||
FlagEditor(QVector<std::tuple<QString, QIcon, T>> flagData,
|
||||
QWidget *parent = nullptr) : QComboBox(parent), m_skipNextHide(false) {
|
||||
FlagEditor(QVector<std::tuple<QString, QIcon, T>> flagData, QWidget *parent = nullptr)
|
||||
: QComboBox(parent), m_skipNextHide(false) {
|
||||
view()->viewport()->installEventFilter(this);
|
||||
auto model = new QStandardItemModel(this);
|
||||
|
||||
|
||||
for (const auto &flagItem : flagData) {
|
||||
auto item = new QStandardItem;
|
||||
|
||||
|
||||
auto [text, icon, flag] = flagItem;
|
||||
//auto icon = style()->standardIcon(QStyle::SP_ComputerIcon);
|
||||
//item->setData(icon, Qt::DecorationRole);
|
||||
|
||||
// auto icon = style()->standardIcon(QStyle::SP_ComputerIcon);
|
||||
// item->setData(icon, Qt::DecorationRole);
|
||||
|
||||
item->setCheckable(true);
|
||||
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
|
||||
item->setData(Qt::Checked, Qt::CheckStateRole);
|
||||
item->setData(icon ,Qt::DecorationRole);
|
||||
item->setData(icon, Qt::DecorationRole);
|
||||
item->setText(text);
|
||||
// Cast to uint so we don't need to register the type
|
||||
auto uFlag = static_cast<uint>(flag);
|
||||
@@ -44,12 +43,10 @@ public:
|
||||
model->appendRow(item);
|
||||
}
|
||||
setModel(model);
|
||||
|
||||
connect(model, &QStandardItemModel::itemChanged,
|
||||
[this](QStandardItem *item) {
|
||||
|
||||
connect(model, &QStandardItemModel::itemChanged, [this](QStandardItem *item) {
|
||||
auto flag = static_cast<QFlags<T>>(item->data(FlagRole).toUInt());
|
||||
auto checkState = item->data(Qt::CheckStateRole)
|
||||
.value<Qt::CheckState>();
|
||||
auto checkState = item->data(Qt::CheckStateRole).value<Qt::CheckState>();
|
||||
// Shouldn't ever be partially checked
|
||||
if (checkState == Qt::Unchecked)
|
||||
// Remove flag
|
||||
@@ -58,24 +55,24 @@ public:
|
||||
m_flags |= flag;
|
||||
flagsChanged(m_flags);
|
||||
});
|
||||
|
||||
|
||||
// Cause a repaint on changed flags
|
||||
// Boost won't let me discard the argument :(
|
||||
flagsChanged.connect([this](auto) {update();});
|
||||
flagsChanged.connect([this](auto) { update(); });
|
||||
}
|
||||
void setFlags(QFlags<T> flags) {
|
||||
m_flags = flags;
|
||||
flagsChanged(flags);
|
||||
auto handledFlags = m_flagHash.keys();
|
||||
auto uFlags = static_cast<uint>(flags);
|
||||
|
||||
|
||||
for (const auto &flag : handledFlags) {
|
||||
if (uFlags & flag)
|
||||
m_flagHash.value(flag)->setData(Qt::Checked, Qt::CheckStateRole);
|
||||
}
|
||||
}
|
||||
//QFlags<T> flags() {}
|
||||
|
||||
// QFlags<T> flags() {}
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *ev) {
|
||||
if (ev->type() == QEvent::MouseButtonPress && obj == view()->viewport())
|
||||
m_skipNextHide = true;
|
||||
@@ -89,7 +86,7 @@ public:
|
||||
}
|
||||
boost::signals2::signal<void(QFlags<T>)> flagsChanged;
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*) {
|
||||
void paintEvent(QPaintEvent *) {
|
||||
// Override paintEvent to draw custom text in the box
|
||||
QStylePainter painter(this);
|
||||
QStyleOptionComboBox opt;
|
||||
@@ -97,14 +94,16 @@ protected:
|
||||
QPalette rp;
|
||||
rp.setColor(QPalette::Highlight, Qt::red);
|
||||
opt.palette = rp;
|
||||
|
||||
|
||||
// Show which flags are selected
|
||||
auto items = m_flagHash.values();
|
||||
// Not sure why using 'auto' breaks something mysteriously here
|
||||
QList<QStandardItem*> checked = fplus::keep_if([](QStandardItem *item) {
|
||||
return item->data(Qt::CheckStateRole)
|
||||
.value<Qt::CheckState>() == Qt::Checked;
|
||||
}, items);
|
||||
QList<QStandardItem *> checked = fplus::keep_if(
|
||||
[](QStandardItem *item) {
|
||||
return item->data(Qt::CheckStateRole).value<Qt::CheckState>() ==
|
||||
Qt::Checked;
|
||||
},
|
||||
items);
|
||||
|
||||
// Where the first icon is drawn
|
||||
auto startY = opt.rect.top() + iconSize().height() / 2;
|
||||
@@ -117,10 +116,9 @@ protected:
|
||||
// Draw icons of selected items
|
||||
QRect drawRect(QPoint(startX, startY), iconSize());
|
||||
for (const auto &item : checked) {
|
||||
auto pixmap = item->data(Qt::DecorationRole)
|
||||
.value<QIcon>().pixmap(iconSize());
|
||||
painter.drawItemPixmap(drawRect,
|
||||
Qt::AlignCenter, pixmap);
|
||||
auto pixmap =
|
||||
item->data(Qt::DecorationRole).value<QIcon>().pixmap(iconSize());
|
||||
painter.drawItemPixmap(drawRect, Qt::AlignCenter, pixmap);
|
||||
drawRect.moveRight(drawRect.right() + deltaX);
|
||||
}
|
||||
}
|
||||
@@ -128,5 +126,5 @@ private:
|
||||
bool m_skipNextHide;
|
||||
const int FlagRole = Qt::UserRole + 1;
|
||||
QFlags<T> m_flags;
|
||||
QHash<uint, QStandardItem*> m_flagHash;
|
||||
QHash<uint, QStandardItem *> m_flagHash;
|
||||
};
|
||||
|
||||
@@ -32,21 +32,19 @@
|
||||
// Delet this
|
||||
namespace p = mpark::patterns;
|
||||
|
||||
Q_DECLARE_METATYPE(DynamicReadableProxy*)
|
||||
Q_DECLARE_METATYPE(DynamicReadableProxy *)
|
||||
|
||||
// TODO: make constructor of the type data Editor a = Maybe (Range a)
|
||||
class FunctionEditor : public QWidget {
|
||||
public:
|
||||
FunctionEditor(DeviceModel &model, TuxClocker::Device::RangeInfo rangeInfo,
|
||||
AssignableProxy &proxy, QString nodeName,
|
||||
QWidget *parent = nullptr)
|
||||
: QWidget(parent), m_assignableProxy(proxy),
|
||||
m_model(model), m_proxyModel(model),
|
||||
m_rangeInfo(rangeInfo) {
|
||||
AssignableProxy &proxy, QString nodeName, QWidget *parent = nullptr)
|
||||
: QWidget(parent), m_assignableProxy(proxy), m_model(model), m_proxyModel(model),
|
||||
m_rangeInfo(rangeInfo) {
|
||||
m_proxyModel.setDisableFiltered(true);
|
||||
m_proxyModel.setFlags(DeviceModel::DynamicReadable);
|
||||
m_proxyModel.setShowIcons(false);
|
||||
//m_proxyModel.setShowValueColumn(false);
|
||||
// m_proxyModel.setShowValueColumn(false);
|
||||
|
||||
m_layout = new QGridLayout(this);
|
||||
m_dependableReadableComboBox = new NodeSelector;
|
||||
@@ -63,18 +61,13 @@ public:
|
||||
m_layout->addWidget(m_dependableLabel, 0, 0, 1, 2);
|
||||
m_layout->addWidget(m_dependableReadableComboBox, 1, 0, 1, 2);
|
||||
m_dragView = new DragChartView;
|
||||
|
||||
p::match(rangeInfo) (
|
||||
p::pattern(p::as<TuxClocker::Device::Range<double>>(p::arg))
|
||||
= [this](auto dr) {
|
||||
m_dragView->setRange(0, 100, dr.min, dr.max);
|
||||
},
|
||||
p::pattern(p::as<TuxClocker::Device::Range<int>>(p::arg))
|
||||
= [this](auto ir) {
|
||||
m_dragView->setRange(0, 100, ir.min, ir.max);
|
||||
}
|
||||
);
|
||||
//m_dragView->setRange(0, 100, 0, 100);
|
||||
|
||||
p::match(rangeInfo)(
|
||||
p::pattern(p::as<TuxClocker::Device::Range<double>>(p::arg)) =
|
||||
[this](auto dr) { m_dragView->setRange(0, 100, dr.min, dr.max); },
|
||||
p::pattern(p::as<TuxClocker::Device::Range<int>>(p::arg)) =
|
||||
[this](auto ir) { m_dragView->setRange(0, 100, ir.min, ir.max); });
|
||||
// m_dragView->setRange(0, 100, 0, 100);
|
||||
m_layout->addWidget(m_dragView, 2, 0, 1, 2);
|
||||
m_applyButton = new QPushButton("Apply");
|
||||
// No connection to apply at first
|
||||
@@ -82,25 +75,23 @@ public:
|
||||
m_cancelButton = new QPushButton("Cancel");
|
||||
m_layout->addWidget(m_cancelButton, 3, 0, 1, 1);
|
||||
m_layout->addWidget(m_applyButton, 3, 1, 1, 1);
|
||||
|
||||
|
||||
connect(m_applyButton, &QPushButton::clicked, [this] {
|
||||
auto proxy = m_latestNodeIndex
|
||||
.data(DeviceModel::DynamicReadableProxyRole)
|
||||
.value<DynamicReadableProxy*>();
|
||||
//qDebug() << proxy;
|
||||
auto proxy = m_latestNodeIndex.data(DeviceModel::DynamicReadableProxyRole)
|
||||
.value<DynamicReadableProxy *>();
|
||||
// qDebug() << proxy;
|
||||
auto points = m_dragView->vector();
|
||||
if (points.length() < 2)
|
||||
return;
|
||||
//qDebug() << points;
|
||||
auto conn = std::make_shared<DynamicReadableConnection<int>>(
|
||||
*proxy, points);
|
||||
// qDebug() << points;
|
||||
auto conn =
|
||||
std::make_shared<DynamicReadableConnection<int>>(*proxy, points);
|
||||
assignableConnectionChanged(conn);
|
||||
});
|
||||
|
||||
|
||||
m_dragView->yAxis().setTitleText(nodeName);
|
||||
|
||||
m_dependableReadableComboBox->indexChanged
|
||||
.connect([this](auto &index) {
|
||||
m_dependableReadableComboBox->indexChanged.connect([this](auto &index) {
|
||||
m_latestNodeIndex = index;
|
||||
m_applyButton->setEnabled(true);
|
||||
auto nodeName = index.data(Qt::DisplayRole).toString();
|
||||
@@ -110,13 +101,13 @@ public:
|
||||
setLayout(m_layout);
|
||||
}
|
||||
boost::signals2::signal<void(std::shared_ptr<AssignableConnection>)>
|
||||
assignableConnectionChanged;
|
||||
assignableConnectionChanged;
|
||||
private:
|
||||
AssignableProxy &m_assignableProxy;
|
||||
DeviceModel &m_model;
|
||||
DeviceProxyModel m_proxyModel;
|
||||
DragChartView *m_dragView;
|
||||
//NodeSelector *m_nodeSelector;
|
||||
// NodeSelector *m_nodeSelector;
|
||||
QComboBox *m_functionComboBox;
|
||||
NodeSelector *m_dependableReadableComboBox;
|
||||
QGridLayout *m_layout;
|
||||
|
||||
@@ -14,37 +14,32 @@ class IntRangeEditor : public AbstractAssignableEditor {
|
||||
public:
|
||||
IntRangeEditor(QWidget *parent = nullptr) : AbstractAssignableEditor(parent) {
|
||||
setAutoFillBackground(true);
|
||||
|
||||
|
||||
m_layout = new QHBoxLayout(this);
|
||||
m_layout->setMargin(0);
|
||||
m_slider = new QSlider(Qt::Horizontal);
|
||||
m_spinBox = new QSpinBox;
|
||||
|
||||
|
||||
m_layout->addWidget(m_slider);
|
||||
m_layout->addWidget(m_spinBox);
|
||||
|
||||
|
||||
setLayout(m_layout);
|
||||
|
||||
|
||||
connect(m_slider, &QSlider::rangeChanged, m_spinBox, &QSpinBox::setRange);
|
||||
connect(m_slider, &QSlider::valueChanged, m_spinBox, &QSpinBox::setValue);
|
||||
connect(m_spinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_slider, &QSlider::setValue);
|
||||
connect(m_spinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_slider,
|
||||
&QSlider::setValue);
|
||||
}
|
||||
IntRangeEditor(TC::Device::Range<int> range, QWidget *parent = nullptr)
|
||||
: IntRangeEditor(parent) {
|
||||
: IntRangeEditor(parent) {
|
||||
setRange(range);
|
||||
}
|
||||
|
||||
virtual QVariant assignableData() override {return m_slider->value();}
|
||||
virtual QString displayData() override {
|
||||
return QString::number(m_slider->value());
|
||||
}
|
||||
virtual void setAssignableData(QVariant data) override {
|
||||
m_slider->setValue(data.toInt());
|
||||
}
|
||||
void setRange(TC::Device::Range<int> range) {
|
||||
m_slider->setRange(range.min, range.max);
|
||||
}
|
||||
int value() {return m_slider->value();}
|
||||
|
||||
virtual QVariant assignableData() override { return m_slider->value(); }
|
||||
virtual QString displayData() override { return QString::number(m_slider->value()); }
|
||||
virtual void setAssignableData(QVariant data) override { m_slider->setValue(data.toInt()); }
|
||||
void setRange(TC::Device::Range<int> range) { m_slider->setRange(range.min, range.max); }
|
||||
int value() { return m_slider->value(); }
|
||||
private:
|
||||
QHBoxLayout *m_layout;
|
||||
QSlider *m_slider;
|
||||
|
||||
@@ -14,17 +14,16 @@
|
||||
// TODO: If a disabled item is clicked, the item that is highlighted get selected instead
|
||||
class NodeSelector : public QComboBox {
|
||||
public:
|
||||
NodeSelector(QWidget *parent = nullptr) : QComboBox(parent),
|
||||
m_skipNextHide(false) {
|
||||
NodeSelector(QWidget *parent = nullptr) : QComboBox(parent), m_skipNextHide(false) {
|
||||
view()->viewport()->installEventFilter(this);
|
||||
}
|
||||
bool eventFilter(QObject *obj, QEvent *ev) {
|
||||
if (ev->type() == QEvent::MouseButtonPress && obj == view()->viewport()) {
|
||||
auto mouse_ev = static_cast<QMouseEvent*>(ev);
|
||||
auto mouse_ev = static_cast<QMouseEvent *>(ev);
|
||||
auto index = view()->indexAt(mouse_ev->pos());
|
||||
// Hide popup when an enabled item is clicked
|
||||
if (!view()->visualRect(index).contains(mouse_ev->pos()) ||
|
||||
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||
m_skipNextHide = true;
|
||||
else if (model()->flags(index) & Qt::ItemIsSelectable) {
|
||||
qDebug() << index.data(DeviceModel::DynamicReadableProxyRole);
|
||||
@@ -45,7 +44,7 @@ public:
|
||||
if (!view()->visualRect(index).contains(event->pos()) ||
|
||||
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||
qDebug() << index.data();
|
||||
|
||||
|
||||
QComboBox::mouseReleaseEvent(event);
|
||||
}*/
|
||||
void setView(QAbstractItemView *view) {
|
||||
@@ -57,12 +56,11 @@ public:
|
||||
virtual void showPopup() override {
|
||||
// TODO: don't account for scrollbar width when it isn't visible
|
||||
// This is quite a weird way to do it but it works
|
||||
auto vBarWidth =
|
||||
QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||
auto vBarWidth = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||
view()->setMinimumWidth(view()->sizeHintForColumn(0) + vBarWidth);
|
||||
QComboBox::showPopup();
|
||||
}
|
||||
boost::signals2::signal<void(QModelIndex&)> indexChanged;
|
||||
boost::signals2::signal<void(QModelIndex &)> indexChanged;
|
||||
private:
|
||||
bool m_skipNextHide;
|
||||
};
|
||||
|
||||
@@ -15,21 +15,16 @@ class AdaptorFactory {
|
||||
public:
|
||||
/* Returns a raw pointer since smart pointers caused some scope issues.
|
||||
* TODO: try to use smart pointers instead? */
|
||||
static std::optional<QDBusAbstractAdaptor*> adaptor(QObject *obj,
|
||||
DeviceInterface iface) {
|
||||
std::optional<QDBusAbstractAdaptor*> retval = std::nullopt;
|
||||
match(iface) (
|
||||
pattern(as<StaticReadable>(arg)) = [&](auto sr) {
|
||||
retval = new StaticReadableAdaptor(obj, sr);
|
||||
},
|
||||
pattern(as<DynamicReadable>(arg)) = [&](auto dr) {
|
||||
retval = new DynamicReadableAdaptor(obj, dr);
|
||||
},
|
||||
pattern(as<Assignable>(arg)) = [&](auto a) {
|
||||
retval = new AssignableAdaptor(obj, a);
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);
|
||||
static std::optional<QDBusAbstractAdaptor *> adaptor(QObject *obj, DeviceInterface iface) {
|
||||
std::optional<QDBusAbstractAdaptor *> retval = std::nullopt;
|
||||
match(iface)(
|
||||
pattern(as<StaticReadable>(arg)) =
|
||||
[&](auto sr) { retval = new StaticReadableAdaptor(obj, sr); },
|
||||
pattern(as<DynamicReadable>(arg)) =
|
||||
[&](auto dr) { retval = new DynamicReadableAdaptor(obj, dr); },
|
||||
pattern(as<Assignable>(arg)) =
|
||||
[&](auto a) { retval = new AssignableAdaptor(obj, a); },
|
||||
pattern(_) = [] {});
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,41 +25,39 @@ Q_DECLARE_METATYPE(TCDBus::Result<QString>)
|
||||
code is stored in 'value' */
|
||||
class DynamicReadableAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
explicit DynamicReadableAdaptor(QObject *obj, DynamicReadable dr) :
|
||||
QDBusAbstractAdaptor(obj) {
|
||||
// Ideally this should be moved somewhere else but QMetaType does not handle namespaces well
|
||||
explicit DynamicReadableAdaptor(QObject *obj, DynamicReadable dr)
|
||||
: QDBusAbstractAdaptor(obj) {
|
||||
// Ideally this should be moved somewhere else but QMetaType does not handle
|
||||
// namespaces well
|
||||
qDBusRegisterMetaType<TCDBus::Result<QDBusVariant>>();
|
||||
qDBusRegisterMetaType<TCDBus::Result<QString>>();
|
||||
m_dynamicReadable = dr;
|
||||
}
|
||||
// Might not have a unit
|
||||
TCDBus::Result<QString> unit_() {
|
||||
return (m_dynamicReadable.unit().has_value()) ?
|
||||
TCDBus::Result<QString>{
|
||||
false,
|
||||
QString::fromStdString(m_dynamicReadable.unit().value())} :
|
||||
TCDBus::Result<QString>{true, QString("")};
|
||||
return (m_dynamicReadable.unit().has_value())
|
||||
? TCDBus::Result<QString>{false,
|
||||
QString::fromStdString(m_dynamicReadable.unit().value())}
|
||||
: TCDBus::Result<QString>{true, QString("")};
|
||||
}
|
||||
public Q_SLOTS:
|
||||
TCDBus::Result<QDBusVariant> value() {
|
||||
QVariant v;
|
||||
TCDBus::Result<QDBusVariant> res{.error = false};
|
||||
/* We have to unwrap the value here, another option would be to convert the std::variant
|
||||
* to char*, but that comes with its own issues */
|
||||
match(m_dynamicReadable.read())
|
||||
(pattern(as<ReadableValue>(arg)) = [&](auto val) {
|
||||
match(val)
|
||||
(pattern(as<uint>(arg)) = [&](auto u) {
|
||||
v.setValue(u);
|
||||
},
|
||||
pattern(as<double>(arg)) = [&](auto d) {
|
||||
v.setValue(d);
|
||||
});
|
||||
/* We have to unwrap the value here, another option would be to convert the
|
||||
* std::variant to char*, but that comes with its own issues */
|
||||
match(m_dynamicReadable.read())(
|
||||
pattern(as<ReadableValue>(arg)) =
|
||||
[&](auto val) {
|
||||
match(val)(
|
||||
pattern(as<uint>(arg)) = [&](auto u) { v.setValue(u); },
|
||||
pattern(as<double>(arg)) = [&](auto d) { v.setValue(d); });
|
||||
},
|
||||
pattern(as<ReadError>(arg)) = [&](auto err) {
|
||||
pattern(as<ReadError>(arg)) =
|
||||
[&](auto err) {
|
||||
v.setValue(static_cast<int>(err));
|
||||
res.error = true;
|
||||
});
|
||||
});
|
||||
res.value = QDBusVariant(v);
|
||||
return res;
|
||||
}
|
||||
@@ -67,7 +65,7 @@ private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.DynamicReadable")
|
||||
Q_PROPERTY(TCDBus::Result<QString> unit READ unit_)
|
||||
|
||||
|
||||
DynamicReadable m_dynamicReadable;
|
||||
};
|
||||
|
||||
@@ -79,8 +77,8 @@ Q_DECLARE_METATYPE(TCDBus::Result<uint>)
|
||||
|
||||
class AssignableAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
explicit AssignableAdaptor(QObject *obj, Assignable a) :
|
||||
QDBusAbstractAdaptor(obj), m_assignable(a) {
|
||||
explicit AssignableAdaptor(QObject *obj, Assignable a)
|
||||
: QDBusAbstractAdaptor(obj), m_assignable(a) {
|
||||
qDBusRegisterMetaType<TCDBus::Range>();
|
||||
qDBusRegisterMetaType<TCDBus::Enumeration>();
|
||||
qDBusRegisterMetaType<QVector<TCDBus::Enumeration>>();
|
||||
@@ -92,108 +90,95 @@ public:
|
||||
|
||||
QVariant a_info;
|
||||
// Unwrap AssignableInfo :(
|
||||
match(a.assignableInfo())
|
||||
(pattern(as<RangeInfo>(arg)) = [&](auto r_info) {
|
||||
match(r_info)
|
||||
(pattern(as<Range<double>>(arg)) = [&](auto dr) {
|
||||
match(a.assignableInfo())(
|
||||
pattern(as<RangeInfo>(arg)) =
|
||||
[&](auto r_info) {
|
||||
match(r_info)(
|
||||
pattern(as<Range<double>>(arg)) =
|
||||
[&](auto dr) {
|
||||
TCDBus::Range r{
|
||||
.min = QDBusVariant(QVariant(dr.min)),
|
||||
.max = QDBusVariant(QVariant(dr.max))
|
||||
};
|
||||
.min = QDBusVariant(QVariant(dr.min)),
|
||||
.max = QDBusVariant(QVariant(dr.max))};
|
||||
a_info.setValue(r);
|
||||
},
|
||||
pattern(as<Range<int>>(arg)) = [&](auto ir) {
|
||||
pattern(as<Range<int>>(arg)) =
|
||||
[&](auto ir) {
|
||||
TCDBus::Range r{
|
||||
.min = QDBusVariant(QVariant(ir.min)),
|
||||
.max = QDBusVariant(QVariant(ir.max))
|
||||
};
|
||||
.min = QDBusVariant(QVariant(ir.min)),
|
||||
.max = QDBusVariant(QVariant(ir.max))};
|
||||
a_info.setValue(r);
|
||||
});
|
||||
});
|
||||
},
|
||||
pattern(as<EnumerationVec>(arg)) = [&](auto enums) {
|
||||
pattern(as<EnumerationVec>(arg)) =
|
||||
[&](auto enums) {
|
||||
QVector<TCDBus::Enumeration> entries;
|
||||
for (const auto &e : enums)
|
||||
entries.append({e.key, QString::fromStdString(e.name)});
|
||||
a_info.setValue(entries);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
m_dbusAssignableInfo = QDBusVariant(a_info);
|
||||
}
|
||||
QDBusVariant assignableInfo_() {return m_dbusAssignableInfo;}
|
||||
QDBusVariant assignableInfo_() { return m_dbusAssignableInfo; }
|
||||
TCDBus::Result<QString> unit_() {
|
||||
if (m_assignable.unit().has_value()) {
|
||||
return TCDBus::Result<QString> {
|
||||
.error = false,
|
||||
.value = QString::fromStdString(m_assignable.unit().value())
|
||||
};
|
||||
|
||||
return TCDBus::Result<QString>{.error = false,
|
||||
.value = QString::fromStdString(m_assignable.unit().value())};
|
||||
}
|
||||
return TCDBus::Result<QString> {
|
||||
.error = true,
|
||||
.value = QString("")
|
||||
};
|
||||
return TCDBus::Result<QString>{.error = true, .value = QString("")};
|
||||
}
|
||||
public Q_SLOTS:
|
||||
TCDBus::Result<QDBusVariant> currentValue() {
|
||||
// Indicate error by default
|
||||
TCDBus::Result<QDBusVariant> retval {
|
||||
.error = true,
|
||||
.value = QDBusVariant(QVariant(0))
|
||||
};
|
||||
match(m_assignable.currentValue())
|
||||
(pattern(some(arg)) = [&](auto aa) {
|
||||
retval.error = false;
|
||||
match(aa)
|
||||
(pattern(as<double>(arg)) = [&](auto d) {
|
||||
retval.value = QDBusVariant(QVariant(d));
|
||||
},
|
||||
pattern(as<int>(arg)) = [&](auto i) {
|
||||
retval.value = QDBusVariant(QVariant(i));
|
||||
},
|
||||
pattern(as<uint>(arg)) = [&](auto u) {
|
||||
retval.value = QDBusVariant(QVariant(u));
|
||||
}
|
||||
);
|
||||
TCDBus::Result<QDBusVariant> retval{
|
||||
.error = true, .value = QDBusVariant(QVariant(0))};
|
||||
match(m_assignable.currentValue())(
|
||||
pattern(some(arg)) =
|
||||
[&](auto aa) {
|
||||
retval.error = false;
|
||||
match(aa)(
|
||||
pattern(as<double>(arg)) =
|
||||
[&](auto d) { retval.value = QDBusVariant(QVariant(d)); },
|
||||
pattern(as<int>(arg)) =
|
||||
[&](auto i) { retval.value = QDBusVariant(QVariant(i)); },
|
||||
pattern(as<uint>(arg)) =
|
||||
[&](auto u) { retval.value = QDBusVariant(QVariant(u)); });
|
||||
},
|
||||
pattern(none) = [] {}
|
||||
);
|
||||
pattern(none) = [] {});
|
||||
return retval;
|
||||
}
|
||||
TCDBus::Result<int> assign(QDBusVariant arg_) {
|
||||
auto v = arg_.variant();
|
||||
|
||||
|
||||
std::optional<AssignmentError> retval;
|
||||
|
||||
match(m_assignable.assignableInfo())
|
||||
(pattern(as<RangeInfo>(arg)) = [&](auto ri) {
|
||||
match(ri)
|
||||
(pattern(as<Range<double>>(_)) = [&] {
|
||||
retval = m_assignable.assign(v.value<double>());
|
||||
},
|
||||
pattern(as<Range<int>>(_)) = [&] {
|
||||
retval = m_assignable.assign(v.value<int>());
|
||||
});
|
||||
|
||||
match(m_assignable.assignableInfo())(
|
||||
pattern(as<RangeInfo>(arg)) =
|
||||
[&](auto ri) {
|
||||
match(ri)(
|
||||
pattern(as<Range<double>>(_)) =
|
||||
[&] { retval = m_assignable.assign(v.value<double>()); },
|
||||
pattern(as<Range<int>>(_)) =
|
||||
[&] { retval = m_assignable.assign(v.value<int>()); });
|
||||
},
|
||||
pattern(as<EnumerationVec>(_)) = [&] {
|
||||
retval = m_assignable.assign(v.value<uint>());
|
||||
});
|
||||
|
||||
pattern(as<EnumerationVec>(_)) =
|
||||
[&] { retval = m_assignable.assign(v.value<uint>()); });
|
||||
|
||||
TCDBus::Result<int> res{.error = false, 0};
|
||||
// Check if optional contains error
|
||||
if (retval.has_value()) {
|
||||
res.error = true;
|
||||
res.value = static_cast<int>(retval.value());
|
||||
}
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Assignable")
|
||||
Q_PROPERTY(QDBusVariant assignableInfo READ assignableInfo_)
|
||||
Q_PROPERTY(TCDBus::Result<QString> unit READ unit_)
|
||||
|
||||
|
||||
Assignable m_assignable;
|
||||
QDBusVariant m_dbusAssignableInfo;
|
||||
};
|
||||
@@ -204,43 +189,44 @@ Q_DECLARE_METATYPE(TCDBus::FlatTreeNode<TCDBus::DeviceNode>)
|
||||
// Holds the name and hash of nodes, even if they don't implement an interface
|
||||
class NodeAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
NodeAdaptor(QObject *obj, DeviceNode devNode) : QDBusAbstractAdaptor(obj),
|
||||
m_devNode(devNode) {}
|
||||
QString name_() {return QString::fromStdString(m_devNode.name);}
|
||||
QString hash_() {return QString::fromStdString(m_devNode.hash);}
|
||||
NodeAdaptor(QObject *obj, DeviceNode devNode)
|
||||
: QDBusAbstractAdaptor(obj), m_devNode(devNode) {}
|
||||
QString name_() { return QString::fromStdString(m_devNode.name); }
|
||||
QString hash_() { return QString::fromStdString(m_devNode.hash); }
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Node")
|
||||
Q_PROPERTY(QString hash READ hash_)
|
||||
Q_PROPERTY(QString name READ name_)
|
||||
|
||||
|
||||
DeviceNode m_devNode;
|
||||
};
|
||||
|
||||
// Holds the main tree and returns it as a list (because parsing XML sucks)
|
||||
class MainAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
explicit MainAdaptor(QObject *obj, TreeNode<TCDBus::DeviceNode> node) : QDBusAbstractAdaptor(obj) {
|
||||
explicit MainAdaptor(QObject *obj, TreeNode<TCDBus::DeviceNode> node)
|
||||
: QDBusAbstractAdaptor(obj) {
|
||||
qDBusRegisterMetaType<TCDBus::DeviceNode>();
|
||||
qDBusRegisterMetaType<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>();
|
||||
qDBusRegisterMetaType<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>>();
|
||||
|
||||
|
||||
for (const auto &f_node : node.toFlatTree().nodes) {
|
||||
// Copy child indices
|
||||
QVector<int> childIndices;
|
||||
for (const auto &i : f_node.childIndices)
|
||||
childIndices.append(i);
|
||||
|
||||
|
||||
TCDBus::FlatTreeNode<TCDBus::DeviceNode> fn{f_node.value, childIndices};
|
||||
m_flatTree.append(fn);
|
||||
}
|
||||
}
|
||||
public Q_SLOTS:
|
||||
QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>> flatDeviceTree() {return m_flatTree;}
|
||||
QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>> flatDeviceTree() { return m_flatTree; }
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker")
|
||||
|
||||
|
||||
TreeNode<TCDBus::DeviceNode> m_rootNode;
|
||||
QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>> m_flatTree;
|
||||
};
|
||||
@@ -248,30 +234,26 @@ private:
|
||||
class StaticReadableAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
StaticReadableAdaptor(QObject *obj, StaticReadable readable)
|
||||
: QDBusAbstractAdaptor(obj), m_readable(readable) {
|
||||
: QDBusAbstractAdaptor(obj), m_readable(readable) {
|
||||
qDBusRegisterMetaType<TCDBus::Result<QString>>();
|
||||
// Unwrap the value and store in QDBusVariant
|
||||
match(m_readable.value()) (
|
||||
pattern(as<uint>(arg)) = [this](auto i) {
|
||||
m_value = QDBusVariant(QVariant(i));
|
||||
}
|
||||
);
|
||||
|
||||
m_unit = (m_readable.unit().has_value()) ? TCDBus::Result<QString>{
|
||||
false,
|
||||
QString::fromStdString(m_readable.unit().value())
|
||||
} : TCDBus::Result<QString>{
|
||||
true, ""
|
||||
};
|
||||
match(m_readable.value())(pattern(as<uint>(arg)) = [this](auto i) {
|
||||
m_value = QDBusVariant(QVariant(i));
|
||||
});
|
||||
|
||||
m_unit = (m_readable.unit().has_value())
|
||||
? TCDBus::Result<QString>{false,
|
||||
QString::fromStdString(m_readable.unit().value())}
|
||||
: TCDBus::Result<QString>{true, ""};
|
||||
}
|
||||
QDBusVariant value_() {return m_value;}
|
||||
TCDBus::Result<QString> unit_() {return m_unit;}
|
||||
QDBusVariant value_() { return m_value; }
|
||||
TCDBus::Result<QString> unit_() { return m_unit; }
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.StaticReadable")
|
||||
Q_PROPERTY(TCDBus::Result<QString> unit READ unit_)
|
||||
Q_PROPERTY(QDBusVariant value READ value_)
|
||||
|
||||
|
||||
StaticReadable m_readable;
|
||||
TCDBus::Result<QString> m_unit;
|
||||
QDBusVariant m_value;
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
QDBusAbstractAdaptor *AssignableAdaptorFactory::assignableAdaptor(QObject *parent, const tc_assignable_node_t *node) {
|
||||
QDBusAbstractAdaptor *AssignableAdaptorFactory::assignableAdaptor(
|
||||
QObject *parent, const tc_assignable_node_t *node) {
|
||||
if (!node->assign_callback) {
|
||||
// Placeholder node
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
switch (node->value_category) {
|
||||
case TC_ASSIGNABLE_RANGE:
|
||||
return new AssignableVariantRangeAdaptor(parent, node);
|
||||
case TC_ASSIGNABLE_ENUM:
|
||||
return new AssignableEnumAdaptor(parent, node);
|
||||
default:
|
||||
return nullptr;
|
||||
case TC_ASSIGNABLE_RANGE:
|
||||
return new AssignableVariantRangeAdaptor(parent, node);
|
||||
case TC_ASSIGNABLE_ENUM:
|
||||
return new AssignableEnumAdaptor(parent, node);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
|
||||
class AssignableAdaptorFactory {
|
||||
public:
|
||||
static QDBusAbstractAdaptor *assignableAdaptor(QObject *parent, const tc_assignable_node_t *node);
|
||||
static QDBusAbstractAdaptor *assignableAdaptor(
|
||||
QObject *parent, const tc_assignable_node_t *node);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#include "AssignableEnumAdaptor.h"
|
||||
|
||||
AssignableEnumAdaptor::AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t* node) : QDBusAbstractAdaptor(obj) {
|
||||
AssignableEnumAdaptor::AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t *node)
|
||||
: QDBusAbstractAdaptor(obj) {
|
||||
m_node = node;
|
||||
}
|
||||
|
||||
QList <EnumData> AssignableEnumAdaptor::values_() {
|
||||
QList <EnumData> retval;
|
||||
|
||||
QList<EnumData> AssignableEnumAdaptor::values_() {
|
||||
QList<EnumData> retval;
|
||||
|
||||
for (uint16_t i = 0; i < m_node->enum_info.property_count; i++) {
|
||||
EnumData ed = {
|
||||
.name = QString(m_node->enum_info.properties[i]),
|
||||
.value = i
|
||||
};
|
||||
EnumData ed = {.name = QString(m_node->enum_info.properties[i]), .value = i};
|
||||
retval.append(ed);
|
||||
}
|
||||
return retval;
|
||||
@@ -19,11 +17,11 @@ QList <EnumData> AssignableEnumAdaptor::values_() {
|
||||
|
||||
short AssignableEnumAdaptor::assign(ushort value) {
|
||||
tc_variant_t tc_v;
|
||||
|
||||
|
||||
tc_v.uint_value = value;
|
||||
tc_v.data_type = TC_TYPE_UINT;
|
||||
|
||||
|
||||
assert(m_node->assign_callback);
|
||||
|
||||
|
||||
return m_node->assign_callback(tc_v, m_node);
|
||||
}
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
|
||||
class AssignableEnumAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
explicit AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t* node);
|
||||
QList <EnumData> values_();
|
||||
QString name_() {return QString(m_node->name);}
|
||||
explicit AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t *node);
|
||||
QList<EnumData> values_();
|
||||
QString name_() { return QString(m_node->name); }
|
||||
public Q_SLOTS:
|
||||
short assign(ushort value);
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Assignable.Enum")
|
||||
Q_PROPERTY(QList <EnumData> values READ values_)
|
||||
Q_PROPERTY(QList<EnumData> values READ values_)
|
||||
Q_PROPERTY(QString name READ name_)
|
||||
|
||||
|
||||
const tc_assignable_node_t *m_node;
|
||||
};
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
#include "AssignableVariantRangeAdaptor.h"
|
||||
|
||||
AssignableVariantRangeAdaptor::AssignableVariantRangeAdaptor(QObject *obj, const tc_assignable_node_t *node) : QDBusAbstractAdaptor(obj) {
|
||||
AssignableVariantRangeAdaptor::AssignableVariantRangeAdaptor(
|
||||
QObject *obj, const tc_assignable_node_t *node)
|
||||
: QDBusAbstractAdaptor(obj) {
|
||||
m_node = node;
|
||||
}
|
||||
|
||||
QDBusVariant AssignableVariantRangeAdaptor::min_() {
|
||||
QVariant v;
|
||||
QDBusVariant dv;
|
||||
|
||||
|
||||
switch (m_node->range_info.range_data_type) {
|
||||
case TC_ASSIGNABLE_RANGE_INT: {
|
||||
qlonglong val = m_node->range_info.int_range.min;
|
||||
v.setValue(val);
|
||||
break;
|
||||
}
|
||||
case TC_ASSIGNABLE_RANGE_DOUBLE:
|
||||
v.setValue(m_node->range_info.double_range.min);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case TC_ASSIGNABLE_RANGE_INT: {
|
||||
qlonglong val = m_node->range_info.int_range.min;
|
||||
v.setValue(val);
|
||||
break;
|
||||
}
|
||||
case TC_ASSIGNABLE_RANGE_DOUBLE:
|
||||
v.setValue(m_node->range_info.double_range.min);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dv.setVariant(v);
|
||||
return dv;
|
||||
@@ -27,18 +29,18 @@ QDBusVariant AssignableVariantRangeAdaptor::min_() {
|
||||
QDBusVariant AssignableVariantRangeAdaptor::max_() {
|
||||
QVariant v;
|
||||
QDBusVariant dv;
|
||||
|
||||
|
||||
switch (m_node->range_info.range_data_type) {
|
||||
case TC_ASSIGNABLE_RANGE_INT: {
|
||||
qlonglong val = m_node->range_info.int_range.max;
|
||||
v.setValue(val);
|
||||
break;
|
||||
}
|
||||
case TC_ASSIGNABLE_RANGE_DOUBLE:
|
||||
v.setValue(m_node->range_info.double_range.max);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case TC_ASSIGNABLE_RANGE_INT: {
|
||||
qlonglong val = m_node->range_info.int_range.max;
|
||||
v.setValue(val);
|
||||
break;
|
||||
}
|
||||
case TC_ASSIGNABLE_RANGE_DOUBLE:
|
||||
v.setValue(m_node->range_info.double_range.max);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dv.setVariant(v);
|
||||
return dv;
|
||||
@@ -46,21 +48,23 @@ QDBusVariant AssignableVariantRangeAdaptor::max_() {
|
||||
|
||||
short AssignableVariantRangeAdaptor::assign(QDBusVariant value) {
|
||||
auto v = value.variant();
|
||||
|
||||
|
||||
// Convert to tc_variant_t
|
||||
tc_variant_t tc_v;
|
||||
|
||||
if (v.canConvert<qlonglong>() && m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_INT) {
|
||||
|
||||
if (v.canConvert<qlonglong>() &&
|
||||
m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_INT) {
|
||||
tc_v.int_value = qvariant_cast<qlonglong>(v);
|
||||
tc_v.data_type = TC_TYPE_INT;
|
||||
return m_node->assign_callback(tc_v, m_node);
|
||||
}
|
||||
|
||||
if (v.canConvert<double>() && m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_DOUBLE) {
|
||||
|
||||
if (v.canConvert<double>() &&
|
||||
m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_DOUBLE) {
|
||||
tc_v.double_value = qvariant_cast<double>(v);
|
||||
tc_v.data_type = TC_TYPE_DOUBLE;
|
||||
return m_node->assign_callback(tc_v, m_node);
|
||||
}
|
||||
|
||||
|
||||
return TC_EGENERIC;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
class AssignableVariantRangeAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
explicit AssignableVariantRangeAdaptor(QObject *obj, const tc_assignable_node_t *node);
|
||||
QString name_() {return QString(m_node->name);}
|
||||
QString unit_() {return QString(m_node->unit);}
|
||||
QString name_() { return QString(m_node->name); }
|
||||
QString unit_() { return QString(m_node->unit); }
|
||||
QDBusVariant min_();
|
||||
QDBusVariant max_();
|
||||
public Q_SLOTS:
|
||||
@@ -20,6 +20,6 @@ private:
|
||||
Q_PROPERTY(QString unit READ unit_)
|
||||
Q_PROPERTY(QDBusVariant min READ min_)
|
||||
Q_PROPERTY(QDBusVariant max READ max_)
|
||||
|
||||
|
||||
const tc_assignable_node_t *m_node;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
struct EnumData {
|
||||
QString name;
|
||||
unsigned value;
|
||||
|
||||
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const EnumData &data) {
|
||||
arg.beginStructure();
|
||||
arg << data.name << data.value;
|
||||
@@ -21,18 +21,18 @@ struct EnumData {
|
||||
}
|
||||
/*friend QDBusArgument &operator<<(QDBusArgument &arg, const QList <EnumData> list) {
|
||||
arg.beginArray();
|
||||
|
||||
|
||||
for (auto data : list) {
|
||||
arg << data;
|
||||
}
|
||||
arg.endArray();
|
||||
return arg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
friend const QDBusArgument &operator>>(const QDBusArgument &arg, QList <EnumData> &list) {
|
||||
arg.beginArray();
|
||||
list.clear();
|
||||
|
||||
|
||||
while (!arg.atEnd()) {
|
||||
EnumData data;
|
||||
arg >> data;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ReadableAdaptorFactory.h"
|
||||
|
||||
QDBusAbstractAdaptor *ReadableAdaptorFactory::readableAdaptor(QObject *parent, const tc_readable_node_t *node) {
|
||||
QDBusAbstractAdaptor *ReadableAdaptorFactory::readableAdaptor(
|
||||
QObject *parent, const tc_readable_node_t *node) {
|
||||
if (!node->value_callback) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
|
||||
class ReadableAdaptorFactory {
|
||||
public:
|
||||
static QDBusAbstractAdaptor *readableAdaptor(QObject *parent, const tc_readable_node_t *node);
|
||||
static QDBusAbstractAdaptor *readableAdaptor(
|
||||
QObject *parent, const tc_readable_node_t *node);
|
||||
};
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
#include "ReadableDynamicVariantAdaptor.h"
|
||||
|
||||
ReadableDynamicVariantAdaptor::ReadableDynamicVariantAdaptor(QObject *obj, const tc_readable_node_t *node) : QDBusAbstractAdaptor(obj) {
|
||||
ReadableDynamicVariantAdaptor::ReadableDynamicVariantAdaptor(
|
||||
QObject *obj, const tc_readable_node_t *node)
|
||||
: QDBusAbstractAdaptor(obj) {
|
||||
m_node = node;
|
||||
}
|
||||
|
||||
Result <QDBusVariant> ReadableDynamicVariantAdaptor::value() {
|
||||
Result<QDBusVariant> ReadableDynamicVariantAdaptor::value() {
|
||||
QDBusVariant dv;
|
||||
QVariant v;
|
||||
|
||||
|
||||
auto result = m_node->value_callback(m_node);
|
||||
switch (result.data.data_type) {
|
||||
case TC_TYPE_DOUBLE:
|
||||
v.setValue(result.data.double_value);
|
||||
break;
|
||||
case TC_TYPE_INT:
|
||||
v.setValue(result.data.int_value);
|
||||
break;
|
||||
case TC_TYPE_UINT: {
|
||||
qulonglong value = result.data.uint_value;
|
||||
v.setValue(value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
case TC_TYPE_DOUBLE:
|
||||
v.setValue(result.data.double_value);
|
||||
break;
|
||||
case TC_TYPE_INT:
|
||||
v.setValue(result.data.int_value);
|
||||
break;
|
||||
case TC_TYPE_UINT: {
|
||||
qulonglong value = result.data.uint_value;
|
||||
v.setValue(value);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dv.setVariant(v);
|
||||
|
||||
Result <QDBusVariant> res = {
|
||||
.valid = result.valid,
|
||||
.value = dv
|
||||
};
|
||||
|
||||
|
||||
Result<QDBusVariant> res = {.valid = result.valid, .value = dv};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
class ReadableDynamicVariantAdaptor : public QDBusAbstractAdaptor {
|
||||
public:
|
||||
explicit ReadableDynamicVariantAdaptor(QObject *obj, const tc_readable_node_t *node);
|
||||
QString name_() {return QString(m_node->name);}
|
||||
QString name_() { return QString(m_node->name); }
|
||||
public Q_SLOTS:
|
||||
Result <QDBusVariant> value();
|
||||
Result<QDBusVariant> value();
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Readable.Dynamic.Variant")
|
||||
Q_PROPERTY(QString name READ name_)
|
||||
|
||||
|
||||
const tc_readable_node_t *m_node;
|
||||
};
|
||||
|
||||
@@ -5,23 +5,22 @@
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
template <typename T>
|
||||
struct Result {
|
||||
bool valid;
|
||||
T value;
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const Result &res) {
|
||||
arg.beginStructure();
|
||||
arg << res.valid << res.value;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
template <typename T> struct Result {
|
||||
bool valid;
|
||||
T value;
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const Result &res) {
|
||||
arg.beginStructure();
|
||||
arg << res.valid << res.value;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Result &varRes) {
|
||||
arg.beginStructure();
|
||||
arg >> varRes.valid >> varRes.value;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Result &varRes) {
|
||||
arg.beginStructure();
|
||||
arg >> varRes.valid >> varRes.value;
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Result<QDBusVariant>)
|
||||
|
||||
@@ -20,75 +20,72 @@ namespace TCDBus = TuxClocker::DBus;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
|
||||
auto connection = QDBusConnection::systemBus();
|
||||
auto plugins = DevicePlugin::loadPlugins();
|
||||
QVector<QDBusAbstractAdaptor*> adaptors;
|
||||
QVector<QDBusAbstractAdaptor *> adaptors;
|
||||
QObject root;
|
||||
TreeNode<TCDBus::DeviceNode> dbusRootNode;
|
||||
|
||||
std::function<void(TreeNode<DeviceNode>, QString, TreeNode<TCDBus::DeviceNode>*)> traverse;
|
||||
traverse = [&traverse, &connection, &adaptors, &root](
|
||||
TreeNode<DeviceNode> node,
|
||||
QString parentPath, TreeNode<TCDBus::DeviceNode> *dbusNode) {
|
||||
|
||||
std::function<void(TreeNode<DeviceNode>, QString, TreeNode<TCDBus::DeviceNode> *)> traverse;
|
||||
traverse = [&traverse, &connection, &adaptors, &root](TreeNode<DeviceNode> node,
|
||||
QString parentPath, TreeNode<TCDBus::DeviceNode> *dbusNode) {
|
||||
auto obj = new QObject(&root); // Is destroyed when root goes out of scope
|
||||
auto objName = QString::fromStdString(node.value().hash).replace(" ", "");
|
||||
auto thisPath = parentPath + objName;
|
||||
QString ifaceName;
|
||||
adaptors.append(new NodeAdaptor(obj, node.value()));
|
||||
if_let(pattern(some(arg)) = node.value().interface) = [&](auto iface) {
|
||||
if_let(pattern(some(arg)) =
|
||||
AdaptorFactory::adaptor(obj, iface)) = [&](auto adaptor) {
|
||||
if_let(pattern(some(arg)) =
|
||||
AdaptorFactory::adaptor(obj, iface)) = [&](auto adaptor) {
|
||||
adaptors.append(adaptor);
|
||||
// TODO: don't hardcode interface name
|
||||
// Maybe create an intermediate class that contains the interface name to avoid this
|
||||
// For some reason this throws a bad_cast when using 'as' with pointer type
|
||||
match(*adaptor)
|
||||
(pattern(as<AssignableAdaptor>(_)) = [&] {
|
||||
ifaceName = "org.tuxclocker.Assignable";
|
||||
},
|
||||
pattern(as<DynamicReadableAdaptor>(_)) = [&] {
|
||||
ifaceName = "org.tuxclocker.DynamicReadable";
|
||||
},
|
||||
pattern(as<StaticReadableAdaptor>(_)) = [&] {
|
||||
ifaceName = "org.tuxclocker.StaticReadable";
|
||||
},
|
||||
pattern(_) = [] {});
|
||||
// Maybe create an intermediate class that contains the interface
|
||||
// name to avoid this For some reason this throws a bad_cast when
|
||||
// using 'as' with pointer type
|
||||
match (*adaptor)(
|
||||
pattern(as<AssignableAdaptor>(_)) =
|
||||
[&] { ifaceName = "org.tuxclocker.Assignable"; },
|
||||
pattern(as<DynamicReadableAdaptor>(_)) =
|
||||
[&] { ifaceName = "org.tuxclocker.DynamicReadable"; },
|
||||
pattern(as<StaticReadableAdaptor>(_)) =
|
||||
[&] { ifaceName = "org.tuxclocker.StaticReadable"; },
|
||||
pattern(_) = [] {});
|
||||
};
|
||||
};
|
||||
if (!connection.registerObject(thisPath, obj))
|
||||
qDebug() << "Couldn't register object at path" << thisPath << connection.lastError();
|
||||
|
||||
qDebug() << "Couldn't register object at path" << thisPath
|
||||
<< connection.lastError();
|
||||
|
||||
auto thisDBusNode = TreeNode<TCDBus::DeviceNode>{{ifaceName, thisPath}};
|
||||
dbusNode->appendChild(thisDBusNode);
|
||||
qDebug() << thisPath;
|
||||
for (const auto &child : node.children())
|
||||
traverse(child, thisPath + "/", &dbusNode->childrenPtr()->back());
|
||||
};
|
||||
|
||||
|
||||
TreeNode<DeviceNode> lvl1nodes;
|
||||
if (plugins.has_value()) {
|
||||
qDebug() << "found " << plugins.value().size() << " plugins";
|
||||
for (auto &plugin : plugins.value()) {
|
||||
//rootNodes.append(plugin->deviceRootNode());
|
||||
// Root node should always be empty
|
||||
// rootNodes.append(plugin->deviceRootNode());
|
||||
// Root node should always be empty
|
||||
for (const auto &node : plugin->deviceRootNode().children()) {
|
||||
lvl1nodes.appendChild(node);
|
||||
traverse(node, "/", &dbusRootNode);
|
||||
|
||||
TreeNode<DeviceNode>::preorder(node, [](auto val) {
|
||||
qDebug() << QString::fromStdString(val.name);
|
||||
});
|
||||
|
||||
TreeNode<DeviceNode>::preorder(node,
|
||||
[](auto val) { qDebug() << QString::fromStdString(val.name); });
|
||||
}
|
||||
}
|
||||
}
|
||||
auto ma = new MainAdaptor(&root, dbusRootNode);
|
||||
connection.registerObject("/", &root);
|
||||
|
||||
|
||||
if (!connection.registerService("org.tuxclocker")) {
|
||||
qDebug() << "unable to register:" << connection.lastError().message();
|
||||
qDebug() << "errcode" << connection.lastError().type();
|
||||
}
|
||||
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user