#include "pivaluetree_edit.h" #include "piqt.h" #include "pivariant_edit.h" #include "qinputdialog.h" #include "qmessagebox.h" #include "qtoolbutton.h" #include "ui_pivaluetree_edit_array.h" #include "ui_pivaluetree_edit_parameters.h" #include #include #include const char property_name[] = "__name__"; PIValueTreeEdit::PIValueTreeEdit(QWidget * parent): QWidget(parent) { widget_params = new QDialog(); ui_array = new Ui::PIValueTreeEditArray(); ui_params = new Ui::PIValueTreeEditParameters(); ui_params->setupUi(widget_params); grid = new GridWidgets(ui_params); grid->parent = this; auto * lay = new QBoxLayout(QBoxLayout::TopToBottom); lay->setContentsMargins(0, 0, 0, 0); setLayout(lay); } PIValueTreeEdit::~PIValueTreeEdit() { delete grid; delete ui_params; delete ui_array; delete widget_params; } void PIValueTreeEdit::setValue(const PIValueTree & v) { source = v; build(); } PIValueTree PIValueTreeEdit::value() const { applyValues(); return source; } void PIValueTreeEdit::setGroupingEnabled(bool yes) { is_grouping = yes; build(); } void PIValueTreeEdit::setFullEditMode(bool yes) { is_full_edit = yes; build(); } void PIValueTreeEdit::rollback() { build(); } void PIValueTreeEdit::clear() { source = PIValueTree(); removeAll(); } void PIValueTreeEdit::changeEvent(QEvent * e) { if (e->type() == QEvent::LanguageChange) { if (widget_array) ui_array->retranslateUi(widget_array); } QWidget::changeEvent(e); } void PIValueTreeEdit::removeAll() { array_edits.clear(); value_edits.clear(); tree_edits.clear(); if (widget_array) { ui_array->layoutArray->takeAt(0); delete widget_array; widget_array = nullptr; } QLayoutItem * child = nullptr; while ((child = layout()->takeAt(0)) != nullptr) { delete child; } grid->clear(); } void PIValueTreeEdit::build() { removeAll(); // piCout << source.attributes().value(PIValueTree::attributeArrayType) << array_type; if (source.isArray()) { grid->create_edit_buttons = false; widget_array = new QWidget(); ui_array->setupUi(widget_array); ui_array->spinCount->setRange(source.attribute(PIValueTree::attributeArrayMinCount, 0).toInt(), source.attribute(PIValueTree::attributeArrayMaxCount, 65536).toInt()); ui_array->spinCount->setValue(source.children().size_s()); ui_array->widgetEdit->setVisible(source.attribute(PIValueTree::attributeArrayResize, false).toBool()); ui_array->layoutArray->addWidget(grid); uint array_type = PIVariant::typeIDFromName(source.attribute(PIValueTree::attributeArrayType).toString()); int index = 0; for (const auto & i: source.children()) { auto * ve = new PIVariantEdit(); ve->setAttributes(source.attributes()); ve->setValue(i.value(), array_type); grid->add(PIValueTree(), QString::number(++index), ve, PI2QString(i.comment())); array_edits << ve; } connect(ui_array->spinCount, QOverload::of(&QSpinBox::valueChanged), this, [this, array_type](int value) { value = piMaxi(value, 0); for (int i = grid->rowCount() - 1; i >= value; --i) grid->removeRow(i); array_edits.resize(grid->rowCount()); for (int i = grid->rowCount(); i < value; ++i) { auto * ve = new PIVariantEdit(); ve->setAttributes(source.attributes()); ve->setValue(PIVariant::fromType(array_type), array_type); grid->add(PIValueTree(), QString::number(i + 1), ve, ""); array_edits << ve; } }); layout()->addWidget(widget_array); } else { grid->create_edit_buttons = is_full_edit; layout()->addWidget(grid); for (const auto & i: source.children()) { if (i.attribute(PIValueTree::attributeHidden, false).toBool()) continue; if (i.attribute(PIValueTree::attributeIsLabel, false).toBool()) { if (i.name().isEmpty()) continue; auto * l = new QLabel(PI2QString(i.name())); l->setAlignment(Qt::AlignCenter); grid->add(i, l); continue; } if (i.hasChildren() || i.isArray()) { auto * ve = new PIValueTreeEdit(); PIStringList rp = root_path; rp << i.name(); ve->root_path = rp; ve->setGroupingEnabled(is_grouping); ve->setFullEditMode(is_full_edit); ve->setValue(i); if (is_grouping) { auto * gb = new QGroupBox(); gb->setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); gb->layout()->addWidget(ve); gb->setTitle(PI2QString(i.name())); gb->setToolTip(PI2QString(i.comment())); gb->setCheckable(true); gb->setChecked(true); gb->setAlignment(Qt::AlignCenter); connect(gb, &QGroupBox::toggled, ve, &QWidget::setVisible); grid->add(i, gb); } else { grid->add(i, PI2QString(i.name()), ve, PI2QString(i.comment())); } tree_edits[i.name()] = ve; } else { auto * ve = new PIVariantEdit(); ve->setAttributes(i.attributes()); ve->setValue(i.value()); grid->add(i, PI2QString(i.name()), ve, PI2QString(i.comment())); value_edits[i.name()] = ve; } } } } void PIValueTreeEdit::applyValues() const { if (source.isArray()) { if (array_edits.isNotEmpty()) source.mergeAttributes(array_edits[0]->defaultAttributes()); source.clearChildren(); for (int i = 0; i < array_edits.size_s(); ++i) source.addChild({PIString::fromNumber(i), array_edits[i]->value()}); } else { auto vit = value_edits.makeIterator(); while (vit.next()) { auto & c(source.child(vit.key())); c.mergeAttributes(vit.value()->defaultAttributes()); c.setValue(vit.value()->value()); } auto tit = tree_edits.makeIterator(); while (tit.next()) { auto & c(source.child(tit.key())); if (!c.isNull()) c = tit.value()->value(); } } } void PIValueTreeEdit::actionTriggered(QToolButton * button, const PIString & vn, QAction * a) { if (a == ui_params->actionRename) { PIString nn = Q2PIString(QInputDialog::getText(nullptr, tr("Rename"), tr("Input new name:"), QLineEdit::Normal, PI2QString(vn))); if (nn.isEmpty() || (nn == vn)) return; for (const auto & c: source.children()) { if (c.name() == nn) { QMessageBox::critical(nullptr, tr("Rename"), tr("This name already exists!")); return; } } source[vn].setName(nn); button->setProperty(property_name, PI2QString(nn)); grid->rename(PI2QString(vn), PI2QString(nn)); } if (a == ui_params->actionRemove) { source.remove(vn); grid->removeRow(grid->getRow(button)); } if (a == ui_params->actionChange) { if (last_IDs_count != PIVariant::knownTypeIDsCount()) { last_IDs_count = PIVariant::knownTypeIDsCount(); ui_params->comboType->clear(); auto ids = PIVariant::knownTypeIDs(); PIVector> types; for (auto id: ids) { if (!PIVariantEditorBase::editorExists(id)) continue; PIString tn = PIVariant::typeNameFromID(id); if (tn.startsWith("PI")) tn.remove(0, 2); if (tn.startsWith("VariantTypes::")) tn.remove(0, 14); types.append({id, PI2QString(tn)}); } types.sort([](const PIPair & a, const PIPair & b) { return QString::localeAwareCompare(a.second, b.second) < 0; }); for (const auto & t: types) ui_params->comboType->addItem(t.second, t.first); } auto old_vt = source.child(vn); ui_params->comboType->setCurrentIndex(ui_params->comboType->findData(old_vt.value().typeID())); ui_params->checkArray->setChecked(old_vt.isArray()); if (widget_params->exec() != QDialog::Accepted) return; auto & el(source[vn]); uint nid = ui_params->comboType->currentData().toUInt(); PIString vs = el.value().toString(); PIVariant var = PIVariant::fromType(nid); var.setValueFromString(vs); source[vn].setValue(var); build(); } } // PIValueTreeEdit::GridWidgets PIValueTreeEdit::GridWidgets::GridWidgets(Ui::PIValueTreeEditParameters * ui_) { ui_params = ui_; icon_conf = QIcon(":/icons/configure.png"); conf_menu.addActions({ui_params->actionRename, ui_params->actionChange, ui_params->actionRemove}); } int PIValueTreeEdit::GridWidgets::getRow(QWidget * w) const { if (!w) return -1; for (int r = 0; r < lay->rowCount(); ++r) { for (int c = 0; c < lay->columnCount(); ++c) { auto * li = lay->itemAtPosition(r, c); if (li) { if (li->widget()) { if (li->widget() == w) return r; } } } } return -1; } void PIValueTreeEdit::GridWidgets::add(const PIValueTree & vt, QString label, QWidget * w, const QString & comment) { int col = beginRow(vt); if (!label.isEmpty()) label += ':'; auto * l = new QLabel(label); auto * c = new QLabel(comment); l->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); c->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); lay->addWidget(l, row_count, col++, Qt::AlignVCenter | Qt::AlignRight); lay->addWidget(w, row_count, col++); lay->addWidget(c, row_count, col++, Qt::AlignVCenter | Qt::AlignLeft); widgets << l << w << c; labels << l; ++row_count; } void PIValueTreeEdit::GridWidgets::add(const PIValueTree & vt, QWidget * w) { int col = beginRow(vt); lay->addWidget(w, row_count, col, 1, -1); widgets << w; ++row_count; } int PIValueTreeEdit::GridWidgets::beginRow(const PIValueTree & vt) { if (!create_edit_buttons) return 0; auto * b = new QToolButton(); b->setIcon(icon_conf); b->setPopupMode(QToolButton::InstantPopup); b->setMenu(&conf_menu); b->setProperty(property_name, PI2QString(vt.name())); lay->addWidget(b, row_count, 0); connect(b, &QToolButton::triggered, this, [this, b](QAction * a) { parent->actionTriggered(b, Q2PIString(b->property(property_name).toString()), a); }); widgets << b; return 1; } void PIValueTreeEdit::GridWidgets::rename(QString prev_name, QString new_name) { if (!prev_name.isEmpty()) prev_name += ':'; if (!new_name.isEmpty()) new_name += ':'; for (auto * l: labels) if (l->text() == prev_name) { l->setText(new_name); break; } } void PIValueTreeEdit::GridWidgets::clear() { piDeleteAllAndClear(widgets); labels.clear(); if (lay) delete lay; lay = new QGridLayout(); lay->setContentsMargins(0, 0, 0, 0); setLayout(lay); row_count = 0; } void PIValueTreeEdit::GridWidgets::removeRow(int index) { if (!lay || index < 0 || row_count <= index) return; for (int c = 0; c < lay->columnCount(); ++c) { auto * li = lay->itemAtPosition(index, c); if (li) { if (li->widget()) { widgets.removeOne(li->widget()); QLabel * lbl = qobject_cast(li->widget()); if (lbl) labels.removeOne(lbl); delete li->widget(); } delete li; } } --row_count; simplify(); } void PIValueTreeEdit::GridWidgets::simplify() { if (!lay) return; QVector> wg; QMap wa; for (int r = 0; r < lay->rowCount(); ++r) { QMap row; for (int c = 0; c < lay->columnCount(); ++c) { auto * li = lay->itemAtPosition(r, c); if (!li) continue; if (li->widget()) { row[c] = li->widget(); wa[li->widget()] = li->alignment(); } } if (!row.isEmpty()) wg << row; } delete lay; lay = new QGridLayout(); lay->setContentsMargins(0, 0, 0, 0); int rindex = 0; for (const auto & row: wg) { QMapIterator it(row); while (it.hasNext()) { it.next(); lay->addWidget(it.value(), rindex, it.key(), wa.value(it.value())); } ++rindex; } setLayout(lay); }