mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-22 09:54:54 -05:00
The "core" macro conditions and actions have been extracted out to the "base" plugin. The library now mostly contains functionality which is required across all plugins and (e.g. definitions for macro segments). The goal is to reduce the complexity and cross-dependencies and group the source files in a better way. This should relsove the "library limit of 65535 objects exceeded" build issue occuring in some Windows build environments.
732 lines
19 KiB
C++
732 lines
19 KiB
C++
#include "osc-helpers.hpp"
|
|
#include "obs-module-helper.hpp"
|
|
#include "utility.hpp"
|
|
|
|
#include <string.h>
|
|
#include <QGroupBox>
|
|
|
|
#ifdef WIN32
|
|
#include <winsock.h>
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
namespace advss {
|
|
|
|
std::unordered_map<size_t, OSCMessageElement::TypeInfo>
|
|
OSCMessageElement::_typeNames = {
|
|
{std::variant_npos,
|
|
{"AdvSceneSwitcher.osc.message.type.none", "-"}},
|
|
{0, {"AdvSceneSwitcher.osc.message.type.int", "i"}},
|
|
{1, {"AdvSceneSwitcher.osc.message.type.float", "f"}},
|
|
{2, {"AdvSceneSwitcher.osc.message.type.string", "s"}},
|
|
{3, {"AdvSceneSwitcher.osc.message.type.binaryBlob", "b"}},
|
|
{4, {"AdvSceneSwitcher.osc.message.type.true", "T"}},
|
|
{5, {"AdvSceneSwitcher.osc.message.type.false", "F"}},
|
|
{6, {"AdvSceneSwitcher.osc.message.type.infinity", "I"}},
|
|
{7, {"AdvSceneSwitcher.osc.message.type.null", "N"}},
|
|
};
|
|
|
|
// Based on https://github.com/mhroth/tinyosc
|
|
struct FillMessageElementBufferVisitor {
|
|
std::vector<char> &buffer;
|
|
uint32_t &curOffset;
|
|
|
|
bool success = false;
|
|
|
|
void operator()(const StringVariable &value)
|
|
{
|
|
std::string string = value;
|
|
int length = (int)strlen(string.c_str());
|
|
if (curOffset + length >= buffer.size()) {
|
|
buffer.resize(curOffset + length + 1);
|
|
}
|
|
strncpy(buffer.data() + curOffset, string.c_str(),
|
|
buffer.size() - curOffset - length);
|
|
curOffset = (curOffset + 4 + length) & ~0x3;
|
|
success = true;
|
|
}
|
|
void operator()(const IntVariable &value)
|
|
{
|
|
if (curOffset + 4 > buffer.size()) {
|
|
buffer.resize(curOffset + 4);
|
|
}
|
|
int32_t k = value;
|
|
*((uint32_t *)(buffer.data() + curOffset)) = htonl(k);
|
|
curOffset += 4;
|
|
success = true;
|
|
}
|
|
void operator()(const DoubleVariable &value)
|
|
{
|
|
if (curOffset + 4 > buffer.size()) {
|
|
buffer.resize(curOffset + 4);
|
|
}
|
|
const float f = value;
|
|
*((uint32_t *)(buffer.data() + curOffset)) =
|
|
htonl(*((uint32_t *)&f));
|
|
curOffset += 4;
|
|
success = true;
|
|
}
|
|
void operator()(const OSCBlob &value)
|
|
{
|
|
if (curOffset + 4 > buffer.size()) {
|
|
buffer.resize(curOffset + 4);
|
|
}
|
|
auto blob = value.GetBinary();
|
|
if (!blob.has_value()) {
|
|
return;
|
|
}
|
|
if (curOffset + 4 + blob->size() > buffer.size()) {
|
|
buffer.resize(curOffset + 4 + blob->size());
|
|
}
|
|
*((uint32_t *)(buffer.data() + curOffset)) =
|
|
htonl(blob->size());
|
|
curOffset += 4;
|
|
memcpy(buffer.data() + curOffset, blob->data(), blob->size());
|
|
curOffset = (curOffset + 3 + blob->size()) & ~0x3;
|
|
success = true;
|
|
}
|
|
void operator()(const OSCTrue &) { success = true; }
|
|
void operator()(const OSCFalse &) { success = true; }
|
|
void operator()(const OSCInfinity &) { success = true; }
|
|
void operator()(const OSCNull &) { success = true; }
|
|
};
|
|
|
|
OSCBlob::OSCBlob(const std::string &stringRepresentation)
|
|
: _stringRep(stringRepresentation)
|
|
{
|
|
}
|
|
|
|
void OSCBlob::SetStringRepresentation(const StringVariable &s)
|
|
{
|
|
_stringRep = s;
|
|
}
|
|
|
|
std::string OSCBlob::GetStringRepresentation() const
|
|
{
|
|
return _stringRep;
|
|
}
|
|
|
|
std::optional<std::vector<char>> OSCBlob::GetBinary() const
|
|
{
|
|
std::vector<char> bytes;
|
|
std::string hexString = _stringRep;
|
|
|
|
for (std::size_t i = 2; i < hexString.size(); i += 4) {
|
|
try {
|
|
auto byteString = hexString.substr(i, 2);
|
|
int value = std::stoi(byteString, nullptr, 16);
|
|
bytes.push_back(static_cast<char>(value));
|
|
} catch (const std::exception &e) {
|
|
blog(LOG_WARNING,
|
|
"failed to convert hex \"%s\" to binary: %s",
|
|
hexString.c_str(), e.what());
|
|
return {};
|
|
}
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
void OSCBlob::Save(obs_data_t *obj, const char *name) const
|
|
{
|
|
_stringRep.Save(obj, name);
|
|
}
|
|
|
|
void OSCBlob::Load(obs_data_t *obj, const char *name)
|
|
{
|
|
_stringRep.Load(obj, name);
|
|
}
|
|
|
|
void OSCTrue::Save(obs_data_t *obj, const char *name) const
|
|
{
|
|
obs_data_set_bool(obj, name, true);
|
|
}
|
|
|
|
void OSCFalse::Save(obs_data_t *obj, const char *name) const
|
|
{
|
|
obs_data_set_bool(obj, name, true);
|
|
}
|
|
|
|
void OSCInfinity::Save(obs_data_t *obj, const char *name) const
|
|
{
|
|
obs_data_set_bool(obj, name, true);
|
|
}
|
|
|
|
void OSCNull::Save(obs_data_t *obj, const char *name) const
|
|
{
|
|
obs_data_set_bool(obj, name, true);
|
|
}
|
|
|
|
std::optional<std::vector<char>> OSCMessage::GetBuffer() const
|
|
{
|
|
if (std::string(_address).empty()) {
|
|
return {};
|
|
}
|
|
|
|
std::vector<char> buffer(128, 0);
|
|
uint32_t currentOffset = (uint32_t)strlen(_address.c_str());
|
|
if (currentOffset > buffer.size()) {
|
|
buffer.resize(buffer.size() * 2);
|
|
}
|
|
strncpy(buffer.data(), _address.c_str(), buffer.size());
|
|
currentOffset = (currentOffset + 4) & ~0x3;
|
|
|
|
std::string typeTags;
|
|
for (const auto &e : _elements) {
|
|
typeTags += e.GetTypeTag();
|
|
}
|
|
|
|
buffer.at(currentOffset++) = ',';
|
|
int length = (int)strlen(typeTags.c_str());
|
|
if (currentOffset + length >= buffer.size()) {
|
|
buffer.resize(buffer.size() * 2);
|
|
}
|
|
strncpy(buffer.data() + currentOffset, typeTags.c_str(),
|
|
buffer.size() - currentOffset - length);
|
|
currentOffset = (currentOffset + 4 + length) & ~0x3;
|
|
|
|
for (const auto &e : _elements) {
|
|
const auto &value = e._value;
|
|
FillMessageElementBufferVisitor visitor{buffer, currentOffset};
|
|
std::visit(visitor, value);
|
|
if (!visitor.success) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
buffer.resize(currentOffset);
|
|
return buffer;
|
|
}
|
|
|
|
const char *OSCMessageElement::GetTypeTag() const
|
|
{
|
|
return GetTypeTag(*this);
|
|
}
|
|
|
|
const char *OSCMessageElement::GetTypeName() const
|
|
{
|
|
return GetTypeName(*this);
|
|
}
|
|
|
|
const char *OSCMessageElement::GetTypeTag(const OSCMessageElement &element)
|
|
{
|
|
return _typeNames.at(element._value.index()).tag;
|
|
}
|
|
|
|
const char *OSCMessageElement::GetTypeName(const OSCMessageElement &element)
|
|
{
|
|
return obs_module_text(
|
|
_typeNames.at(element._value.index()).localizedName);
|
|
}
|
|
|
|
void OSCMessageElement::Save(obs_data_t *obj) const
|
|
{
|
|
std::visit(
|
|
[obj](auto &&arg) {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, StringVariable>) {
|
|
arg.Save(obj, "strValue");
|
|
} else if constexpr (std::is_same_v<T, IntVariable>) {
|
|
arg.Save(obj, "intValue");
|
|
} else if constexpr (std::is_same_v<T, DoubleVariable>) {
|
|
arg.Save(obj, "floatValue");
|
|
} else if constexpr (std::is_same_v<T, OSCBlob>) {
|
|
arg.Save(obj, "binaryValue");
|
|
} else if constexpr (std::is_same_v<T, OSCTrue>) {
|
|
arg.Save(obj, "trueValue");
|
|
} else if constexpr (std::is_same_v<T, OSCFalse>) {
|
|
arg.Save(obj, "falseValue");
|
|
} else if constexpr (std::is_same_v<T, OSCInfinity>) {
|
|
arg.Save(obj, "infiniteValue");
|
|
} else if constexpr (std::is_same_v<T, OSCNull>) {
|
|
arg.Save(obj, "nullValue");
|
|
} else {
|
|
blog(LOG_WARNING,
|
|
"cannot save unknown OSCMessageElement");
|
|
}
|
|
},
|
|
_value);
|
|
}
|
|
|
|
void OSCMessageElement::Load(obs_data_t *obj)
|
|
{
|
|
if (obs_data_has_user_value(obj, "strValue")) {
|
|
StringVariable string;
|
|
string.Load(obj, "strValue");
|
|
_value = string;
|
|
} else if (obs_data_has_user_value(obj, "intValue")) {
|
|
NumberVariable<int> intValue;
|
|
intValue.Load(obj, "intValue");
|
|
_value = intValue;
|
|
} else if (obs_data_has_user_value(obj, "floatValue")) {
|
|
NumberVariable<double> floatValue;
|
|
floatValue.Load(obj, "floatValue");
|
|
_value = floatValue;
|
|
} else if (obs_data_has_user_value(obj, "binaryValue")) {
|
|
OSCBlob binValue;
|
|
binValue.Load(obj, "binaryValue");
|
|
_value = binValue;
|
|
} else if (obs_data_has_user_value(obj, "trueValue")) {
|
|
_value = OSCTrue();
|
|
} else if (obs_data_has_user_value(obj, "falseValue")) {
|
|
_value = OSCFalse();
|
|
} else if (obs_data_has_user_value(obj, "OSCInfinite")) {
|
|
_value = OSCInfinity();
|
|
} else if (obs_data_has_user_value(obj, "nullValue")) {
|
|
_value = OSCNull();
|
|
} else {
|
|
blog(LOG_WARNING, "cannot load unknown OSCMessageElement");
|
|
}
|
|
}
|
|
|
|
std::string OSCMessageElement::ToString() const
|
|
{
|
|
return std::visit(
|
|
[](auto &&arg) -> std::string {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, StringVariable>) {
|
|
return arg;
|
|
} else if constexpr (std::is_same_v<T, OSCBlob> ||
|
|
std::is_same_v<T, OSCTrue> ||
|
|
std::is_same_v<T, OSCFalse> ||
|
|
std::is_same_v<T, OSCInfinity> ||
|
|
std::is_same_v<T, OSCNull>) {
|
|
return arg.GetStringRepresentation();
|
|
} else {
|
|
return std::to_string(arg.GetValue());
|
|
}
|
|
},
|
|
_value);
|
|
}
|
|
|
|
void OSCMessage::Save(obs_data_t *obj) const
|
|
{
|
|
auto data = obs_data_create();
|
|
_address.Save(data, "address");
|
|
auto elements = obs_data_array_create();
|
|
for (const auto &e : _elements) {
|
|
auto array_obj = obs_data_create();
|
|
e.Save(array_obj);
|
|
obs_data_array_push_back(elements, array_obj);
|
|
obs_data_release(array_obj);
|
|
}
|
|
obs_data_set_array(data, "elements", elements);
|
|
obs_data_set_obj(obj, "oscMessage", data);
|
|
obs_data_array_release(elements);
|
|
obs_data_release(data);
|
|
}
|
|
|
|
void OSCMessage::Load(obs_data_t *obj)
|
|
{
|
|
|
|
auto data = obs_data_get_obj(obj, "oscMessage");
|
|
_address.Load(data, "address");
|
|
_elements.clear();
|
|
auto elements = obs_data_get_array(data, "elements");
|
|
size_t count = obs_data_array_count(elements);
|
|
for (size_t i = 0; i < count; i++) {
|
|
auto array_obj = obs_data_array_item(elements, i);
|
|
OSCMessageElement e;
|
|
e.Load(array_obj);
|
|
_elements.push_back(e);
|
|
obs_data_release(array_obj);
|
|
}
|
|
obs_data_array_release(elements);
|
|
obs_data_release(data);
|
|
}
|
|
|
|
std::string OSCMessage::ToString() const
|
|
{
|
|
std::string res = "address: " + std::string(_address) + " message: ";
|
|
for (const auto &e : _elements) {
|
|
res += "[" + e.ToString() + "]";
|
|
}
|
|
return res;
|
|
}
|
|
|
|
OSCMessageElementEdit::OSCMessageElementEdit(QWidget *parent)
|
|
: QWidget(parent),
|
|
_type(new QComboBox(this)),
|
|
_intValue(new VariableSpinBox(this)),
|
|
_doubleValue(new VariableDoubleSpinBox(this)),
|
|
_text(new VariableLineEdit(this)),
|
|
_binaryText(new VariableLineEdit(this))
|
|
{
|
|
installEventFilter(this);
|
|
|
|
_intValue->setMinimum(INT_MIN);
|
|
_intValue->setMaximum(INT_MAX);
|
|
_doubleValue->setMinimum(-9999999999);
|
|
_doubleValue->setMaximum(9999999999);
|
|
_doubleValue->setDecimals(10);
|
|
|
|
_intValue->hide();
|
|
_doubleValue->hide();
|
|
_text->hide();
|
|
_binaryText->hide();
|
|
|
|
for (size_t i = 0; i < OSCMessageElement::_typeNames.size() - 1; i++) {
|
|
_type->addItem(obs_module_text(
|
|
OSCMessageElement::_typeNames.at(i).localizedName));
|
|
}
|
|
_type->setCurrentIndex(0);
|
|
|
|
QWidget::connect(_type, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(TypeChanged(int)));
|
|
QWidget::connect(
|
|
_doubleValue,
|
|
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
|
this, SLOT(DoubleChanged(const NumberVariable<double> &)));
|
|
QWidget::connect(
|
|
_intValue,
|
|
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
|
this, SLOT(IntChanged(const NumberVariable<int> &)));
|
|
QWidget::connect(_text, SIGNAL(editingFinished()), this,
|
|
SLOT(TextChanged()));
|
|
QWidget::connect(_binaryText, SIGNAL(editingFinished()), this,
|
|
SLOT(BinaryTextChanged()));
|
|
|
|
auto layout = new QHBoxLayout();
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->addWidget(_type, 1);
|
|
layout->addWidget(_intValue, 4);
|
|
layout->addWidget(_doubleValue, 4);
|
|
layout->addWidget(_text, 4);
|
|
layout->addWidget(_binaryText, 4);
|
|
setLayout(layout);
|
|
}
|
|
|
|
void OSCMessageElementEdit::SetMessageElement(const OSCMessageElement &element)
|
|
{
|
|
const QSignalBlocker b(this);
|
|
_type->setCurrentText(element.GetTypeName());
|
|
SetVisibility(element);
|
|
|
|
if (std::holds_alternative<StringVariable>(element._value)) {
|
|
_text->setText(std::get<StringVariable>(element._value));
|
|
} else if (std::holds_alternative<IntVariable>(element._value)) {
|
|
_intValue->SetValue(std::get<IntVariable>(element._value));
|
|
} else if (std::holds_alternative<DoubleVariable>(element._value)) {
|
|
_doubleValue->SetValue(
|
|
std::get<DoubleVariable>(element._value));
|
|
} else if (std::holds_alternative<OSCBlob>(element._value)) {
|
|
_binaryText->setText(std::get<OSCBlob>(element._value)
|
|
.GetStringRepresentation());
|
|
}
|
|
}
|
|
|
|
bool OSCMessageElementEdit::eventFilter(QObject *obj, QEvent *event)
|
|
{
|
|
if (event->type() == QEvent::MouseButtonPress ||
|
|
event->type() == QEvent::MouseButtonDblClick) {
|
|
emit Focussed();
|
|
}
|
|
|
|
return QWidget::eventFilter(obj, event);
|
|
}
|
|
void OSCMessageElementEdit::showEvent(QShowEvent *event)
|
|
{
|
|
QWidget::showEvent(event);
|
|
|
|
QWidgetList childWidgets = findChildren<QWidget *>();
|
|
for (QWidget *childWidget : childWidgets) {
|
|
childWidget->installEventFilter(this);
|
|
}
|
|
}
|
|
|
|
void OSCMessageElementEdit::DoubleChanged(const DoubleVariable &value)
|
|
{
|
|
emit ElementValueChanged(OSCMessageElement(value));
|
|
}
|
|
|
|
void OSCMessageElementEdit::IntChanged(const IntVariable &value)
|
|
{
|
|
emit ElementValueChanged(OSCMessageElement(value));
|
|
}
|
|
|
|
void OSCMessageElementEdit::TextChanged()
|
|
{
|
|
emit ElementValueChanged(StringVariable(_text->text().toStdString()));
|
|
}
|
|
|
|
void OSCMessageElementEdit::BinaryTextChanged()
|
|
{
|
|
emit ElementValueChanged(OSCBlob(_binaryText->text().toStdString()));
|
|
}
|
|
|
|
void OSCMessageElementEdit::SetVisibility(const OSCMessageElement &element)
|
|
{
|
|
_intValue->hide();
|
|
_doubleValue->hide();
|
|
_text->hide();
|
|
_binaryText->hide();
|
|
|
|
if (std::holds_alternative<StringVariable>(element._value)) {
|
|
_text->show();
|
|
} else if (std::holds_alternative<IntVariable>(element._value)) {
|
|
_intValue->show();
|
|
} else if (std::holds_alternative<DoubleVariable>(element._value)) {
|
|
_doubleValue->show();
|
|
} else if (std::holds_alternative<OSCBlob>(element._value)) {
|
|
_binaryText->show();
|
|
}
|
|
}
|
|
|
|
void OSCMessageElementEdit::TypeChanged(int idx)
|
|
{
|
|
OSCMessageElement element;
|
|
if (idx == 0) {
|
|
element = OSCMessageElement(IntVariable(0));
|
|
} else if (idx == 1) {
|
|
element = OSCMessageElement(DoubleVariable(0.0));
|
|
} else if (idx == 2) {
|
|
element = OSCMessageElement("value");
|
|
} else if (idx == 3) {
|
|
element = OSCMessageElement(OSCBlob("\\x00\\x01\\x02\\x03"));
|
|
} else if (idx == 4) {
|
|
element = OSCMessageElement(OSCTrue());
|
|
} else if (idx == 5) {
|
|
element = OSCMessageElement(OSCFalse());
|
|
} else if (idx == 6) {
|
|
element = OSCMessageElement(OSCInfinity());
|
|
} else if (idx == 7) {
|
|
element = OSCMessageElement(OSCNull());
|
|
}
|
|
SetVisibility(element);
|
|
SetMessageElement(element);
|
|
emit ElementValueChanged(element);
|
|
}
|
|
|
|
OSCMessageEdit::OSCMessageEdit(QWidget *parent)
|
|
: QWidget(parent),
|
|
_address(new VariableLineEdit(this)),
|
|
_elements(new QListWidget()),
|
|
_add(new QPushButton()),
|
|
_remove(new QPushButton()),
|
|
_up(new QPushButton()),
|
|
_down(new QPushButton())
|
|
{
|
|
_elements->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
_elements->setAutoScroll(false);
|
|
|
|
_add->setMaximumWidth(22);
|
|
_add->setProperty("themeID",
|
|
QVariant(QString::fromUtf8("addIconSmall")));
|
|
_add->setFlat(true);
|
|
_remove->setMaximumWidth(22);
|
|
_remove->setProperty("themeID",
|
|
QVariant(QString::fromUtf8("removeIconSmall")));
|
|
_remove->setFlat(true);
|
|
_up->setMaximumWidth(22);
|
|
_up->setProperty("themeID",
|
|
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
|
_up->setFlat(true);
|
|
_down->setMaximumWidth(22);
|
|
_down->setProperty("themeID",
|
|
QVariant(QString::fromUtf8("downArrowIconSmall")));
|
|
_down->setFlat(true);
|
|
|
|
QWidget::connect(_address, SIGNAL(editingFinished()), this,
|
|
SLOT(AddressChanged()));
|
|
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
|
|
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(Remove()));
|
|
QWidget::connect(_up, SIGNAL(clicked()), this, SLOT(Up()));
|
|
QWidget::connect(_down, SIGNAL(clicked()), this, SLOT(Down()));
|
|
|
|
auto controlsLayout = new QHBoxLayout();
|
|
controlsLayout->addWidget(_add);
|
|
controlsLayout->addWidget(_remove);
|
|
QFrame *line = new QFrame();
|
|
line->setFrameShape(QFrame::VLine);
|
|
line->setFrameShadow(QFrame::Sunken);
|
|
controlsLayout->addWidget(line);
|
|
controlsLayout->addWidget(_up);
|
|
controlsLayout->addWidget(_down);
|
|
controlsLayout->addStretch();
|
|
|
|
auto layout = new QVBoxLayout;
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->addWidget(_address);
|
|
layout->addWidget(_elements);
|
|
layout->addLayout(controlsLayout);
|
|
setLayout(layout);
|
|
}
|
|
|
|
void OSCMessageEdit::InsertElement(const OSCMessageElement &element)
|
|
{
|
|
auto item = new QListWidgetItem(_elements);
|
|
_elements->addItem(item);
|
|
auto elementEdit = new OSCMessageElementEdit(this);
|
|
elementEdit->SetMessageElement(element);
|
|
item->setSizeHint(elementEdit->minimumSizeHint());
|
|
_elements->setItemWidget(item, elementEdit);
|
|
QWidget::connect(elementEdit,
|
|
SIGNAL(ElementValueChanged(const OSCMessageElement &)),
|
|
this,
|
|
SLOT(ElementValueChanged(const OSCMessageElement &)));
|
|
QWidget::connect(elementEdit, SIGNAL(Focussed()), this,
|
|
SLOT(ElementFocussed()));
|
|
_currentSelection._elements.push_back(element);
|
|
}
|
|
|
|
void OSCMessageEdit::SetMessage(const OSCMessage &message)
|
|
{
|
|
_address->setText(message._address);
|
|
for (const auto &element : message._elements) {
|
|
InsertElement(element);
|
|
}
|
|
_currentSelection = message;
|
|
SetWidgetSize();
|
|
}
|
|
|
|
void OSCMessageEdit::AddressChanged()
|
|
{
|
|
_currentSelection._address = _address->text().toStdString();
|
|
emit MessageChanged(_currentSelection);
|
|
}
|
|
|
|
void OSCMessageEdit::Add()
|
|
{
|
|
OSCMessageElement element;
|
|
InsertElement(element);
|
|
emit MessageChanged(_currentSelection);
|
|
SetWidgetSize();
|
|
}
|
|
|
|
void OSCMessageEdit::Remove()
|
|
{
|
|
auto item = _elements->currentItem();
|
|
int idx = _elements->currentRow();
|
|
if (!item || idx == -1) {
|
|
return;
|
|
}
|
|
delete item;
|
|
_currentSelection._elements.erase(_currentSelection._elements.begin() +
|
|
idx);
|
|
emit MessageChanged(_currentSelection);
|
|
SetWidgetSize();
|
|
}
|
|
|
|
static bool moveUp(QListWidget *list)
|
|
{
|
|
int index = list->currentRow();
|
|
if (index == -1 || index == 0) {
|
|
return false;
|
|
}
|
|
|
|
QWidget *row = list->itemWidget(list->currentItem());
|
|
QListWidgetItem *itemN = list->currentItem()->clone();
|
|
|
|
list->insertItem(index - 1, itemN);
|
|
list->setItemWidget(itemN, row);
|
|
|
|
list->takeItem(index + 1);
|
|
list->setCurrentRow(index - 1);
|
|
return true;
|
|
}
|
|
|
|
void OSCMessageEdit::Up()
|
|
{
|
|
int idx = _elements->currentRow();
|
|
if (!moveUp(_elements)) {
|
|
return;
|
|
}
|
|
|
|
iter_swap(_currentSelection._elements.begin() + idx,
|
|
_currentSelection._elements.begin() + idx - 1);
|
|
|
|
emit MessageChanged(_currentSelection);
|
|
SetWidgetSize();
|
|
}
|
|
|
|
static bool moveDown(QListWidget *list)
|
|
{
|
|
int index = list->currentRow();
|
|
if (index == -1 || index == list->count() - 1) {
|
|
return false;
|
|
}
|
|
|
|
QWidget *row = list->itemWidget(list->currentItem());
|
|
QListWidgetItem *itemN = list->currentItem()->clone();
|
|
|
|
list->insertItem(index + 2, itemN);
|
|
list->setItemWidget(itemN, row);
|
|
|
|
list->takeItem(index);
|
|
list->setCurrentRow(index + 1);
|
|
return true;
|
|
}
|
|
|
|
void OSCMessageEdit::Down()
|
|
{
|
|
int idx = _elements->currentRow();
|
|
if (!moveDown(_elements)) {
|
|
return;
|
|
}
|
|
|
|
iter_swap(_currentSelection._elements.begin() + idx,
|
|
_currentSelection._elements.begin() + idx + 1);
|
|
|
|
emit MessageChanged(_currentSelection);
|
|
SetWidgetSize();
|
|
}
|
|
|
|
static QListWidgetItem *getItemFromWidget(QListWidget *list, QWidget *widget)
|
|
{
|
|
for (int i = 0; i < list->count(); i++) {
|
|
auto item = list->item(i);
|
|
if (!item) {
|
|
continue;
|
|
}
|
|
auto itemWidget = list->itemWidget(item);
|
|
if (itemWidget == widget) {
|
|
return item;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int OSCMessageEdit::GetIndexOfSignal()
|
|
{
|
|
auto sender = this->sender();
|
|
if (!sender) {
|
|
return -1;
|
|
}
|
|
auto widget = qobject_cast<QWidget *>(sender);
|
|
if (!widget) {
|
|
return -1;
|
|
}
|
|
return _elements->row(getItemFromWidget(_elements, widget));
|
|
}
|
|
|
|
void OSCMessageEdit::ElementFocussed()
|
|
{
|
|
int idx = GetIndexOfSignal();
|
|
if (idx == -1) {
|
|
return;
|
|
}
|
|
_elements->setCurrentRow(idx);
|
|
}
|
|
|
|
void OSCMessageEdit::ElementValueChanged(const OSCMessageElement &element)
|
|
{
|
|
int idx = GetIndexOfSignal();
|
|
if (idx == -1) {
|
|
return;
|
|
}
|
|
|
|
_currentSelection._elements.at(idx) = element;
|
|
_elements->setCurrentRow(idx);
|
|
emit MessageChanged(_currentSelection);
|
|
}
|
|
|
|
void OSCMessageEdit::SetWidgetSize()
|
|
{
|
|
SetHeightToContentHeight(_elements);
|
|
adjustSize();
|
|
updateGeometry();
|
|
}
|
|
|
|
} // namespace advss
|