/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #include "portswindow.h" #include "abstractfileformat.h" #include "devicegroupdialog.h" #include "portconfigdialog.h" #include "settings.h" #include "streamconfigdialog.h" #include "streamlistdelegate.h" #include #include #include #include #include PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) : QWidget(parent), proxyPortModel(NULL) { QAction *sep; delegate = new StreamListDelegate; proxyPortModel = new QSortFilterProxyModel(this); //slm = new StreamListModel(); //plm = new PortGroupList(); plm = pgl; setupUi(this); tvPortList->header()->hide(); tvStreamList->setItemDelegate(delegate); tvStreamList->verticalHeader()->setDefaultSectionSize( tvStreamList->verticalHeader()->minimumSectionSize()); deviceGroupList->verticalHeader()->setDefaultSectionSize( deviceGroupList->verticalHeader()->minimumSectionSize()); // Populate PortList Context Menu Actions tvPortList->addAction(actionNew_Port_Group); tvPortList->addAction(actionDelete_Port_Group); tvPortList->addAction(actionConnect_Port_Group); tvPortList->addAction(actionDisconnect_Port_Group); tvPortList->addAction(actionExclusive_Control); tvPortList->addAction(actionPort_Configuration); // Populate StramList Context Menu Actions tvStreamList->addAction(actionNew_Stream); tvStreamList->addAction(actionEdit_Stream); tvStreamList->addAction(actionDuplicate_Stream); tvStreamList->addAction(actionDelete_Stream); sep = new QAction(this); sep->setSeparator(true); tvStreamList->addAction(sep); tvStreamList->addAction(actionOpen_Streams); tvStreamList->addAction(actionSave_Streams); // Populate DeviceGroup Context Menu Actions deviceGroupList->addAction(actionNewDeviceGroup); deviceGroupList->addAction(actionEditDeviceGroup); deviceGroupList->addAction(actionDeleteDeviceGroup); // PortList, StreamList, DeviceGroupList actions combined // make this window's actions addActions(tvPortList->actions()); sep = new QAction(this); sep->setSeparator(true); addAction(sep); addActions(tvStreamList->actions()); sep = new QAction(this); sep->setSeparator(true); addAction(sep); addActions(deviceGroupList->actions()); tvStreamList->setModel(plm->getStreamModel()); deviceGroupList->setModel(plm->getDeviceGroupModel()); // XXX: It would be ideal if we only needed to do the below to // get the proxy model to do its magic. However, the QModelIndex // used by the source model and the proxy model are different // i.e. the row, column, internalId/internalPtr used by both // will be different. Since our domain objects - PortGroupList, // PortGroup, Port etc. use these attributes, we need to map the // proxy's index to the source's index before invoking any domain // object methods // TODO: research if we can skip the mapping when the domain // objects' design is reviewed if (proxyPortModel) { proxyPortModel->setSourceModel(plm->getPortModel()); tvPortList->setModel(proxyPortModel); } else tvPortList->setModel(plm->getPortModel()); connect( plm->getPortModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portModel_dataChanged(const QModelIndex&, const QModelIndex&))); connect(plm->getPortModel(), SIGNAL(modelReset()), SLOT(when_portModel_reset())); connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portView_currentChanged(const QModelIndex&, const QModelIndex&))); connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(updateStreamViewActions())); connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(updateStreamViewActions())); connect(tvStreamList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateStreamViewActions())); connect(tvStreamList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(updateStreamViewActions())); tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); connect(plm->getDeviceGroupModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(updateDeviceViewActions())); connect(plm->getDeviceGroupModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(updateDeviceViewActions())); connect(deviceGroupList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateDeviceViewActions())); connect(deviceGroupList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(updateDeviceViewActions())); // FIXME: hardcoding deviceGroupList->resizeColumnToContents(1); deviceGroupList->resizeColumnToContents(2); // Initially we don't have any ports/streams/devices // - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); updateStreamViewActions(); updateDeviceViewActions(); connect(plm->getStreamModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(streamModelDataChanged())); connect(plm->getStreamModel(), SIGNAL(modelReset()), this, SLOT(streamModelDataChanged())); } void PortsWindow::streamModelDataChanged() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (plm->isPort(current)) plm->port(current).recalculateAverageRates(); } PortsWindow::~PortsWindow() { delete delegate; delete proxyPortModel; } void PortsWindow::showMyReservedPortsOnly(bool enabled) { if (!proxyPortModel) return; if (enabled) { QString rx = "Port Group|\\[" + QRegExp::escape(appSettings->value(kUserKey, kUserDefaultValue).toString()) + "\\]"; qDebug("%s: regexp: <%s>", __FUNCTION__, qPrintable(rx)); proxyPortModel->setFilterRegExp(QRegExp(rx)); } else proxyPortModel->setFilterRegExp(QRegExp("")); } void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) { QModelIndex currentPort = tvPortList->currentIndex(); StreamConfigDialog *scd; int ret; if (proxyPortModel) currentPort = proxyPortModel->mapToSource(currentPort); if (!index.isValid()) { qDebug("%s: invalid index", __FUNCTION__); return; } scd = new StreamConfigDialog(plm->port(currentPort), index.row(), this); qDebug("stream list activated\n"); ret = scd->exec(); if (ret == QDialog::Accepted) plm->port(currentPort).recalculateAverageRates(); delete scd; } void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex, const QModelIndex& previousIndex) { QModelIndex current = currentIndex; QModelIndex previous = previousIndex; Port *port = NULL; if (proxyPortModel) { current = proxyPortModel->mapToSource(current); previous = proxyPortModel->mapToSource(previous); } plm->getStreamModel()->setCurrentPortIndex(current); updatePortViewActions(currentIndex); updateStreamViewActions(); updateDeviceViewActions(); qDebug("In %s", __FUNCTION__); if (plm->isPort(current)) port = &(plm->port(current)); plm->getDeviceGroupModel()->setPort(port); if (previous.isValid() && plm->isPort(previous)) { disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), this, SLOT(updatePortRates())); } if (!current.isValid()) { qDebug("setting stacked widget to blank page"); swDetail->setCurrentIndex(2); // blank page } else { if (plm->isPortGroup(current)) { swDetail->setCurrentIndex(1); // portGroup detail page } else if (plm->isPort(current)) { swDetail->setCurrentIndex(0); // port detail page updatePortRates(); connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), SLOT(updatePortRates())); } } } void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { qDebug("In %s", __FUNCTION__); #if 0 // not sure why the >= <= operators are not overloaded in QModelIndex if ((tvPortList->currentIndex() >= topLeft) && (tvPortList->currentIndex() <= bottomRight)) #endif if (((topLeft < tvPortList->currentIndex()) || (topLeft == tvPortList->currentIndex())) && (((tvPortList->currentIndex() < bottomRight)) || (tvPortList->currentIndex() == bottomRight))) { // Update UI to reflect potential change in exclusive mode, // transmit mode et al when_portView_currentChanged(tvPortList->currentIndex(), tvPortList->currentIndex()); } } void PortsWindow::when_portModel_reset() { when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } void PortsWindow::on_averagePacketsPerSec_editingFinished() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); Q_ASSERT(plm->isPort(current)); bool isOk; double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); plm->port(current).setAveragePacketRate(pps); } void PortsWindow::on_averageBitsPerSec_editingFinished() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); Q_ASSERT(plm->isPort(current)); bool isOk; double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); plm->port(current).setAverageBitRate(bps); } void PortsWindow::updatePortRates() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (!current.isValid()) return; if (!plm->isPort(current)) return; averagePacketsPerSec->setText(QString("%L1") .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); averageBitsPerSec->setText(QString("%L1") .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); } void PortsWindow::updateStreamViewActions() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); // For some reason hasSelection() returns true even if selection size is 0 // so additional check for size introduced if (tvStreamList->selectionModel()->hasSelection() && (tvStreamList->selectionModel()->selection().size() > 0)) { qDebug("Has selection %d", tvStreamList->selectionModel()->selection().size()); // If more than one non-contiguous ranges selected, // disable "New" and "Edit" if (tvStreamList->selectionModel()->selection().size() > 1) { actionNew_Stream->setDisabled(true); actionEdit_Stream->setDisabled(true); } else { actionNew_Stream->setEnabled(true); // Enable "Edit" only if the single range has a single row if (tvStreamList->selectionModel()->selection().at(0).height() > 1) actionEdit_Stream->setDisabled(true); else actionEdit_Stream->setEnabled(true); } // Duplicate/Delete are always enabled as long as we have a selection actionDuplicate_Stream->setEnabled(true); actionDelete_Stream->setEnabled(true); } else { qDebug("No selection"); if (plm->isPort(current)) actionNew_Stream->setEnabled(true); else actionNew_Stream->setDisabled(true); actionEdit_Stream->setDisabled(true); actionDuplicate_Stream->setDisabled(true); actionDelete_Stream->setDisabled(true); } actionOpen_Streams->setEnabled(plm->isPort(current)); actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); } void PortsWindow::updateDeviceViewActions() { QModelIndex current = tvPortList->currentIndex(); QItemSelectionModel *devSel = deviceGroupList->selectionModel(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); // For some reason hasSelection() returns true even if selection size is 0 // so additional check for size introduced if (devSel->hasSelection() && (devSel->selection().size() > 0)) { // If more than one non-contiguous ranges selected, // disable "New" and "Edit" if (devSel->selection().size() > 1) { actionNewDeviceGroup->setDisabled(true); actionEditDeviceGroup->setDisabled(true); } else { actionNewDeviceGroup->setEnabled(true); // Enable "Edit" only if the single range has a single row if (devSel->selection().at(0).height() > 1) actionEditDeviceGroup->setDisabled(true); else actionEditDeviceGroup->setEnabled(true); } // Delete is always enabled as long as we have a selection actionDeleteDeviceGroup->setEnabled(true); } else { qDebug("No device selection"); if (plm->isPort(current)) actionNewDeviceGroup->setEnabled(true); else actionNewDeviceGroup->setDisabled(true); actionEditDeviceGroup->setDisabled(true); actionDeleteDeviceGroup->setDisabled(true); } } void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex) { QModelIndex current = currentIndex; if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (!current.isValid()) { qDebug("current is now invalid"); actionDelete_Port_Group->setDisabled(true); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setDisabled(true); actionExclusive_Control->setDisabled(true); actionPort_Configuration->setDisabled(true); goto _EXIT; } qDebug("currentChanged %llx", current.internalId()); if (plm->isPortGroup(current)) { actionDelete_Port_Group->setEnabled(true); actionExclusive_Control->setDisabled(true); actionPort_Configuration->setDisabled(true); switch(plm->portGroup(current).state()) { case QAbstractSocket::UnconnectedState: case QAbstractSocket::ClosingState: qDebug("state = unconnected|closing"); actionConnect_Port_Group->setEnabled(true); actionDisconnect_Port_Group->setDisabled(true); break; case QAbstractSocket::HostLookupState: case QAbstractSocket::ConnectingState: case QAbstractSocket::ConnectedState: qDebug("state = lookup|connecting|connected"); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setEnabled(true); break; case QAbstractSocket::BoundState: case QAbstractSocket::ListeningState: default: // FIXME(LOW): indicate error qDebug("unexpected state"); break; } } else if (plm->isPort(current)) { actionDelete_Port_Group->setDisabled(true); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setDisabled(true); actionExclusive_Control->setEnabled(true); if (plm->port(current).hasExclusiveControl()) actionExclusive_Control->setChecked(true); else actionExclusive_Control->setChecked(false); actionPort_Configuration->setEnabled(true); } _EXIT: return; } void PortsWindow::on_pbApply_clicked() { QModelIndex curPort; QModelIndex curPortGroup; curPort = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) curPort = proxyPortModel->mapToSource(curPort); if (!curPort.isValid()) { qDebug("%s: curPort is invalid", __FUNCTION__); goto _exit; } if (!plm->isPort(curPort)) { qDebug("%s: curPort is not a port", __FUNCTION__); goto _exit; } if (plm->port(curPort).getStats().state().is_transmit_on()) { QMessageBox::information(0, "Configuration Change", "Please stop transmit on the port before applying any changes"); goto _exit; } curPortGroup = plm->getPortModel()->parent(curPort); if (!curPortGroup.isValid()) { qDebug("%s: curPortGroup is invalid", __FUNCTION__); goto _exit; } if (!plm->isPortGroup(curPortGroup)) { qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); goto _exit; } // FIXME(HI): shd this be a signal? //portGroup.when_configApply(port); // FIXME(MED): mixing port id and index!!! plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); _exit: return; #if 0 // TODO (LOW): This block is for testing only QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (current.isValid()) qDebug("current = %llx", current.internalId()); else qDebug("current is invalid"); #endif } void PortsWindow::on_actionNew_Port_Group_triggered() { bool ok; QString text = QInputDialog::getText(this, "Add Port Group", "Port Group Address (HostName[:Port])", QLineEdit::Normal, lastNewPortGroup, &ok); if (ok) { QStringList addr = text.split(":"); quint16 port = DEFAULT_SERVER_PORT; if (addr.size() > 2) { // IPv6 Address // IPv6 addresses with port number SHOULD be specified as // [2001:db8::1]:80 (RFC5952 Sec6) to avoid ambiguity due to ':' addr = text.split("]:"); if (addr.size() > 1) port = addr[1].toUShort(); } else if (addr.size() == 2) // Hostname/IPv4 + Port specified port = addr[1].toUShort(); // Play nice and remove square brackets irrespective of addr type addr[0].remove(QChar('[')); addr[0].remove(QChar(']')); PortGroup *pg = new PortGroup(addr[0], port); plm->addPortGroup(*pg); lastNewPortGroup = text; } } void PortsWindow::on_actionDelete_Port_Group_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (current.isValid()) plm->removePortGroup(plm->portGroup(current)); } void PortsWindow::on_actionConnect_Port_Group_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (current.isValid()) plm->portGroup(current).connectToHost(); } void PortsWindow::on_actionDisconnect_Port_Group_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (current.isValid()) plm->portGroup(current).disconnectFromHost(); } void PortsWindow::on_actionExclusive_Control_triggered(bool checked) { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (plm->isPort(current)) { OstProto::Port config; config.set_is_exclusive_control(checked); plm->portGroup(current.parent()).modifyPort(current.row(), config); } } void PortsWindow::on_actionPort_Configuration_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (!plm->isPort(current)) return; OstProto::Port config; config.set_transmit_mode(plm->port(current).transmitMode()); config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); config.set_user_name(plm->port(current).userName().toStdString()); PortConfigDialog dialog(config, this); if (dialog.exec() == QDialog::Accepted) plm->portGroup(current.parent()).modifyPort(current.row(), config); } void PortsWindow::on_actionNew_Stream_triggered() { qDebug("New Stream Action"); // In case nothing is selected, insert 1 row at the top int row = 0, count = 1; // In case we have a single range selected; insert as many rows as // in the singe selected range before the top of the selected range if (tvStreamList->selectionModel()->selection().size() == 1) { row = tvStreamList->selectionModel()->selection().at(0).top(); count = tvStreamList->selectionModel()->selection().at(0).height(); } plm->getStreamModel()->insertRows(row, count); } void PortsWindow::on_actionEdit_Stream_triggered() { qDebug("Edit Stream Action"); // Ensure we have only one range selected which contains only one row if ((tvStreamList->selectionModel()->selection().size() == 1) && (tvStreamList->selectionModel()->selection().at(0).height() == 1)) { on_tvStreamList_activated(tvStreamList->selectionModel()-> selection().at(0).topLeft()); } } void PortsWindow::on_actionDuplicate_Stream_triggered() { QItemSelectionModel* model = tvStreamList->selectionModel(); QModelIndex current = tvPortList->selectionModel()->currentIndex(); qDebug("Duplicate Stream Action"); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (model->hasSelection()) { bool isOk; int count = QInputDialog::getInteger(this, "Duplicate Streams", "Count", 1, 1, 9999, 1, &isOk); if (!isOk) return; QList list; foreach(QModelIndex index, model->selectedRows()) list.append(index.row()); plm->port(current).duplicateStreams(list, count); } else qDebug("No selection"); } void PortsWindow::on_actionDelete_Stream_triggered() { qDebug("Delete Stream Action"); QModelIndex index; if (tvStreamList->selectionModel()->hasSelection()) { qDebug("SelectedIndexes %d", tvStreamList->selectionModel()->selectedRows().size()); while(tvStreamList->selectionModel()->selectedRows().size()) { index = tvStreamList->selectionModel()->selectedRows().at(0); plm->getStreamModel()->removeRows(index.row(), 1); } } else qDebug("No selection"); } void PortsWindow::on_actionOpen_Streams_triggered() { qDebug("Open Streams Action"); QModelIndex current = tvPortList->selectionModel()->currentIndex(); static QString dirName; QString fileName; QString errorStr; bool append = true; bool ret; if (proxyPortModel) current = proxyPortModel->mapToSource(current); Q_ASSERT(plm->isPort(current)); fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); if (fileName.isEmpty()) goto _exit; if (tvStreamList->model()->rowCount()) { QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), tr("Append to existing streams? Or overwrite?"), QMessageBox::NoButton, this); QPushButton *appendBtn = msgBox.addButton(tr("Append"), QMessageBox::ActionRole); QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), QMessageBox::ActionRole); QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); msgBox.exec(); if (msgBox.clickedButton() == cancelBtn) goto _exit; else if (msgBox.clickedButton() == appendBtn) append = true; else if (msgBox.clickedButton() == overwriteBtn) append = false; else Q_ASSERT(false); } ret = plm->port(current).openStreams(fileName, append, errorStr); if (!ret || !errorStr.isEmpty()) { QMessageBox msgBox(this); QStringList str = errorStr.split("\n\n\n\n"); msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); msgBox.setWindowTitle(qApp->applicationName()); msgBox.setText(str.at(0)); if (str.size() > 1) msgBox.setDetailedText(str.at(1)); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } dirName = QFileInfo(fileName).absolutePath(); _exit: return; } void PortsWindow::on_actionSave_Streams_triggered() { qDebug("Save Streams Action"); QModelIndex current = tvPortList->selectionModel()->currentIndex(); static QString fileName; QStringList fileTypes = AbstractFileFormat::supportedFileTypes(); QString fileType; QString errorStr; QFileDialog::Options options; if (proxyPortModel) current = proxyPortModel->mapToSource(current); // On Mac OS with Native Dialog, getSaveFileName() ignores fileType // which we need.On some Linux distros the native dialog can't // distinguish between Ostinato(*) and PCAP(*) #if defined(Q_OS_MAC) || defined(Q_OS_UNIX) options |= QFileDialog::DontUseNativeDialog; #endif if (fileTypes.size()) fileType = fileTypes.at(0); Q_ASSERT(plm->isPort(current)); _retry: fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), fileName, fileTypes.join(";;"), &fileType, options); if (fileName.isEmpty()) goto _exit; fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); if (!fileType.startsWith("Ostinato") && !fileType.startsWith("Python")) { if (QMessageBox::warning(this, tr("Ostinato"), QString("You have chosen to save in %1 format. All stream " "attributes may not be saved in this format.\n\n" "It is recommended to save in native Ostinato format.\n\n" "Continue to save in %2 format?").arg(fileType).arg(fileType), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) goto _retry; } // TODO: all or selected? if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) QMessageBox::critical(this, qApp->applicationName(), errorStr); else if (!errorStr.isEmpty()) QMessageBox::warning(this, qApp->applicationName(), errorStr); fileName = QFileInfo(fileName).absolutePath(); _exit: return; } // // DeviceGroup slots // void PortsWindow::on_actionNewDeviceGroup_triggered() { // In case nothing is selected, insert 1 row at the top int row = 0, count = 1; QItemSelection selection = deviceGroupList->selectionModel()->selection(); // In case we have a single range selected; insert as many rows as // in the singe selected range before the top of the selected range if (selection.size() == 1) { row = selection.at(0).top(); count = selection.at(0).height(); } plm->getDeviceGroupModel()->insertRows(row, count); } void PortsWindow::on_actionDeleteDeviceGroup_triggered() { QModelIndex index; if (deviceGroupList->selectionModel()->hasSelection()) { while(deviceGroupList->selectionModel()->selectedRows().size()) { index = deviceGroupList->selectionModel()->selectedRows().at(0); plm->getDeviceGroupModel()->removeRows(index.row(), 1); } } } void PortsWindow::on_actionEditDeviceGroup_triggered() { QItemSelection selection = deviceGroupList->selectionModel()->selection(); // Ensure we have only one range selected which contains only one row if ((selection.size() == 1) && (selection.at(0).height() == 1)) on_deviceGroupList_activated(selection.at(0).topLeft()); } void PortsWindow::on_deviceGroupList_activated(const QModelIndex &index) { if (!index.isValid()) return; DeviceGroupDialog dgd(this); dgd.exec(); }