#include "compounddeviceeditdialog.h" #include "ui_compounddeviceeditdialog.h" #include "device.h" #include #include #include #include #include using namespace std; CompoundDeviceEditDialog::CompoundDeviceEditDialog(CompoundDevice *cdev, QWidget *parent) : QDialog(parent), ui(new Ui::CompoundDeviceEditDialog) { ldev = *cdev; ui->setupUi(this); connect(ui->name, &QLineEdit::editingFinished, [=](){ ldev.name = ui->name->text(); checkIfOkay(); }); for(int i=0;i<(int)CompoundDevice::Synchronization::Last;i++) { ui->sync->addItem(CompoundDevice::SyncToString((CompoundDevice::Synchronization) i)); if((CompoundDevice::Synchronization) i == CompoundDevice::Synchronization::Trigger) { // Disable for now auto *model = qobject_cast(ui->sync->model()); Q_ASSERT(model != nullptr); bool disabled = true; auto *item = model->item(i); item->setFlags(disabled ? item->flags() & ~Qt::ItemIsEnabled : item->flags() | Qt::ItemIsEnabled); } } connect(ui->sync, &QComboBox::currentTextChanged, [=](){ ldev.sync = CompoundDevice::SyncFromString(ui->sync->currentText()); updateDeviceFrames(); }); connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [=](){ *cdev = ldev; accept(); }); connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QDialog::reject); connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){ ldev.saveToFileDialog("Save compound device", "Compound device file (*.cdev)", ".cdev"); }); connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){ if(ldev.openFromFileDialog("Load compound device", "Compound device file (*.cdev)")) { setInitialGUI(); } }); ui->status->setStyleSheet("QLabel { color : red; }"); graph = new QWidget(); ui->scrollArea->setWidget(graph); auto layout = new QHBoxLayout(); graph->setLayout(layout); graph->setAcceptDrops(true); graph->setObjectName("Graph"); graph->installEventFilter(this); ui->lLibreVNA1->installEventFilter(this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); setInitialGUI(); } CompoundDeviceEditDialog::~CompoundDeviceEditDialog() { delete ui; } void CompoundDeviceEditDialog::checkIfOkay() { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); if(deviceFrames.size() != ldev.deviceSerials.size()) { // probably still populating the GUI, skip check return; } if(ldev.name.isEmpty()) { ui->status->setText("Name must not be empty"); return; } if(ldev.deviceSerials.size() < 2) { ui->status->setText("At least two devices are required"); return; } if(ldev.deviceSerials.size() > 4) { ui->status->setText("Only up to four devices supported"); return; } // Check serials for(int i=0;igetSerial(); if(ldev.deviceSerials[i].isEmpty()) { ui->status->setText("Device "+QString::number(i+1)+" has no serial number"); return; } } // Check serials for duplicates for(int i=0;istatus->setText("Duplicate serial number ("+ldev.deviceSerials[i]+") in devices "+QString::number(i+1)+" and "+QString::number(j+1)); return; } } } // Check port mapping // Looking for duplicate and missing ports bool highestPortFound = false; int highestPort; for(int port=0;port<2*ldev.deviceSerials.size();port++) { int num = 0; for(int i=0;igetPort1() == port) { num++; } if(deviceFrames[i]->getPort2() == port) { num++; } } if(num > 1) { ui->status->setText("Duplicate port "+QString::number(port+1)); return; } else if(num == 0) { if(port == 0) { ui->status->setText("Port 1 must be present"); return; } if(!highestPortFound) { highestPort = port; } highestPortFound = true; } else if(highestPortFound) { // port is present, but earlier port was missing ui->status->setText("Missing port: "+QString::number(port+1)+" is selected, but port "+QString::number(port)+" is not present."); return; } } if(!highestPortFound) { highestPort = 2*ldev.deviceSerials.size(); } // All good, actually create the port mapping ldev.portMapping.clear(); for(int port=0;portgetPort1() == port) { found = true; map.device = i; map.port = 0; break; } if(deviceFrames[i]->getPort2() == port) { found = true; map.device = i; map.port = 1; break; } } if(!found) { ui->status->setText("Failed to find port "+QString::number(port+1)+" (likely a bug)"); return; } ldev.portMapping.push_back(map); } ui->status->clear(); ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(true); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } void CompoundDeviceEditDialog::setInitialGUI() { ui->name->setText(ldev.name); ui->sync->setCurrentText(CompoundDevice::SyncToString(ldev.sync)); // Removing the old frames actually modifies the state of ldev as well. Block signals to prevent this from happening for(auto f : deviceFrames) { f->blockSignals(true); } QHBoxLayout* layout = (QHBoxLayout*) graph->layout(); // remove all widgets from the layout if (layout != NULL ) { QLayoutItem* item; while ((item = layout->takeAt(0)) != NULL ) { delete item->widget(); delete item; } } deviceFrames.clear(); layout->addStretch(1); for(int i=0;iaddStretch(1); checkIfOkay(); } DeviceFrame *CompoundDeviceEditDialog::frameAtPosition(int pos) { pos -= graph->layout()->itemAt(0)->geometry().width(); if(pos > 0 && pos <= (int) deviceFrames.size() * frameSize) { return deviceFrames[pos / frameSize]; } return nullptr; } unsigned int CompoundDeviceEditDialog::findInsertPosition(int xcoord) { xcoord -= graph->layout()->itemAt(0)->geometry().width(); // added in port 1 network int index = (xcoord + frameSize / 2) / frameSize; if(index < 0) { index = 0; } else if(index > (int) deviceFrames.size()) { index = deviceFrames.size(); } // add 1 (first widget is always the stretch) return index + 1; } void CompoundDeviceEditDialog::addFrameAtPosition(int pos, DeviceFrame *c) { auto index = findInsertPosition(pos); // add component to the deviceFrame vector index -= 1; // first widget is fixed if(index <= deviceFrames.size()) { addFrame(index, c); } } void CompoundDeviceEditDialog::addFrame(int index, DeviceFrame *c) { if(deviceFrames.size() == ldev.deviceSerials.size()) { ldev.deviceSerials.insert(ldev.deviceSerials.begin() + index, ""); } deviceFrames.insert(deviceFrames.begin() + index, c); deviceFrames[index]->setPosition(index); // remove from list when the component deletes itself connect(c, &DeviceFrame::deleted, [=](){ removeDeviceFrame(c); }); QHBoxLayout *l = static_cast(graph->layout()); l->insertWidget(index + 1, c); c->show(); updateDeviceFrames(); } void CompoundDeviceEditDialog::createDragFrame(DeviceFrame *c) { QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; QByteArray encodedPointer; QDataStream stream(&encodedPointer, QIODevice::WriteOnly); stream << quintptr(c); mimeData->setData("compoundDeviceFrame/pointer", encodedPointer); drag->setMimeData(mimeData); drag->exec(Qt::MoveAction); } void CompoundDeviceEditDialog::updateInsertIndicator(int xcoord) { auto index = findInsertPosition(xcoord); QHBoxLayout *l = static_cast(graph->layout()); l->removeWidget(insertIndicator); l->insertWidget(index, insertIndicator); } bool CompoundDeviceEditDialog::eventFilter(QObject *object, QEvent *event) { if(object->objectName() == "Graph") { if(event->type() == QEvent::MouseButtonPress) { auto mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::LeftButton) { dragFrame = frameAtPosition(mouseEvent->pos().x()); if(dragFrame) { dragStartPosition = mouseEvent->pos(); return true; } } return false; } else if(event->type() == QEvent::MouseMove) { auto mouseEvent = static_cast(event); if (!(mouseEvent->buttons() & Qt::LeftButton)) { return false; } if (!dragFrame) { return false; } if ((mouseEvent->pos() - dragStartPosition).manhattanLength() < QApplication::startDragDistance()) { return false; } // remove and hide component while it is being dragged graph->layout()->removeWidget(dragFrame); dragFrame->hide(); removeDeviceFrame(dragFrame); graph->update(); createDragFrame(dragFrame); return true; } else if(event->type() == QEvent::DragEnter) { auto dragEvent = static_cast(event); if(dragEvent->mimeData()->hasFormat("compoundDeviceFrame/pointer")) { dropPending = true; auto data = dragEvent->mimeData()->data("compoundDeviceFrame/pointer"); QDataStream stream(&data, QIODevice::ReadOnly); quintptr dropPtr; stream >> dropPtr; dropFrame = (DeviceFrame*) dropPtr; dragEvent->acceptProposedAction(); insertIndicator = new QWidget(); insertIndicator->setMinimumSize(2, frameSize); insertIndicator->setMaximumSize(2, frameSize); insertIndicator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); insertIndicator->setStyleSheet("background-color:red;"); updateInsertIndicator(dragEvent->pos().x()); return true; } } else if(event->type() == QEvent::DragMove) { auto dragEvent = static_cast(event); updateInsertIndicator(dragEvent->pos().x()); return true; } else if(event->type() == QEvent::Drop) { auto dragEvent = static_cast(event); delete insertIndicator; addFrameAtPosition(dragEvent->pos().x(), dropFrame); return true; } else if(event->type() == QEvent::DragLeave) { dropPending = false; dropFrame = nullptr; delete insertIndicator; } } else { // clicked/dragged one of the components outside of the graph if(event->type() == QEvent::MouseButtonPress) { auto mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::LeftButton) { dragStartPosition = mouseEvent->pos(); if(object->objectName() == "lLibreVNA1") { dragFrame = new DeviceFrame(&ldev, 0); connect(dragFrame, &DeviceFrame::settingChanged, this, &CompoundDeviceEditDialog::checkIfOkay); } else { dragFrame = nullptr; } return true; } return false; } else if(event->type() == QEvent::MouseMove) { auto mouseEvent = static_cast(event); if (!(mouseEvent->buttons() & Qt::LeftButton)) { return false; } if (!dragFrame) { return false; } if ((mouseEvent->pos() - dragStartPosition).manhattanLength() < QApplication::startDragDistance()) { return false; } createDragFrame(dragFrame); return true; } } return false; } void CompoundDeviceEditDialog::removeDeviceFrame(DeviceFrame *dev) { auto it = std::find(deviceFrames.begin(), deviceFrames.end(), dev); if(it == deviceFrames.end()) { // not found, shouldn't happen return; } auto pos = it - deviceFrames.begin(); deviceFrames.erase(it); ldev.deviceSerials.erase(ldev.deviceSerials.begin() + pos); // remove all port mappings from the removed device bool mappingFound; do { mappingFound = false; for(int i=0;isetPosition(i++); } } DeviceFrame::DeviceFrame(CompoundDevice *dev, int position) : dev(dev), position(position) { setMinimumSize(frameSize, frameSize); setMaximumSize(frameSize, frameSize); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setFocusPolicy(Qt::FocusPolicy::ClickFocus); serial = new QComboBox(); serial->setEditable(true); port1 = new QComboBox(); port2 = new QComboBox(); connect(serial, &QComboBox::currentTextChanged, this, &DeviceFrame::settingChanged); connect(port1, &QComboBox::currentTextChanged, this, &DeviceFrame::settingChanged); connect(port2, &QComboBox::currentTextChanged, this, &DeviceFrame::settingChanged); auto layout = new QVBoxLayout; layout->addStretch(1); auto l1 = new QHBoxLayout; l1->addStretch(1); l1->addWidget(serial); l1->addStretch(1); layout->addLayout(l1); layout->addStretch(1); auto l2 = new QHBoxLayout; l2->addWidget(port1); l2->addStretch(1); l2->addWidget(port2); layout->addLayout(l2); setLayout(layout); update(); // Set initial state if(position < dev->deviceSerials.size()) { serial->setCurrentText(dev->deviceSerials[position]); for(int i=0;iportMapping.size();i++) { if(dev->portMapping[i].device == position) { if(dev->portMapping[i].port == 0) { port1->setCurrentIndex(i + 1); } else if(dev->portMapping[i].port == 1) { port2->setCurrentIndex(i + 1); } } } } } DeviceFrame::~DeviceFrame() { emit deleted(); } void DeviceFrame::setPosition(int pos) { position = pos; update(); } void DeviceFrame::update() { auto p1 = port1->currentText(); auto p2 = port2->currentText(); auto s = serial->currentText(); port1->clear(); port2->clear(); serial->clear(); port1->addItem("Unused"); port2->addItem("Unused"); for(int i=0;ideviceSerials.size();i++) { port1->addItem("Port "+QString::number(i*2+1)); port2->addItem("Port "+QString::number(i*2+1)); port1->addItem("Port "+QString::number(i*2+2)); port2->addItem("Port "+QString::number(i*2+2)); } if(port1->findText(p1) >= 0) { port1->setCurrentText(p1); } else { port1->setCurrentIndex(0); } if(port2->findText(p2) >= 0) { port2->setCurrentText(p2); } else { port2->setCurrentIndex(0); } auto devices = Device::GetDevices(); for(auto d : devices) { serial->addItem(d); } // if(!serial->findText(s) >= 0) { // serial->addItem(s); // } serial->setCurrentText(s); if(dev->sync == CompoundDevice::Synchronization::USB) { setStyleSheet("image: url(:/icons/compound_V1_USB.png);"); } else if(dev->sync == CompoundDevice::Synchronization::ExtRef) { if(position == 0) { setStyleSheet("image: url(:/icons/compound_V1_Ref_Left.png);"); } else if(position == dev->deviceSerials.size() - 1) { setStyleSheet("image: url(:/icons/compound_V1_Ref_Right.png);"); } else { setStyleSheet("image: url(:/icons/compound_V1_Ref_Middle.png);"); } } } QString DeviceFrame::getSerial() { return serial->currentText(); } int DeviceFrame::getPort1() { return port1->currentIndex() - 1; } int DeviceFrame::getPort2() { return port2->currentIndex() - 1; }