The complete source code of IdealIRC
http://www.idealirc.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1151 lines
35 KiB
1151 lines
35 KiB
/*
|
|
* IdealIRC - Internet Relay Chat client
|
|
* Copyright (C) 2021 Tom-Andre Barstad.
|
|
* This software is licensed under the Software Attribution License.
|
|
* See LICENSE for more information.
|
|
*/
|
|
|
|
#include "ScriptDialog.h"
|
|
#include "Script/Dialog.h"
|
|
#include "Script/ScriptException.h"
|
|
#include "Script/ValueExtract.h"
|
|
#include "Script/Builtin/ListUtils.h" // Needed for LIST_END_MAGIC
|
|
|
|
#include "IdealIRC.h"
|
|
|
|
#include <QDialog>
|
|
#include <QLabel>
|
|
#include <QPushButton>
|
|
#include <QLineEdit>
|
|
#include <QPlainTextEdit>
|
|
#include <QSpinBox>
|
|
#include <QListWidget>
|
|
#include <QComboBox>
|
|
#include <QTableWidget>
|
|
#include <QHeaderView>
|
|
#include <QTreeWidget>
|
|
#include <QGroupBox>
|
|
#include <QRadioButton>
|
|
#include <QCheckBox>
|
|
#include <QScrollBar>
|
|
#include <unordered_map>
|
|
#include <string>
|
|
#include <stdexcept>
|
|
|
|
extern IdealIRC* IIRC_MainWindow_Ptr;
|
|
|
|
struct ScriptWidget
|
|
{
|
|
explicit ScriptWidget(Script& script_) : script(script_) {}
|
|
|
|
std::unordered_map<std::string, ValueHolder> customAttrs;
|
|
QWidget* widget{ nullptr }; // Pointer owned by the QDialog
|
|
const DialogWidget* tmpl{ nullptr };
|
|
Script& script;
|
|
};
|
|
|
|
struct ScriptDialogPriv
|
|
{
|
|
explicit ScriptDialogPriv(Script& script_)
|
|
: dialog(new QDialog(nullptr))
|
|
, script(script_)
|
|
{
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose, false);
|
|
}
|
|
|
|
std::unordered_map<std::string, ValueHolder> customAttrs;
|
|
std::unordered_map<std::string, ScriptWidget> widgets;
|
|
std::unique_ptr<QDialog> dialog;
|
|
const Dialog* tmpl{ nullptr };
|
|
Script& script;
|
|
};
|
|
|
|
namespace {
|
|
|
|
template<DialogWidget::Type type, typename T>
|
|
void buildWidget(T* widget, const DialogWidget& tmpl)
|
|
{
|
|
ValueHolder valX = tmpl.defaultAttr("x");
|
|
ValueHolder valY = tmpl.defaultAttr("y");
|
|
ValueHolder valWidth = tmpl.defaultAttr("width");
|
|
ValueHolder valHeight = tmpl.defaultAttr("height");
|
|
ValueHolder valFont = tmpl.defaultAttr("font");
|
|
ValueHolder valFontsize = tmpl.defaultAttr("fontsize");
|
|
ValueHolder valEnabled = tmpl.defaultAttr("enabled");
|
|
ValueHolder valVisible = tmpl.defaultAttr("visible");
|
|
ValueHolder valText = tmpl.defaultAttr("text");
|
|
ValueHolder valTooltip = tmpl.defaultAttr("tooltip");
|
|
|
|
auto rect = widget->geometry();
|
|
|
|
if (valX.getType() != ValueHolder::Type::Void)
|
|
rect.setX(ValueExtract(valX).toInt());
|
|
if (valY.getType() != ValueHolder::Type::Void)
|
|
rect.setY(ValueExtract(valY).toInt());
|
|
if (valWidth.getType() != ValueHolder::Type::Void)
|
|
rect.setWidth(ValueExtract(valWidth).toInt());
|
|
if (valHeight.getType() != ValueHolder::Type::Void)
|
|
rect.setHeight(ValueExtract(valHeight).toInt());
|
|
|
|
widget->setGeometry(rect);
|
|
|
|
auto font = widget->font();
|
|
if (valFont.getType() != ValueHolder::Type::Void)
|
|
font.setFamily(ValueExtract(valFont).toString().c_str());
|
|
|
|
if (valFontsize.getType() != ValueHolder::Type::Void)
|
|
font.setPixelSize(ValueExtract(valFontsize).toInt());
|
|
|
|
widget->setFont(font);
|
|
|
|
if (valEnabled.getType() != ValueHolder::Type::Void)
|
|
widget->setEnabled(ValueExtract(valEnabled).toBool());
|
|
|
|
if (valVisible.getType() != ValueHolder::Type::Void)
|
|
widget->setVisible(ValueExtract(valVisible).toBool());
|
|
|
|
if (valTooltip.getType() != ValueHolder::Type::Void)
|
|
widget->setToolTip(ValueExtract(valHeight).toString().c_str());
|
|
|
|
|
|
using WType = DialogWidget::Type;
|
|
if constexpr (type == WType::Spinbox) {
|
|
ValueHolder value = tmpl.defaultAttr("value");
|
|
ValueHolder min = tmpl.defaultAttr("min");
|
|
ValueHolder max = tmpl.defaultAttr("max");
|
|
ValueHolder step = tmpl.defaultAttr("step");
|
|
if (min.getType() == ValueHolder::Type::Void)
|
|
throw ScriptException("Attribute 'min' is required for Spinbox");
|
|
if (max.getType() == ValueHolder::Type::Void)
|
|
throw ScriptException("Attribute 'max' is required for Spinbox");
|
|
|
|
|
|
if (min.getType() != ValueHolder::Type::Integer)
|
|
throw ScriptException("Attribute 'min' for Spinbox must be a whole number");
|
|
if (max.getType() != ValueHolder::Type::Integer)
|
|
throw ScriptException("Attribute 'max' for Spinbox must be a whole number");
|
|
|
|
if (value.getType() == ValueHolder::Type::Void)
|
|
value = min;
|
|
|
|
if (step.getType() == ValueHolder::Type::Void)
|
|
step = 1;
|
|
|
|
if (value.getType() != ValueHolder::Type::Integer)
|
|
throw ScriptException("Attribute 'value' for Spinbox must be a whole number");
|
|
if (step.getType() != ValueHolder::Type::Integer)
|
|
throw ScriptException("Attribute 'step' for Spinbox must be a whole number");
|
|
|
|
widget->setMinimum(ValueExtract(min).toInt());
|
|
widget->setMaximum(ValueExtract(max).toInt());
|
|
widget->setValue(ValueExtract(value).toInt());
|
|
widget->setSingleStep(ValueExtract(step).toInt());
|
|
}
|
|
else if constexpr (type == WType::Textbox) {
|
|
ValueHolder readOnly = tmpl.defaultAttr("readonly");
|
|
if (readOnly.getType() != ValueHolder::Type::Void)
|
|
widget->setEnabled(ValueExtract(readOnly).toBool());
|
|
widget->setPlainText(ValueExtract(valText).toString().c_str());
|
|
}
|
|
else if constexpr (type == WType::Groupbox) {
|
|
ValueHolder readOnly = tmpl.defaultAttr("readonly");
|
|
if (readOnly.getType() != ValueHolder::Type::Void)
|
|
widget->setEnabled(ValueExtract(readOnly).toBool());
|
|
widget->setTitle(ValueExtract(valText).toString().c_str());
|
|
}
|
|
else if constexpr (type == WType::Button) {
|
|
ValueHolder checkable = tmpl.defaultAttr("checkable");
|
|
ValueHolder checked = tmpl.defaultAttr("checked");
|
|
if (checkable.getType() != ValueHolder::Type::Void)
|
|
widget->setCheckable(ValueExtract(checkable).toBool());
|
|
if (checked.getType() != ValueHolder::Type::Void)
|
|
widget->setChecked(ValueExtract(checked).toBool());
|
|
widget->setText(ValueExtract(valText).toString().c_str());
|
|
}
|
|
else if constexpr (type == WType::Checkbox) {
|
|
ValueHolder checked = tmpl.defaultAttr("checked");
|
|
if (checked.getType() != ValueHolder::Type::Void)
|
|
widget->setChecked(ValueExtract(checked).toBool());
|
|
widget->setText(ValueExtract(valText).toString().c_str());
|
|
}
|
|
else if constexpr (type == WType::Radio) {
|
|
ValueHolder checked = tmpl.defaultAttr("checked");
|
|
if (checked.getType() != ValueHolder::Type::Void)
|
|
widget->setChecked(ValueExtract(checked).toBool());
|
|
widget->setText(ValueExtract(valText).toString().c_str());
|
|
}
|
|
else if constexpr (type == WType::Table) {
|
|
|
|
}
|
|
else if constexpr (type != WType::Vscroll && type != WType::Hscroll
|
|
&& type != WType::Listbox && type != WType::Combobox
|
|
&& type != WType::Tree) {
|
|
widget->setText(ValueExtract(valText).toString().c_str());
|
|
}
|
|
}
|
|
|
|
void widgetSetText(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
std::string text = ValueExtract(valh).toString();
|
|
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Label) {
|
|
auto* cw = dynamic_cast<QLabel*>(widget.widget);
|
|
cw->setText(text.c_str());
|
|
}
|
|
else if (widget.tmpl->type() == WType::Groupbox) {
|
|
auto* cw = dynamic_cast<QGroupBox*>(widget.widget);
|
|
cw->setTitle(text.c_str());
|
|
}
|
|
else if (widget.tmpl->type() == WType::Textline) {
|
|
auto* cw = dynamic_cast<QLineEdit*>(widget.widget);
|
|
cw->setText(text.c_str());
|
|
}
|
|
else if (widget.tmpl->type() == WType::Textbox) {
|
|
auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget);
|
|
cw->setPlainText(text.c_str());
|
|
}
|
|
else if (widget.tmpl->type() == WType::Button) {
|
|
auto* cw = dynamic_cast<QPushButton*>(widget.widget);
|
|
cw->setText(text.c_str());
|
|
}
|
|
else if (widget.tmpl->type() == WType::Radio) {
|
|
auto* cw = dynamic_cast<QRadioButton*>(widget.widget);
|
|
cw->setText(text.c_str());
|
|
}
|
|
else if (widget.tmpl->type() == WType::Checkbox) {
|
|
auto* cw = dynamic_cast<QCheckBox*>(widget.widget);
|
|
cw->setText(text.c_str());
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("text", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetCheckable(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
bool value = ValueExtract(valh).toBool();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Button) {
|
|
auto* cw = dynamic_cast<QPushButton*>(widget.widget);
|
|
cw->setCheckable(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("checkable", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetChecked(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
bool value = ValueExtract(valh).toBool();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Button) {
|
|
auto* cw = dynamic_cast<QPushButton*>(widget.widget);
|
|
cw->setChecked(value);
|
|
}
|
|
else if (widget.tmpl->type() == WType::Radio) {
|
|
auto* cw = dynamic_cast<QRadioButton*>(widget.widget);
|
|
cw->setChecked(value);
|
|
}
|
|
else if (widget.tmpl->type() == WType::Checkbox) {
|
|
auto* cw = dynamic_cast<QCheckBox*>(widget.widget);
|
|
cw->setChecked(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("checked", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetReadOnly(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
bool value = ValueExtract(valh).toBool();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Textline) {
|
|
auto* cw = dynamic_cast<QLineEdit*>(widget.widget);
|
|
cw->setReadOnly(value);
|
|
}
|
|
else if (widget.tmpl->type() == WType::Textbox) {
|
|
auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget);
|
|
cw->setReadOnly(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("readonly", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetValue(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
int value = ValueExtract(valh).toInt();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
cw->setValue(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("value", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetMin(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
int value = ValueExtract(valh).toInt();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
cw->setMinimum(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("min", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetMax(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
int value = ValueExtract(valh).toInt();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
cw->setMaximum(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("max", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetStep(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
int value = ValueExtract(valh).toInt();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
cw->setSingleStep(value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("step", valh);
|
|
}
|
|
}
|
|
|
|
void widgetSetHeader(ScriptWidget& widget, ValueHolder& valh)
|
|
{
|
|
ValueArray& list = ValueExtract(valh).toMap();
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Table) {
|
|
auto* cw = dynamic_cast<QTableWidget*>(widget.widget);
|
|
QStringList header;
|
|
for (int i = 0; ; ++i) {
|
|
std::string si = std::to_string(i);
|
|
try {
|
|
ValueHolder& val = *list.at(si).get();
|
|
switch (val.getType()) {
|
|
case ValueHolder::Type::Integer:
|
|
header.push_back(QString::number(ValueExtract(val).toInt()));
|
|
break;
|
|
case ValueHolder::Type::Real:
|
|
header.push_back(QString::number(ValueExtract(val).toReal()));
|
|
break;
|
|
case ValueHolder::Type::Bool:
|
|
header.push_back(ValueExtract(val).toBool() ? QStringLiteral("True") : QStringLiteral("False"));
|
|
break;
|
|
case ValueHolder::Type::String:
|
|
header.push_back(ValueExtract(val).toString().c_str());
|
|
break;
|
|
default:
|
|
header.push_back(QStringLiteral(""));
|
|
}
|
|
} catch (const std::out_of_range&) {
|
|
break;
|
|
}
|
|
}
|
|
cw->setColumnCount(header.size());
|
|
cw->setHorizontalHeaderLabels(header);
|
|
cw->resizeColumnsToContents();
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign("step", valh);
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetText(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Label) {
|
|
auto* cw = dynamic_cast<QLabel*>(widget.widget);
|
|
return cw->text().toStdString();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Groupbox) {
|
|
auto* cw = dynamic_cast<QGroupBox*>(widget.widget);
|
|
return cw->title().toStdString();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Textline) {
|
|
auto* cw = dynamic_cast<QLineEdit*>(widget.widget);
|
|
return cw->text().toStdString();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Textbox) {
|
|
auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget);
|
|
return cw->toPlainText().toStdString();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Button) {
|
|
auto* cw = dynamic_cast<QPushButton*>(widget.widget);
|
|
return cw->text().toStdString();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Radio) {
|
|
auto* cw = dynamic_cast<QRadioButton*>(widget.widget);
|
|
return cw->text().toStdString();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Checkbox) {
|
|
auto* cw = dynamic_cast<QCheckBox*>(widget.widget);
|
|
return cw->text().toStdString();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("text");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetCheckable(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Button) {
|
|
auto* cw = dynamic_cast<QPushButton*>(widget.widget);
|
|
return cw->isCheckable();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("checkable");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetChecked(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Button) {
|
|
auto* cw = dynamic_cast<QPushButton*>(widget.widget);
|
|
return cw->isChecked();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Radio) {
|
|
auto* cw = dynamic_cast<QRadioButton*>(widget.widget);
|
|
return cw->isChecked();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Checkbox) {
|
|
auto* cw = dynamic_cast<QCheckBox*>(widget.widget);
|
|
return cw->isChecked();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("checked");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetReadOnly(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Textline) {
|
|
auto* cw = dynamic_cast<QLineEdit*>(widget.widget);
|
|
return cw->isReadOnly();
|
|
}
|
|
else if (widget.tmpl->type() == WType::Textbox) {
|
|
auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget);
|
|
return cw->isReadOnly();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("readonly");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetValue(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
return cw->value();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("value");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetMin(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
return cw->minimum();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("min");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetMax(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
return cw->maximum();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("max");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetStep(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Spinbox) {
|
|
auto* cw = dynamic_cast<QSpinBox*>(widget.widget);
|
|
return cw->singleStep();
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("step");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
ValueHolder widgetGetHeader(ScriptWidget& widget)
|
|
{
|
|
using WType = DialogWidget::Type;
|
|
if (widget.tmpl->type() == WType::Table) {
|
|
auto* cw = dynamic_cast<QTableWidget*>(widget.widget);
|
|
ValueArray header;
|
|
for (int i = 0; ; ++i) {
|
|
auto* item = cw->horizontalHeaderItem(i);
|
|
if (!item)
|
|
break;
|
|
header.emplace(std::to_string(i), new ValueHolder(item->text().toStdString()));
|
|
}
|
|
return ValueHolder(std::move(header));
|
|
}
|
|
else {
|
|
ValueHolder val;
|
|
try {
|
|
val = widget.customAttrs.at("header");
|
|
}
|
|
catch (...) {}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
ScriptDialog::ScriptDialog(const Dialog& dialog, Script& script, int handle)
|
|
: mp(new ScriptDialogPriv(script))
|
|
{
|
|
ValueHolder valX = dialog.defaultAttr("x");
|
|
ValueHolder valY = dialog.defaultAttr("y");
|
|
ValueHolder valWidth = dialog.defaultAttr("width");
|
|
ValueHolder valHeight = dialog.defaultAttr("height");
|
|
ValueHolder valTitle = dialog.defaultAttr("title");
|
|
ValueHolder valFont = dialog.defaultAttr("font");
|
|
ValueHolder valFontsize = dialog.defaultAttr("fontsize");
|
|
ValueHolder valIcon = dialog.defaultAttr("icon");
|
|
|
|
mp->tmpl = &dialog;
|
|
|
|
ValueHolder voidValue;
|
|
auto IIRC_rect = IIRC_MainWindow_Ptr->geometry();
|
|
|
|
if (valWidth != voidValue)
|
|
setAttr("width", valWidth);
|
|
if (valHeight != voidValue)
|
|
setAttr("height", valHeight);
|
|
|
|
if (valX != voidValue)
|
|
setAttr("x", valX);
|
|
else {
|
|
int width = ValueExtract(valWidth).toInt();
|
|
ValueHolder x = (2 * IIRC_rect.x() + IIRC_rect.width() - width) / 2;
|
|
setAttr("x", x);
|
|
}
|
|
|
|
if (valY != voidValue)
|
|
setAttr("y", valY);
|
|
else {
|
|
int height = ValueExtract(valHeight).toInt();
|
|
ValueHolder y = (2 * IIRC_rect.y() + IIRC_rect.height() - height) / 2;
|
|
setAttr("y", y);
|
|
}
|
|
|
|
if (valTitle != voidValue)
|
|
setAttr("title", valTitle);
|
|
else {
|
|
ValueHolder defaultTitle = std::string("Unnamed dialog");
|
|
setAttr("title", defaultTitle);
|
|
}
|
|
|
|
if (valFont != voidValue)
|
|
setAttr("font", valFont);
|
|
|
|
if (valFontsize != voidValue)
|
|
setAttr("fontsize", valFontsize);
|
|
|
|
if (valIcon != voidValue)
|
|
setAttr("icon", valIcon);
|
|
|
|
for (auto wref : dialog.allWidgets()) {
|
|
auto& widget = wref.get();
|
|
|
|
mp->widgets.emplace(widget.name(), ScriptWidget(script));
|
|
auto& qw = mp->widgets.at(widget.name());
|
|
qw.tmpl = &widget;
|
|
|
|
using WType = DialogWidget::Type;
|
|
switch (widget.type()) {
|
|
case WType::Label:
|
|
{
|
|
auto* w = new QLabel(mp->dialog.get());
|
|
buildWidget<WType::Label, QLabel>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Button:
|
|
{
|
|
auto* w = new QPushButton(mp->dialog.get());
|
|
buildWidget<WType::Button, QPushButton>(w, widget);
|
|
qw.widget = w;
|
|
|
|
const DialogWidget* widgetTemplate = qw.tmpl;
|
|
Script* scr = &mp->script;
|
|
connect(w, &QPushButton::clicked, [scr, widgetTemplate, handle]{
|
|
auto hook = widgetTemplate->hook("clicked");
|
|
if (!hook) return;
|
|
SymbolScope sym;
|
|
sym.set("handle", handle);
|
|
if (!scr->runScopeWithSymbols(*hook, sym))
|
|
ScriptManager::printError(scr->lastError());
|
|
});
|
|
|
|
break;
|
|
}
|
|
|
|
case WType::Textline:
|
|
{
|
|
auto* w = new QLineEdit(mp->dialog.get());
|
|
buildWidget<WType::Textline, QLineEdit>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Textbox:
|
|
{
|
|
auto* w = new QPlainTextEdit(mp->dialog.get());
|
|
buildWidget<WType::Textbox, QPlainTextEdit>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Spinbox:
|
|
{
|
|
auto* w = new QSpinBox(mp->dialog.get());
|
|
buildWidget<WType::Spinbox, QSpinBox>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Listbox:
|
|
{
|
|
auto* w = new QListWidget(mp->dialog.get());
|
|
buildWidget<WType::Listbox, QListWidget>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Combobox:
|
|
{
|
|
auto* w = new QComboBox(mp->dialog.get());
|
|
buildWidget<WType::Combobox, QComboBox>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Table:
|
|
{
|
|
auto* w = new QTableWidget(mp->dialog.get());
|
|
buildWidget<WType::Table, QTableWidget>(w, widget);
|
|
qw.widget = w;
|
|
w->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
w->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
break;
|
|
}
|
|
|
|
case WType::Tree:
|
|
{
|
|
auto* w = new QTreeWidget(mp->dialog.get());
|
|
buildWidget<WType::Tree, QTreeWidget>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Groupbox:
|
|
{
|
|
auto* w = new QGroupBox(mp->dialog.get());
|
|
buildWidget<WType::Groupbox, QGroupBox>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
// TODO case DialogWidget::Type::Tab:
|
|
// break;
|
|
case WType::Radio:
|
|
{
|
|
auto* w = new QRadioButton(mp->dialog.get());
|
|
buildWidget<WType::Radio, QRadioButton>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Checkbox:
|
|
{
|
|
auto* w = new QCheckBox(mp->dialog.get());
|
|
buildWidget<WType::Checkbox, QCheckBox>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Vscroll:
|
|
{
|
|
auto* w = new QScrollBar(mp->dialog.get());
|
|
buildWidget<WType::Vscroll, QScrollBar>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
case WType::Hscroll:
|
|
{
|
|
auto* w = new QScrollBar(mp->dialog.get());
|
|
buildWidget<WType::Hscroll, QScrollBar>(w, widget);
|
|
qw.widget = w;
|
|
break;
|
|
}
|
|
|
|
// TODO case DialogWidget::Type::Canvas:
|
|
// break;
|
|
}
|
|
}
|
|
|
|
show();
|
|
}
|
|
|
|
ScriptDialog::ScriptDialog(ScriptDialog&& other)
|
|
: mp(std::move(other.mp))
|
|
{}
|
|
|
|
ScriptDialog::~ScriptDialog() = default;
|
|
|
|
void ScriptDialog::setAttr(const std::string& key, ValueHolder& value)
|
|
{
|
|
if (key == "x") {
|
|
auto rect = mp->dialog->geometry();
|
|
rect.setX(ValueExtract(value).toInt());
|
|
mp->dialog->setGeometry(rect);
|
|
}
|
|
else if (key == "y") {
|
|
auto rect = mp->dialog->geometry();
|
|
rect.setY(ValueExtract(value).toInt());
|
|
mp->dialog->setGeometry(rect);
|
|
}
|
|
else if (key == "width") {
|
|
auto rect = mp->dialog->geometry();
|
|
int width = ValueExtract(value).toInt();
|
|
rect.setWidth(width);
|
|
mp->dialog->setGeometry(rect);
|
|
mp->dialog->setMinimumWidth(width);
|
|
mp->dialog->setMaximumWidth(width);
|
|
}
|
|
else if (key == "height") {
|
|
auto rect = mp->dialog->geometry();
|
|
int height = ValueExtract(value).toInt();
|
|
rect.setHeight(height);
|
|
mp->dialog->setGeometry(rect);
|
|
mp->dialog->setMinimumHeight(height);
|
|
mp->dialog->setMaximumHeight(height);
|
|
}
|
|
else if (key == "title") {
|
|
mp->dialog->setWindowTitle(ValueExtract(value).toString().c_str());
|
|
}
|
|
else if (key == "font") {
|
|
QFont font = mp->dialog->font();
|
|
font.setFamily(ValueExtract(value).toString().c_str());
|
|
mp->dialog->setFont(font);
|
|
}
|
|
else if (key == "fontsize") {
|
|
QFont font = mp->dialog->font();
|
|
font.setPixelSize(ValueExtract(value).toInt());
|
|
mp->dialog->setFont(font);
|
|
}
|
|
else if (key == "icon") {
|
|
mp->dialog->setWindowIcon(QIcon(ValueExtract(value).toString().c_str()));
|
|
}
|
|
else {
|
|
mp->customAttrs.insert_or_assign(key, value);
|
|
}
|
|
}
|
|
|
|
ValueHolder ScriptDialog::attr(const std::string& key) const
|
|
{
|
|
if (key == "x") {
|
|
auto rect = mp->dialog->geometry();
|
|
return rect.x();
|
|
}
|
|
else if (key == "y") {
|
|
auto rect = mp->dialog->geometry();
|
|
return rect.y();
|
|
}
|
|
else if (key == "width") {
|
|
auto rect = mp->dialog->geometry();
|
|
return rect.width();
|
|
}
|
|
else if (key == "height") {
|
|
auto rect = mp->dialog->geometry();
|
|
return rect.height();
|
|
}
|
|
else if (key == "title") {
|
|
return mp->dialog->windowTitle().toStdString();
|
|
}
|
|
else if (key == "font") {
|
|
QFont font = mp->dialog->font();
|
|
return font.family().toStdString();
|
|
}
|
|
else if (key == "fontsize") {
|
|
QFont font = mp->dialog->font();
|
|
return font.pixelSize();
|
|
}
|
|
else if (key == "icon") {
|
|
return mp->dialog->windowIcon().name().toStdString();
|
|
}
|
|
else {
|
|
auto iter = mp->customAttrs.find(key);
|
|
if (iter == mp->customAttrs.end())
|
|
return ValueHolder();
|
|
return iter->second;
|
|
}
|
|
}
|
|
|
|
void ScriptDialog::setWidgetAttr(const std::string& widgetName, const std::string& key, ValueHolder& value)
|
|
{
|
|
try {
|
|
ScriptWidget& widget = mp->widgets.at(widgetName);
|
|
if (key == "x") {
|
|
auto rect = widget.widget->geometry();
|
|
rect.setX(ValueExtract(value).toInt());
|
|
widget.widget->setGeometry(rect);
|
|
}
|
|
else if (key == "y") {
|
|
auto rect = widget.widget->geometry();
|
|
rect.setY(ValueExtract(value).toInt());
|
|
widget.widget->setGeometry(rect);
|
|
}
|
|
else if (key == "width") {
|
|
auto rect = widget.widget->geometry();
|
|
rect.setWidth(ValueExtract(value).toInt());
|
|
widget.widget->setGeometry(rect);
|
|
}
|
|
else if (key == "height") {
|
|
auto rect = widget.widget->geometry();
|
|
rect.setHeight(ValueExtract(value).toInt());
|
|
widget.widget->setGeometry(rect);
|
|
}
|
|
else if (key == "font") {
|
|
QFont font = widget.widget->font();
|
|
font.setFamily(ValueExtract(value).toString().c_str());
|
|
widget.widget->setFont(font);
|
|
}
|
|
else if (key == "fontsize") {
|
|
QFont font = widget.widget->font();
|
|
font.setPixelSize(ValueExtract(value).toInt());
|
|
widget.widget->setFont(font);
|
|
}
|
|
else if (key == "enabled") {
|
|
widget.widget->setEnabled(ValueExtract(value).toBool());
|
|
}
|
|
else if (key == "visible") {
|
|
widget.widget->setVisible(ValueExtract(value).toBool());
|
|
}
|
|
else if (key == "text") {
|
|
widgetSetText(widget, value);
|
|
}
|
|
else if (key == "checkable") {
|
|
widgetSetCheckable(widget, value);
|
|
}
|
|
else if (key == "checked") {
|
|
widgetSetChecked(widget, value);
|
|
}
|
|
else if (key == "readonly") {
|
|
widgetSetReadOnly(widget, value);
|
|
}
|
|
else if (key == "tooltip") {
|
|
widget.widget->setToolTip(ValueExtract(value).toString().c_str());
|
|
}
|
|
else if (key == "value") {
|
|
widgetSetValue(widget, value);
|
|
}
|
|
else if (key == "min") {
|
|
widgetSetMin(widget, value);
|
|
}
|
|
else if (key == "max") {
|
|
widgetSetMax(widget, value);
|
|
}
|
|
else if (key == "step") {
|
|
widgetSetStep(widget, value);
|
|
}
|
|
else if (key == "header") {
|
|
widgetSetHeader(widget, value);
|
|
}
|
|
else {
|
|
widget.customAttrs.insert_or_assign(key, value);
|
|
}
|
|
} catch (std::out_of_range&) {
|
|
throw ScriptException("Widget not found: " + widgetName);
|
|
}
|
|
}
|
|
|
|
ValueHolder ScriptDialog::widgetAttr(const std::string& widgetName, const std::string& key) const
|
|
{
|
|
try {
|
|
auto& widget = mp->widgets.at(widgetName);
|
|
if (key == "x") {
|
|
auto rect = widget.widget->geometry();
|
|
return rect.x();
|
|
}
|
|
else if (key == "y") {
|
|
auto rect = widget.widget->geometry();
|
|
return rect.y();
|
|
}
|
|
else if (key == "width") {
|
|
auto rect = widget.widget->geometry();
|
|
return rect.width();
|
|
}
|
|
else if (key == "height") {
|
|
auto rect = widget.widget->geometry();
|
|
return rect.height();
|
|
}
|
|
else if (key == "font") {
|
|
QFont font = widget.widget->font();
|
|
return font.family().toStdString();
|
|
}
|
|
else if (key == "fontsize") {
|
|
QFont font = widget.widget->font();
|
|
return font.pixelSize();
|
|
}
|
|
else if (key == "enabled") {
|
|
return widget.widget->isEnabled();
|
|
}
|
|
else if (key == "visible") {
|
|
return widget.widget->isVisible();
|
|
}
|
|
else if (key == "text") {
|
|
return widgetGetText(widget);
|
|
}
|
|
else if (key == "checkable") {
|
|
return widgetGetCheckable(widget);
|
|
}
|
|
else if (key == "checked") {
|
|
return widgetGetChecked(widget);
|
|
}
|
|
else if (key == "readonly") {
|
|
return widgetGetReadOnly(widget);
|
|
}
|
|
else if (key == "tooltip") {
|
|
return widget.widget->toolTip().toStdString();
|
|
}
|
|
else if (key == "value") {
|
|
return widgetGetValue(widget);
|
|
}
|
|
else if (key == "min") {
|
|
return widgetGetMin(widget);
|
|
}
|
|
else if (key == "max") {
|
|
return widgetGetMax(widget);
|
|
}
|
|
else if (key == "step") {
|
|
return widgetGetStep(widget);
|
|
}
|
|
else if (key == "header") {
|
|
return widgetGetHeader(widget);
|
|
}
|
|
else {
|
|
auto iter = widget.customAttrs.find(key);
|
|
if (iter == widget.customAttrs.end())
|
|
return ValueHolder();
|
|
return iter->second;
|
|
}
|
|
} catch (std::out_of_range&) {
|
|
throw ScriptException("Widget not found: " + widgetName);
|
|
}
|
|
}
|
|
|
|
int ScriptDialog::tableWidgetCount(const std::string& widgetName)
|
|
{
|
|
auto& baseWidget = mp->widgets.at(widgetName);
|
|
auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget);
|
|
if (!widget)
|
|
throw ScriptException("Not a table widget");
|
|
|
|
return widget->rowCount();
|
|
}
|
|
|
|
void ScriptDialog::tableWidgetInsert(const std::string& widgetName, const ValueArray& cols)
|
|
{
|
|
auto& baseWidget = mp->widgets.at(widgetName);
|
|
auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget);
|
|
if (!widget)
|
|
throw ScriptException("Not a table widget");
|
|
|
|
const int rowCount = widget->rowCount() + 1;
|
|
widget->setRowCount(rowCount);
|
|
|
|
try {
|
|
const auto& colCountHolder = cols.at(LIST_END_MAGIC);
|
|
int colCount = ValueExtract(*(colCountHolder.get())).toInt();
|
|
if (colCount > widget->columnCount())
|
|
colCount = widget->columnCount();
|
|
|
|
for (int i = 0; i < colCount; ++i) {
|
|
auto& valHolder = *(cols.at(std::to_string(i)).get());
|
|
QString value;
|
|
switch (valHolder.getType()) {
|
|
case ValueHolder::Type::Void:
|
|
value = "[Void]";
|
|
break;
|
|
case ValueHolder::Type::Integer: {
|
|
int n = ValueExtract(valHolder).toInt();
|
|
value = QString::number(n);
|
|
break;
|
|
}
|
|
case ValueHolder::Type::Real: {
|
|
double n = ValueExtract(valHolder).toReal();
|
|
value = QString::number(n);
|
|
break;
|
|
}
|
|
case ValueHolder::Type::Bool: {
|
|
bool n = ValueExtract(valHolder).toBool();
|
|
value = QString::number(n);
|
|
break;
|
|
}
|
|
case ValueHolder::Type::String:
|
|
value = QString::fromStdString( ValueExtract(valHolder).toString() );
|
|
break;
|
|
case ValueHolder::Type::Map:
|
|
value = "[Map]";
|
|
break;
|
|
}
|
|
|
|
auto* item = new QTableWidgetItem(value);
|
|
widget->setItem(rowCount - 1, i, item);
|
|
}
|
|
|
|
widget->resizeColumnsToContents();
|
|
|
|
} catch (const std::out_of_range&) {
|
|
throw ScriptException("Not a list");
|
|
}
|
|
}
|
|
|
|
void ScriptDialog::tableWidgetRemove(const std::string& widgetName, int index)
|
|
{
|
|
auto& baseWidget = mp->widgets.at(widgetName);
|
|
auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget);
|
|
if (!widget)
|
|
throw ScriptException("Not a table widget");
|
|
|
|
if (index < 0 || index >= widget->rowCount())
|
|
return;
|
|
|
|
widget->removeRow(index);
|
|
}
|
|
|
|
int ScriptDialog::tableWidgetSelected(const std::string& widgetName)
|
|
{
|
|
auto& baseWidget = mp->widgets.at(widgetName);
|
|
auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget);
|
|
if (!widget)
|
|
throw ScriptException("Not a table widget");
|
|
|
|
auto* selection = widget->selectionModel();
|
|
auto idx = selection->currentIndex();
|
|
return idx.isValid() ? idx.row() : -1;
|
|
}
|
|
|
|
ValueArray ScriptDialog::tableWidgetRow(const std::string& widgetName, int index)
|
|
{
|
|
auto& baseWidget = mp->widgets.at(widgetName);
|
|
auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget);
|
|
if (!widget)
|
|
throw ScriptException("Not a table widget");
|
|
|
|
ValueArray ret;
|
|
|
|
if (index < 0 || index >= widget->rowCount()) {
|
|
ret.emplace(LIST_END_MAGIC, std::make_unique<ValueHolder>(0));
|
|
return ret;
|
|
}
|
|
|
|
const int colCount = widget->columnCount();
|
|
int i = 0;
|
|
for (; i < colCount; ++i) {
|
|
auto* item = widget->item(index, i);
|
|
const auto text = item->text().toStdString();
|
|
ret.emplace(std::to_string(i), std::make_unique<ValueHolder>(text));
|
|
}
|
|
ret.emplace(LIST_END_MAGIC, std::make_unique<ValueHolder>(i));
|
|
return ret;
|
|
}
|
|
|
|
const std::string& ScriptDialog::name() const
|
|
{
|
|
return mp->tmpl->name();
|
|
}
|
|
|
|
void ScriptDialog::close()
|
|
{
|
|
mp->dialog->close();
|
|
mp->dialog->deleteLater();
|
|
mp->dialog.release();
|
|
}
|
|
|
|
void ScriptDialog::hide()
|
|
{
|
|
mp->dialog->hide();
|
|
}
|
|
|
|
void ScriptDialog::show()
|
|
{
|
|
mp->dialog->show();
|
|
}
|
|
|
|
|