#include "adduniversedialog.h" #include "lineeditdialog.h" #include "multiversewindow.h" #include "multiversemodel.h" #include "ui_multiversewindow.h" #include "universewindow.h" #include #include #include /** * @brief MultiverseWindow::MultiverseWindow * @param parent */ MultiverseWindow::MultiverseWindow(QWidget *parent, QSacnNode *node) : QMainWindow(parent) , ui(new Ui::MultiverseWindow) , node(node) , model(new MultiverseModel(this, node)) , sortProxy(new QSortFilterProxyModel(this)) { // build the UI ui->setupUi(this); ui->menubar->setNativeMenuBar(false); auto mergeMode = new QActionGroup(this); mergeMode->addAction(ui->actionMergeModeHTP); mergeMode->addAction(ui->actionMergeModeLTP); // initial states ui->actionTerminate->setEnabled(false); ui->actionUnsubscribe->setEnabled(false); ui->actionInspect->setEnabled(false); ui->actionIPv4->setChecked(node->isEnabledIPv4()); ui->actionIPv6->setChecked(node->isEnabledIPv6()); ui->actionDiscovery->setChecked(node->discoveryEnabled()); // setup the model sortProxy->setSourceModel(model); ui->multiverseView->setModel(sortProxy); ui->multiverseView->setSortingEnabled(true); ui->multiverseView->sortByColumn(MultiverseModel::Column::Universe, Qt::SortOrder::AscendingOrder); ui->multiverseView->setContextMenuPolicy(Qt::CustomContextMenu); ui->multiverseView->expandAll(); for (int i = 0; i < ui->multiverseView->model()->rowCount(); i++) ui->multiverseView->setFirstColumnSpanned(i, QModelIndex(), true); // model connections connect(ui->multiverseView->selectionModel(), &QItemSelectionModel::currentChanged, this, &MultiverseWindow::selectionChanged); connect(ui->multiverseView, &QTreeView::customContextMenuRequested, this, &MultiverseWindow::openContextMenu); connect(ui->multiverseView, &QTreeView::doubleClicked, this, &MultiverseWindow::openUniverseEditor); // action connections connect(ui->actionUACN, &QAction::triggered, this, &MultiverseWindow::openUacnEditor); connect(ui->actionIPv4, &QAction::toggled, node, &QSacnNode::setIPv4); connect(ui->actionIPv6, &QAction::toggled, node, &QSacnNode::setIPv6); connect(ui->actionDiscovery, &QAction::toggled, node, [&, node](bool checked) { checked ? node->discoveryStart() : node->discoveryStop(); }); connect(ui->actionCreate, &QAction::triggered, this, [this, node]() { auto dialog = new AddUniverseDialog(this, 1); connect(dialog, &AddUniverseDialog::submit, node, &QSacnNode::create); dialog->setWindowTitle(tr("Create")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->open(); }); connect(ui->actionTerminate, &QAction::triggered, this, [&, node]() { auto index = ui->multiverseView->currentIndex(); auto data = index.data(Qt::EditRole); if (data.metaType().id() == qMetaTypeId()) { auto univ = data.value(); if (univ->isEditable()) node->terminate(univ->number()); } }); connect(ui->actionSubscribe, &QAction::triggered, this, [this, node]() { auto val = QVariant(1); auto index = ui->multiverseView->currentIndex(); if (index.isValid()) { auto actual = sortProxy->mapToSource(index); auto item = static_cast(actual.internalPointer()); val = item->data(MultiverseModel::Column::Universe, Qt::DisplayRole); } auto dialog = new AddUniverseDialog(this, val.toInt()); connect(dialog, &AddUniverseDialog::submit, node, &QSacnNode::subscribe); dialog->setWindowTitle(tr("Subscribe")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->open(); }); connect(ui->actionUnsubscribe, &QAction::triggered, this, [&, node]() { auto index = ui->multiverseView->currentIndex(); auto data = index.data(Qt::EditRole); if (data.metaType().id() == qMetaTypeId()) { auto univ = data.value(); if (!univ->isEditable()) node->unsubscribe(univ->number()); } }); connect(ui->actionInspect, &QAction::triggered, this, [this]() { auto selected = ui->multiverseView->currentIndex(); openUniverseEditor(selected); }); connect(ui->actionUniverseHoldLastLook, &QAction::toggled, this, [this](bool state) { auto selected = ui->multiverseView->currentIndex(); auto data = selected.data(Qt::EditRole); auto univ = data.value(); univ->setHoldLastLook(state); }); connect(ui->actionMergeModeHTP, &QAction::toggled, this, [this](bool state) { auto selected = ui->multiverseView->currentIndex(); auto data = selected.data(Qt::EditRole); auto univ = data.value(); if (state) univ->setMergeMode(sACN::UniverseArbitrator::MERGE_HTP); else univ->setMergeMode(sACN::UniverseArbitrator::MERGE_LTP); }); connect(ui->actionAbout, &QAction::triggered, this, [this](){ QString title = tr("About") % " " % qAppName(); QString text = QApplication::organizationName() % "\n" % "https://" % QApplication::organizationDomain() % "\n\n" % QApplication::applicationName() % " is a demonstration of the capabilities " % "of the sACN implimentation in the OpenLCP protocol library using the " % "QsACN platform driver." % "\n\n" % "© 2020-2022 Kevin Matz" % "\n\n" % "Permission is hereby granted, free of charge, to any person obtaining a copy" % "of this software and associated documentation files (the \"Software\"), " % "to deal in the Software without restriction, including without limitation " % "the rights to use, copy, modify, merge, publish, distribute, sublicense, " % "and/or sell copies of the Software, and to permit persons to whom the " % "Software is furnished to do so, subject to the following conditions:" % "\n\n" % "The above copyright notice and this permission notice shall be included in " % "all copies or substantial portions of the Software." % "\n\n" % "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR " % "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, " % "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE " % "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER " % "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING " % "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER " % "DEALINGS IN THE SOFTWARE."; QMessageBox::about(this, title, text); }); connect(ui->actionAbout_Qt, &QAction::triggered, this, [this](){QMessageBox::aboutQt(this);}); } /** * @brief MultiverseWindow::~MultiverseWindow */ MultiverseWindow::~MultiverseWindow() { qDeleteAll(mInspectors); delete ui; } void MultiverseWindow::closeEvent(QCloseEvent *) { // close all universe windows foreach (const auto & inspector, mInspectors) inspector->close(); } /** * @brief MultiverseWindow::selectionChanged * @param selected * @param deselected */ void MultiverseWindow::selectionChanged(const QModelIndex ¤t, const QModelIndex &previous) { Q_UNUSED(previous) ui->actionTerminate->setEnabled(false); ui->actionUnsubscribe->setEnabled(false); ui->actionInspect->setEnabled(false); ui->actionUniverseHoldLastLook->setEnabled(false); ui->menuMerge_Mode->setEnabled(false); auto data = current.data(Qt::EditRole); // Data universes: if (data.metaType().id() == qMetaTypeId()) { auto univ = data.value(); ui->actionInspect->setEnabled(true); ui->actionUniverseHoldLastLook->setChecked(univ->getHoldLastLook()); switch (univ->getMergeMode()){ case sACN::UniverseArbitrator::MERGE_HTP: ui->actionMergeModeHTP->setChecked(true); break; case sACN::UniverseArbitrator::MERGE_LTP: ui->actionMergeModeLTP->setChecked(true); break; default: break; } if (univ->isEditable()) ui->actionTerminate->setEnabled(true); else { ui->actionUnsubscribe->setEnabled(true); if (univ->hasSources()) { ui->actionUniverseHoldLastLook->setEnabled(true); ui->menuMerge_Mode->setEnabled(true); } } return; } } /** * @brief MultiverseWindow::openContextMenu * @param pos */ void MultiverseWindow::openContextMenu(const QPoint &pos) { auto menu = new QMenu(ui->multiverseView); menu->setAttribute(Qt::WA_DeleteOnClose, true); auto globalpos = ui->multiverseView->mapToGlobal(pos); auto data = ui->multiverseView->indexAt(pos).data(Qt::EditRole); if (data.metaType().id() == qMetaTypeId()) { // Data universes: menu->addAction(ui->actionInspect); menu->addSeparator(); auto univ = data.value(); if (univ->isEditable()) menu->addAction(ui->actionTerminate); else menu->addAction(ui->actionUnsubscribe); menu->popup(globalpos); } else if (data.metaType().id() == qMetaTypeId()) { // Discovery universes: menu->addAction(ui->actionSubscribe); menu->popup(globalpos); } else // unknow metaType menu->deleteLater(); } /** * @brief MultiverseWindow::openUniverseEditor * @param index */ void MultiverseWindow::openUniverseEditor(const QModelIndex &index) { auto data = index.data(Qt::EditRole); // Data universes: if (data.metaType().id() == qMetaTypeId()) { auto univ = data.value(); foreach (const auto & inspector, mInspectors) if (inspector->universe() == univ) return inspector->show(); auto view = new UniverseWindow(nullptr, univ); mInspectors.insert(view); connect(view, &QObject::destroyed, this, [this, view](){mInspectors.remove(view);}); connect(univ, &QObject::destroyed, view, &QMainWindow::close); view->setAttribute(Qt::WA_DeleteOnClose, true); view->show(); return; } // Discovery universes: if (data.metaType().id() == qMetaTypeId()) { auto disc = data.value(); auto dialog = new AddUniverseDialog(this, disc->universe); connect(dialog, &AddUniverseDialog::submit, node, &QSacnNode::subscribe); dialog->setWindowTitle(tr("Create")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->open(); return; } } /** * @brief MultiverseWindow::openUacnEditor */ void MultiverseWindow::openUacnEditor() { auto editor = new LineEditDialog(this, "Name", 63, node->userName().c_str(), node->fixedName().c_str()); connect(editor, &LineEditDialog::submit, this, [&](const QString name) {node->assignUserName(name.toStdString());}); editor->setWindowTitle("User Assigned Component Name"); editor->setAttribute(Qt::WA_DeleteOnClose); editor->open(); }