File indexing completed on 2024-05-12 15:27:50

0001 /***************************************************************************
0002 File                 : ImportFileWidget.cpp
0003 Project              : LabPlot
0004 Description          : import file data widget
0005 --------------------------------------------------------------------
0006 Copyright            : (C) 2009-2018 Stefan Gerlach (stefan.gerlach@uni.kn)
0007 Copyright            : (C) 2009-2020 Alexander Semke (alexander.semke@web.de)
0008 Copyright            : (C) 2017-2018 Fabian Kristof (fkristofszabolcs@gmail.com)
0009 Copyright            : (C) 2018-2019 Kovacs Ferencz (kferike98@gmail.com)
0010 
0011 ***************************************************************************/
0012 
0013 /***************************************************************************
0014  *                                                                         *
0015  *  This program is free software; you can redistribute it and/or modify   *
0016  *  it under the terms of the GNU General Public License as published by   *
0017  *  the Free Software Foundation; either version 2 of the License, or      *
0018  *  (at your option) any later version.                                    *
0019  *                                                                         *
0020  *  This program is distributed in the hope that it will be useful,        *
0021  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0022  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0023  *  GNU General Public License for more details.                           *
0024  *                                                                         *
0025  *   You should have received a copy of the GNU General Public License     *
0026  *   along with this program; if not, write to the Free Software           *
0027  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0028  *   Boston, MA  02110-1301  USA                                           *
0029  *                                                                         *
0030  ***************************************************************************/
0031 
0032 #include "ImportFileWidget.h"
0033 #include "FileInfoDialog.h"
0034 #include "backend/datasources/filters/filters.h"
0035 #include "AsciiOptionsWidget.h"
0036 #include "BinaryOptionsWidget.h"
0037 #include "HDF5OptionsWidget.h"
0038 #include "ImageOptionsWidget.h"
0039 #include "NetCDFOptionsWidget.h"
0040 #include "FITSOptionsWidget.h"
0041 #include "JsonOptionsWidget.h"
0042 #include "ROOTOptionsWidget.h"
0043 
0044 #include <QCompleter>
0045 #include <QDir>
0046 #include <QDirModel>
0047 #include <QFileDialog>
0048 #include <QInputDialog>
0049 #include <QIntValidator>
0050 #include <QLocalSocket>
0051 #include <QStandardItemModel>
0052 #include <QTableWidget>
0053 #include <QTcpSocket>
0054 #include <QTimer>
0055 #include <QUdpSocket>
0056 #include <QTreeWidgetItem>
0057 
0058 #include <KConfigGroup>
0059 #include <KLocalizedString>
0060 #include <KSharedConfig>
0061 #include <KUrlComboBox>
0062 
0063 #ifdef HAVE_MQTT
0064 #include "kdefrontend/widgets/MQTTWillSettingsWidget.h"
0065 #include "MQTTConnectionManagerDialog.h"
0066 #include "MQTTSubscriptionWidget.h"
0067 #include <QMqttClient>
0068 #include <QMqttSubscription>
0069 #include <QMqttTopicFilter>
0070 #include <QMqttMessage>
0071 #include <QMessageBox>
0072 #include <QWidgetAction>
0073 #include <QMenu>
0074 #endif
0075 
0076 QString absolutePath(const QString& fileName) {
0077 #ifdef HAVE_WINDOWS
0078     if (!fileName.isEmpty() && fileName.at(1) != QLatin1String(":"))
0079 #else
0080     if (!fileName.isEmpty() && fileName.at(0) != QLatin1String("/"))
0081 #endif
0082         return QDir::homePath() + QLatin1String("/") + fileName;
0083 
0084     return fileName;
0085 }
0086 
0087 /*!
0088    \class ImportFileWidget
0089    \brief Widget for importing data from a file.
0090 
0091    \ingroup kdefrontend
0092 */
0093 ImportFileWidget::ImportFileWidget(QWidget* parent, bool liveDataSource, const QString& fileName) : QWidget(parent),
0094     m_fileName(fileName),
0095     m_liveDataSource(liveDataSource)
0096 #ifdef HAVE_MQTT
0097     ,
0098     m_subscriptionWidget(new MQTTSubscriptionWidget(this))
0099 #endif
0100 {
0101     ui.setupUi(this);
0102 
0103     //add supported file types
0104     if (!liveDataSource) {
0105         ui.cbFileType->addItem(i18n("ASCII data"), static_cast<int>(AbstractFileFilter::FileType::Ascii));
0106         ui.cbFileType->addItem(i18n("Binary data"), static_cast<int>(AbstractFileFilter::FileType::Binary));
0107         ui.cbFileType->addItem(i18n("Image"), static_cast<int>(AbstractFileFilter::FileType::Image));
0108 #ifdef HAVE_HDF5
0109         ui.cbFileType->addItem(i18n("Hierarchical Data Format 5 (HDF5)"), static_cast<int>(AbstractFileFilter::FileType::HDF5));
0110 #endif
0111 #ifdef HAVE_NETCDF
0112         ui.cbFileType->addItem(i18n("Network Common Data Format (NetCDF)"), static_cast<int>(AbstractFileFilter::FileType::NETCDF));
0113 #endif
0114 #ifdef HAVE_FITS
0115         ui.cbFileType->addItem(i18n("Flexible Image Transport System Data Format (FITS)"), static_cast<int>(AbstractFileFilter::FileType::FITS));
0116 #endif
0117         ui.cbFileType->addItem(i18n("JSON data"), static_cast<int>(AbstractFileFilter::FileType::JSON));
0118 #ifdef HAVE_ZIP
0119         ui.cbFileType->addItem(i18n("ROOT (CERN)"), static_cast<int>(AbstractFileFilter::FileType::ROOT));
0120 #endif
0121         ui.cbFileType->addItem(i18n("Ngspice RAW ASCII"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawAscii));
0122         ui.cbFileType->addItem(i18n("Ngspice RAW Binary"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawBinary));
0123 
0124         //hide widgets relevant for live data reading only
0125         ui.lRelativePath->hide();
0126         ui.chbRelativePath->hide();
0127         ui.lSourceType->hide();
0128         ui.cbSourceType->hide();
0129         ui.gbUpdateOptions->hide();
0130     } else {
0131         ui.cbFileType->addItem(i18n("ASCII data"), static_cast<int>(AbstractFileFilter::FileType::Ascii));
0132         ui.cbFileType->addItem(i18n("Binary data"), static_cast<int>(AbstractFileFilter::FileType::Binary));
0133 #ifdef HAVE_ZIP
0134         ui.cbFileType->addItem(i18n("ROOT (CERN)"), static_cast<int>(AbstractFileFilter::FileType::ROOT));
0135 #endif
0136         ui.cbFileType->addItem(i18n("Ngspice RAW ASCII"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawAscii));
0137         ui.cbFileType->addItem(i18n("Ngspice RAW Binary"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawBinary));
0138 
0139         ui.lePort->setValidator( new QIntValidator(ui.lePort) );
0140         ui.cbBaudRate->addItems(LiveDataSource::supportedBaudRates());
0141         ui.cbSerialPort->addItems(LiveDataSource::availablePorts());
0142 
0143         ui.tabWidget->removeTab(2);
0144 
0145         ui.chbLinkFile->setToolTip(i18n("If this option is checked, only the link to the file is stored in the project file but not its content."));
0146         ui.chbRelativePath->setToolTip(i18n("If this option is checked, the relative path of the file (relative to project's folder) will be saved."));
0147     }
0148 
0149     QStringList filterItems {i18n("Automatic"), i18n("Custom")};
0150     ui.cbFilter->addItems(filterItems);
0151 
0152     //hide options that will be activated on demand
0153     ui.gbOptions->hide();
0154     ui.gbUpdateOptions->hide();
0155     setMQTTVisible(false);
0156 
0157     ui.cbReadingType->addItem(i18n("Whole file"), static_cast<int>(LiveDataSource::ReadingType::WholeFile));
0158 
0159     ui.bOpen->setIcon( QIcon::fromTheme(QLatin1String("document-open")) );
0160     ui.bFileInfo->setIcon( QIcon::fromTheme(QLatin1String("help-about")) );
0161     ui.bManageFilters->setIcon( QIcon::fromTheme(QLatin1String("configure")) );
0162     ui.bSaveFilter->setIcon( QIcon::fromTheme(QLatin1String("document-save")) );
0163     ui.bRefreshPreview->setIcon( QIcon::fromTheme(QLatin1String("view-refresh")) );
0164 
0165     ui.tvJson->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0166     ui.tvJson->setAlternatingRowColors(true);
0167     showJsonModel(false);
0168 
0169     // the table widget for preview
0170     m_twPreview = new QTableWidget(ui.tePreview);
0171     m_twPreview->verticalHeader()->hide();
0172     m_twPreview->setEditTriggers(QTableWidget::NoEditTriggers);
0173     auto* layout = new QHBoxLayout;
0174     layout->addWidget(m_twPreview);
0175     ui.tePreview->setLayout(layout);
0176     m_twPreview->hide();
0177 
0178     // the combobox for the import path
0179     m_cbFileName = new KUrlComboBox(KUrlComboBox::Mode::Files, this);
0180     m_cbFileName->setMaxItems(7);
0181     auto* gridLayout = dynamic_cast<QGridLayout*>(ui.gbDataSource->layout());
0182     if (gridLayout)
0183         gridLayout->addWidget(m_cbFileName, 1, 2, 1, 3);
0184 
0185 
0186     //tooltips
0187     QString info = i18n("Specify how the data source has to be processed on every read:"
0188                        "<ul>"
0189                        "<li>Continuously fixed - fixed amount of samples is processed starting from the beginning of the newly received data.</li>"
0190                        "<li>From End - fixed amount of samples is processed starting from the end of the newly received data.</li>"
0191                        "<li>Till the End - all newly received data is processed.</li>"
0192                        "<li>Whole file - on every read the whole file is re-read completely and processed. Only available for \"File Or Named Pipe\" data sources.</li>"
0193                        "</ul>");
0194     ui.lReadingType->setToolTip(info);
0195     ui.cbReadingType->setToolTip(info);
0196 
0197     info = i18n("Number of samples (lines) to be processed on every read.\n"
0198                 "Only needs to be specified for the reading mode \"Continuously Fixed\" and \"From End\".");
0199     ui.lSampleSize->setToolTip(info);
0200     ui.sbSampleSize->setToolTip(info);
0201 
0202     info = i18n("Specify when and how frequently the data source needs to be read:"
0203                 "<ul>"
0204                 "<li>Periodically - the data source is read periodically with user specified time interval.</li>"
0205                 "<li>On New Data - the data source is read when new data arrives.</li>"
0206                 "</ul>");
0207     ui.lUpdateType->setToolTip(info);
0208     ui.cbUpdateType->setToolTip(info);
0209 
0210     info = i18n("Specify how frequently the data source has to be read.");
0211     ui.lUpdateInterval->setToolTip(info);
0212     ui.sbUpdateInterval->setToolTip(info);
0213 
0214     info = i18n("Specify how many samples need to be kept in memory after reading.\n"
0215                 "Use \"All\" if all data has to be kept.");
0216     ui.lKeepLastValues->setToolTip(info);
0217     ui.sbKeepNValues->setToolTip(info);
0218 
0219 #ifdef HAVE_MQTT
0220     ui.cbSourceType->addItem(QLatin1String("MQTT"));
0221     m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() + QLatin1String("MQTT_connections");
0222 
0223     //add subscriptions widget
0224     layout = new QHBoxLayout;
0225     layout->setContentsMargins(0, 0, 0, 0);
0226     layout->setSpacing(0);
0227     layout->addWidget(m_subscriptionWidget);
0228     ui.frameSubscriptions->setLayout(layout);
0229 
0230     ui.bManageConnections->setIcon(QIcon::fromTheme(QLatin1String("network-server")));
0231     ui.bManageConnections->setToolTip(i18n("Manage MQTT connections"));
0232 
0233     info = i18n("Specify the 'Last Will and Testament' message (LWT). At least one topic has to be subscribed.");
0234     ui.lLWT->setToolTip(info);
0235     ui.bLWT->setToolTip(info);
0236     ui.bLWT->setEnabled(false);
0237     ui.bLWT->setIcon(ui.bLWT->style()->standardIcon(QStyle::SP_FileDialogDetailedView));
0238 #endif
0239 
0240     //TODO: implement save/load of user-defined settings later and activate these buttons again
0241     ui.bSaveFilter->hide();
0242     ui.bManageFilters->hide();
0243 }
0244 
0245 void ImportFileWidget::loadSettings() {
0246     m_suppressRefresh = true;
0247 
0248     //load last used settings
0249     QString confName;
0250     if (m_liveDataSource)
0251         confName = QLatin1String("LiveDataImport");
0252     else
0253         confName = QLatin1String("FileImport");
0254     KConfigGroup conf(KSharedConfig::openConfig(), confName);
0255 
0256     //read the source type first since settings in fileNameChanged() depend on this
0257     ui.cbSourceType->setCurrentIndex(conf.readEntry("SourceType").toInt());
0258 
0259     //general settings
0260     AbstractFileFilter::FileType fileType = static_cast<AbstractFileFilter::FileType>(conf.readEntry("Type", 0));
0261     for (int i = 0; i < ui.cbFileType->count(); ++i) {
0262         if (static_cast<AbstractFileFilter::FileType>(ui.cbFileType->itemData(i).toInt()) == fileType) {
0263             if (ui.cbFileType->currentIndex() == i)
0264                 initOptionsWidget();
0265             else
0266                 ui.cbFileType->setCurrentIndex(i);
0267 
0268             break;
0269         }
0270     }
0271 
0272     if (m_fileName.isEmpty()) {
0273         ui.cbFilter->setCurrentIndex(conf.readEntry("Filter", 0));
0274         m_cbFileName->setUrl(QUrl(conf.readEntry("LastImportedFile", "")));
0275         QStringList urls = m_cbFileName->urls();
0276         urls.append(conf.readXdgListEntry("LastImportedFiles"));
0277         m_cbFileName->setUrls(urls);
0278         filterChanged(ui.cbFilter->currentIndex()); // needed if filter is not changed
0279     } else
0280         m_cbFileName->setUrl(QUrl(m_fileName));
0281 
0282     ui.sbPreviewLines->setValue(conf.readEntry("PreviewLines", 100));
0283 
0284     //live data related settings
0285     ui.cbBaudRate->setCurrentIndex(conf.readEntry("BaudRate", 13)); // index for bautrate 19200b/s
0286     ui.cbReadingType->setCurrentIndex(conf.readEntry("ReadingType", static_cast<int>(LiveDataSource::ReadingType::WholeFile)));
0287     ui.cbSerialPort->setCurrentIndex(conf.readEntry("SerialPort").toInt());
0288     ui.cbUpdateType->setCurrentIndex(conf.readEntry("UpdateType", static_cast<int>(LiveDataSource::UpdateType::NewData)));
0289     updateTypeChanged(ui.cbUpdateType->currentIndex());
0290     ui.leHost->setText(conf.readEntry("Host",""));
0291     ui.sbKeepNValues->setValue(conf.readEntry("KeepNValues", 0)); // keep all values
0292     ui.lePort->setText(conf.readEntry("Port",""));
0293     ui.sbSampleSize->setValue(conf.readEntry("SampleSize", 1));
0294     ui.sbUpdateInterval->setValue(conf.readEntry("UpdateInterval", 1000));
0295     ui.chbLinkFile->setCheckState((Qt::CheckState)conf.readEntry("LinkFile", (int)Qt::CheckState::Unchecked));
0296     ui.chbRelativePath->setCheckState((Qt::CheckState)conf.readEntry("RelativePath", (int)Qt::CheckState::Unchecked));
0297 
0298 #ifdef HAVE_MQTT
0299     //read available MQTT connections
0300     m_initialisingMQTT = true;
0301     readMQTTConnections();
0302     ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(conf.readEntry("Connection", "")));
0303     m_initialisingMQTT = false;
0304 
0305     m_willSettings.enabled = conf.readEntry("mqttWillEnabled", m_willSettings.enabled);
0306     m_willSettings.willRetain = conf.readEntry("mqttWillRetain", m_willSettings.willRetain);
0307     m_willSettings.willUpdateType = static_cast<MQTTClient::WillUpdateType>(conf.readEntry("mqttWillUpdateType", (int)m_willSettings.willUpdateType));
0308     m_willSettings.willMessageType = static_cast<MQTTClient::WillMessageType>(conf.readEntry("mqttWillMessageType", (int)m_willSettings.willMessageType));
0309     m_willSettings.willQoS = conf.readEntry("mqttWillQoS", (int)m_willSettings.willQoS);
0310     m_willSettings.willOwnMessage = conf.readEntry("mqttWillOwnMessage", m_willSettings.willOwnMessage);
0311     m_willSettings.willTimeInterval = conf.readEntry("mqttWillUpdateInterval", m_willSettings.willTimeInterval);
0312 
0313     const QString& willStatistics = conf.readEntry("mqttWillStatistics","");
0314     const QStringList& statisticsList = willStatistics.split('|', QString::SplitBehavior::SkipEmptyParts);
0315     for (auto value : statisticsList)
0316         m_willSettings.willStatistics[value.toInt()] = true;
0317 #endif
0318 
0319     //initialize the slots after all settings were set in order to avoid unneeded refreshes
0320     initSlots();
0321 
0322     //update the status of the widgets
0323     fileTypeChanged();
0324     sourceTypeChanged(static_cast<int>(currentSourceType()));
0325     readingTypeChanged(ui.cbReadingType->currentIndex());
0326 
0327     //all set now, refresh the content of the file and the preview for the selected dataset
0328     m_suppressRefresh = false;
0329     QTimer::singleShot(100, this, [=] () {
0330         WAIT_CURSOR;
0331         if (currentSourceType() == LiveDataSource::SourceType::FileOrPipe) {
0332             QString tempFileName = fileName();
0333             const QString& fileName = absolutePath(tempFileName);
0334             if (QFile::exists(fileName))
0335                 updateContent(fileName);
0336         }
0337 
0338         refreshPreview();
0339         RESET_CURSOR;
0340     });
0341 }
0342 
0343 ImportFileWidget::~ImportFileWidget() {
0344     // save current settings
0345     QString confName;
0346     if (m_liveDataSource)
0347         confName = QLatin1String("LiveDataImport");
0348     else
0349         confName = QLatin1String("FileImport");
0350     KConfigGroup conf(KSharedConfig::openConfig(), confName);
0351 
0352     // general settings
0353     conf.writeEntry("Type", (int)currentFileType());
0354     conf.writeEntry("Filter", ui.cbFilter->currentIndex());
0355     conf.writeEntry("LastImportedFile", m_cbFileName->currentText());
0356     conf.writeXdgListEntry("LastImportedFiles", m_cbFileName->urls());
0357     conf.writeEntry("PreviewLines", ui.sbPreviewLines->value());
0358 
0359     //live data related settings
0360     conf.writeEntry("SourceType", (int)currentSourceType());
0361     conf.writeEntry("UpdateType", ui.cbUpdateType->currentIndex());
0362     conf.writeEntry("ReadingType", ui.cbReadingType->currentIndex());
0363     conf.writeEntry("SampleSize", ui.sbSampleSize->value());
0364     conf.writeEntry("KeepNValues", ui.sbKeepNValues->value());
0365     conf.writeEntry("BaudRate", ui.cbBaudRate->currentIndex());
0366     conf.writeEntry("SerialPort", ui.cbSerialPort->currentIndex());
0367     conf.writeEntry("Host", ui.leHost->text());
0368     conf.writeEntry("Port", ui.lePort->text());
0369     conf.writeEntry("UpdateInterval", ui.sbUpdateInterval->value());
0370     conf.writeEntry("LinkFile", (int)ui.chbLinkFile->checkState());
0371     conf.writeEntry("RelativePath", (int)ui.chbRelativePath->checkState());
0372 
0373 #ifdef HAVE_MQTT
0374     delete m_connectTimeoutTimer;
0375     delete m_subscriptionWidget;
0376 
0377     //MQTT related settings
0378     conf.writeEntry("Connection", ui.cbConnection->currentText());
0379     conf.writeEntry("mqttWillMessageType", static_cast<int>(m_willSettings.willMessageType));
0380     conf.writeEntry("mqttWillUpdateType", static_cast<int>(m_willSettings.willUpdateType));
0381     conf.writeEntry("mqttWillQoS", QString::number(m_willSettings.willQoS));
0382     conf.writeEntry("mqttWillOwnMessage", m_willSettings.willOwnMessage);
0383     conf.writeEntry("mqttWillUpdateInterval", QString::number(m_willSettings.willTimeInterval));
0384     QString willStatistics;
0385     for (int i = 0; i < m_willSettings.willStatistics.size(); ++i) {
0386         if (m_willSettings.willStatistics[i])
0387             willStatistics += QString::number(i)+ QLatin1Char('|');
0388     }
0389     conf.writeEntry("mqttWillStatistics", willStatistics);
0390     conf.writeEntry("mqttWillRetain", static_cast<int>(m_willSettings.willRetain));
0391     conf.writeEntry("mqttWillUse", static_cast<int>(m_willSettings.enabled));
0392 #endif
0393 
0394     // data type specific settings
0395     if (m_asciiOptionsWidget)
0396         m_asciiOptionsWidget->saveSettings();
0397     if (m_binaryOptionsWidget)
0398         m_binaryOptionsWidget->saveSettings();
0399     if (m_imageOptionsWidget)
0400         m_imageOptionsWidget->saveSettings();
0401     if (m_jsonOptionsWidget)
0402         m_jsonOptionsWidget->saveSettings();
0403 }
0404 
0405 void ImportFileWidget::initSlots() {
0406     //SLOTs for the general part of the data source configuration
0407     connect(ui.cbSourceType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged),
0408             this, static_cast<void (ImportFileWidget::*) (int)>(&ImportFileWidget::sourceTypeChanged));
0409     connect(m_cbFileName, &KUrlComboBox::urlActivated,
0410             this, [=](const QUrl &url){fileNameChanged(url.path());});
0411     connect(ui.leHost, &QLineEdit::textChanged, this, &ImportFileWidget::hostChanged);
0412     connect(ui.lePort, &QLineEdit::textChanged, this, &ImportFileWidget::portChanged);
0413     connect(ui.tvJson, &QTreeView::clicked, this, &ImportFileWidget::refreshPreview);
0414     connect(ui.bOpen, &QPushButton::clicked, this, &ImportFileWidget::selectFile);
0415     connect(ui.bFileInfo, &QPushButton::clicked, this, &ImportFileWidget::fileInfoDialog);
0416     connect(ui.bSaveFilter, &QPushButton::clicked, this, &ImportFileWidget::saveFilter);
0417     connect(ui.bManageFilters, &QPushButton::clicked, this, &ImportFileWidget::manageFilters);
0418     connect(ui.cbFileType, static_cast<void (KComboBox::*) (int)>(&KComboBox::currentIndexChanged),
0419             this, &ImportFileWidget::fileTypeChanged);
0420     connect(ui.cbUpdateType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged),
0421             this, &ImportFileWidget::updateTypeChanged);
0422     connect(ui.cbReadingType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged),
0423             this, &ImportFileWidget::readingTypeChanged);
0424     connect(ui.cbFilter, static_cast<void (KComboBox::*) (int)>(&KComboBox::activated), this, &ImportFileWidget::filterChanged);
0425     connect(ui.bRefreshPreview, &QPushButton::clicked, this, &ImportFileWidget::refreshPreview);
0426 
0427 #ifdef HAVE_MQTT
0428     connect(ui.cbConnection, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), this, &ImportFileWidget::mqttConnectionChanged);
0429     connect(ui.cbFileType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), [this]() {
0430         emit checkFileType();
0431     });
0432     connect(ui.bManageConnections, &QPushButton::clicked, this, &ImportFileWidget::showMQTTConnectionManager);
0433     connect(ui.bLWT, &QPushButton::clicked, this, &ImportFileWidget::showWillSettings);
0434     connect(m_subscriptionWidget, &MQTTSubscriptionWidget::makeSubscription, this, &ImportFileWidget::subscribeTopic);
0435     connect(m_subscriptionWidget, &MQTTSubscriptionWidget::MQTTUnsubscribeFromTopic, this, &ImportFileWidget::unsubscribeTopic);
0436     connect(m_subscriptionWidget, &MQTTSubscriptionWidget::enableWill, this, &ImportFileWidget::enableWill);
0437     connect(m_subscriptionWidget, &MQTTSubscriptionWidget::subscriptionChanged, this, &ImportFileWidget::refreshPreview);
0438 #endif
0439 }
0440 
0441 void ImportFileWidget::showAsciiHeaderOptions(bool b) {
0442     if (m_asciiOptionsWidget)
0443         m_asciiOptionsWidget->showAsciiHeaderOptions(b);
0444 }
0445 
0446 void ImportFileWidget::showJsonModel(bool b) {
0447     ui.tvJson->setVisible(b);
0448     ui.lField->setVisible(b);
0449 }
0450 
0451 void ImportFileWidget::showOptions(bool b) {
0452     ui.gbOptions->setVisible(b);
0453 
0454     if (m_liveDataSource)
0455         ui.gbUpdateOptions->setVisible(b);
0456 
0457     resize(layout()->minimumSize());
0458 }
0459 
0460 QString ImportFileWidget::fileName() const {
0461     return m_cbFileName->currentText();
0462 }
0463 
0464 QString ImportFileWidget::selectedObject() const {
0465     const QString& path = fileName();
0466 
0467     //determine the file name only
0468     QString name = path.right(path.length() - path.lastIndexOf('/') - 1);
0469 
0470     //strip away the extension if available
0471     if (name.indexOf('.') != -1)
0472         name = name.left(name.lastIndexOf('.'));
0473 
0474     //for multi-dimensional formats like HDF, netCDF and FITS add the currently selected object
0475     const auto format = currentFileType();
0476     if (format == AbstractFileFilter::FileType::HDF5) {
0477         const QStringList& hdf5Names = m_hdf5OptionsWidget->selectedNames();
0478         if (hdf5Names.size())
0479             name += hdf5Names.first(); //the names of the selected HDF5 objects already have '/'
0480     } else if (format == AbstractFileFilter::FileType::NETCDF) {
0481         const QStringList& names = m_netcdfOptionsWidget->selectedNames();
0482         if (names.size())
0483             name += QLatin1Char('/') + names.first();
0484     } else if (format == AbstractFileFilter::FileType::FITS) {
0485         const QString& extensionName = m_fitsOptionsWidget->currentExtensionName();
0486         if (!extensionName.isEmpty())
0487             name += QLatin1Char('/') + extensionName;
0488     } else if (format == AbstractFileFilter::FileType::ROOT) {
0489         const QStringList& names = m_rootOptionsWidget->selectedNames();
0490         if (names.size())
0491             name += QLatin1Char('/') + names.first();
0492     }
0493 
0494     return name;
0495 }
0496 
0497 /*!
0498  * returns \c true if the number of lines to be imported from the currently selected file is zero ("file is empty"),
0499  * returns \c false otherwise.
0500  */
0501 bool ImportFileWidget::isFileEmpty() const {
0502     return m_fileEmpty;
0503 }
0504 
0505 QString ImportFileWidget::host() const {
0506     return ui.leHost->text();
0507 }
0508 
0509 QString ImportFileWidget::port() const {
0510     return ui.lePort->text();
0511 }
0512 
0513 QString ImportFileWidget::serialPort() const {
0514     return ui.cbSerialPort->currentText();
0515 }
0516 
0517 int ImportFileWidget::baudRate() const {
0518     return ui.cbBaudRate->currentText().toInt();
0519 }
0520 
0521 /*!
0522     saves the settings to the data source \c source.
0523 */
0524 void ImportFileWidget::saveSettings(LiveDataSource* source) const {
0525     AbstractFileFilter::FileType fileType = currentFileType();
0526     auto updateType = static_cast<LiveDataSource::UpdateType>(ui.cbUpdateType->currentIndex());
0527     LiveDataSource::SourceType sourceType = currentSourceType();
0528     auto readingType = static_cast<LiveDataSource::ReadingType>(ui.cbReadingType->currentIndex());
0529 
0530     source->setFileType(fileType);
0531     currentFileFilter();
0532     source->setFilter(m_currentFilter.release()); // pass ownership of the filter to the LiveDataSource
0533 
0534     source->setSourceType(sourceType);
0535 
0536     switch (sourceType) {
0537     case LiveDataSource::SourceType::FileOrPipe:
0538         source->setFileName(fileName());
0539         source->setFileLinked(ui.chbLinkFile->isChecked());
0540         source->setComment(fileName());
0541         if (m_liveDataSource)
0542             source->setUseRelativePath(ui.chbRelativePath->isChecked());
0543         break;
0544     case LiveDataSource::SourceType::LocalSocket:
0545         source->setFileName(fileName());
0546         source->setLocalSocketName(fileName());
0547         source->setComment(fileName());
0548         break;
0549     case LiveDataSource::SourceType::NetworkTcpSocket:
0550     case LiveDataSource::SourceType::NetworkUdpSocket:
0551         source->setHost(ui.leHost->text());
0552         source->setPort((quint16)ui.lePort->text().toInt());
0553         break;
0554     case LiveDataSource::SourceType::SerialPort:
0555         source->setBaudRate(ui.cbBaudRate->currentText().toInt());
0556         source->setSerialPort(ui.cbSerialPort->currentText());
0557         break;
0558     case LiveDataSource::SourceType::MQTT:
0559         break;
0560     default:
0561         break;
0562     }
0563 
0564     //reading options
0565     source->setReadingType(readingType);
0566     source->setKeepNValues(ui.sbKeepNValues->value());
0567     source->setUpdateType(updateType);
0568     if (updateType == LiveDataSource::UpdateType::TimeInterval)
0569         source->setUpdateInterval(ui.sbUpdateInterval->value());
0570 
0571     if (readingType != LiveDataSource::ReadingType::TillEnd)
0572         source->setSampleSize(ui.sbSampleSize->value());
0573 }
0574 
0575 /*!
0576     returns the currently used file type.
0577 */
0578 AbstractFileFilter::FileType ImportFileWidget::currentFileType() const {
0579     return static_cast<AbstractFileFilter::FileType>(ui.cbFileType->currentData().toInt());
0580 }
0581 
0582 LiveDataSource::SourceType ImportFileWidget::currentSourceType() const {
0583     return static_cast<LiveDataSource::SourceType>(ui.cbSourceType->currentIndex());
0584 }
0585 
0586 /*!
0587     returns the currently used filter.
0588 */
0589 AbstractFileFilter* ImportFileWidget::currentFileFilter() const {
0590     DEBUG("ImportFileWidget::currentFileFilter()");
0591     AbstractFileFilter::FileType fileType = currentFileType();
0592     if (m_currentFilter && m_currentFilter->type() != fileType)
0593         m_currentFilter.reset();
0594 
0595     switch (fileType) {
0596     case AbstractFileFilter::FileType::Ascii: {
0597         DEBUG(" ASCII");
0598         if (!m_currentFilter)
0599             m_currentFilter.reset(new AsciiFilter);
0600         auto filter = static_cast<AsciiFilter*>(m_currentFilter.get());
0601 
0602         if (ui.cbFilter->currentIndex() == 0)     //"automatic"
0603             filter->setAutoModeEnabled(true);
0604         else if (ui.cbFilter->currentIndex() == 1) { //"custom"
0605             filter->setAutoModeEnabled(false);
0606             if (m_asciiOptionsWidget)
0607                 m_asciiOptionsWidget->applyFilterSettings(filter);
0608         } else
0609             filter->loadFilterSettings(ui.cbFilter->currentText());
0610 
0611         //save the data portion to import
0612         filter->setStartRow(ui.sbStartRow->value());
0613         filter->setEndRow(ui.sbEndRow->value());
0614         filter->setStartColumn(ui.sbStartColumn->value());
0615         filter->setEndColumn(ui.sbEndColumn->value());
0616 
0617         break;
0618     }
0619     case AbstractFileFilter::FileType::Binary: {
0620         DEBUG(" Binary");
0621         if (!m_currentFilter)
0622             m_currentFilter.reset(new BinaryFilter);
0623         auto filter = static_cast<BinaryFilter*>(m_currentFilter.get());
0624         if ( ui.cbFilter->currentIndex() == 0 )     //"automatic"
0625             filter->setAutoModeEnabled(true);
0626         else if (ui.cbFilter->currentIndex() == 1) {    //"custom"
0627             filter->setAutoModeEnabled(false);
0628             if (m_binaryOptionsWidget)
0629                 m_binaryOptionsWidget->applyFilterSettings(filter);
0630         } else {
0631             //TODO: load filter settings
0632             //          filter->setFilterName( ui.cbFilter->currentText() );
0633         }
0634 
0635         filter->setStartRow(ui.sbStartRow->value());
0636         filter->setEndRow(ui.sbEndRow->value());
0637 
0638         break;
0639     }
0640     case AbstractFileFilter::FileType::Image: {
0641         DEBUG(" Image");
0642         if (!m_currentFilter)
0643             m_currentFilter.reset(new ImageFilter);
0644         auto filter = static_cast<ImageFilter*>(m_currentFilter.get());
0645 
0646         filter->setImportFormat(m_imageOptionsWidget->currentFormat());
0647         filter->setStartRow(ui.sbStartRow->value());
0648         filter->setEndRow(ui.sbEndRow->value());
0649         filter->setStartColumn(ui.sbStartColumn->value());
0650         filter->setEndColumn(ui.sbEndColumn->value());
0651 
0652         break;
0653     }
0654     case AbstractFileFilter::FileType::HDF5: {
0655         DEBUG("ImportFileWidget::currentFileFilter(): HDF5");
0656         if (!m_currentFilter)
0657             m_currentFilter.reset(new HDF5Filter);
0658         auto filter = static_cast<HDF5Filter*>(m_currentFilter.get());
0659         QStringList names = selectedHDF5Names();
0660         QDEBUG("ImportFileWidget::currentFileFilter(): selected HDF5 names =" << names);
0661         if (!names.isEmpty())
0662             filter->setCurrentDataSetName(names[0]);
0663         filter->setStartRow(ui.sbStartRow->value());
0664         filter->setEndRow(ui.sbEndRow->value());
0665         filter->setStartColumn(ui.sbStartColumn->value());
0666         filter->setEndColumn(ui.sbEndColumn->value());
0667         DEBUG("ImportFileWidget::currentFileFilter(): OK");
0668 
0669         break;
0670     }
0671     case AbstractFileFilter::FileType::NETCDF: {
0672         DEBUG(" NETCDF");
0673         if (!m_currentFilter)
0674             m_currentFilter.reset(new NetCDFFilter);
0675         auto filter = static_cast<NetCDFFilter*>(m_currentFilter.get());
0676 
0677         if (!selectedNetCDFNames().isEmpty())
0678             filter->setCurrentVarName(selectedNetCDFNames()[0]);
0679         filter->setStartRow(ui.sbStartRow->value());
0680         filter->setEndRow(ui.sbEndRow->value());
0681         filter->setStartColumn(ui.sbStartColumn->value());
0682         filter->setEndColumn(ui.sbEndColumn->value());
0683 
0684         break;
0685     }
0686     case AbstractFileFilter::FileType::FITS: {
0687         DEBUG(" FITS");
0688         if (!m_currentFilter)
0689             m_currentFilter.reset(new FITSFilter);
0690         auto filter = static_cast<FITSFilter*>(m_currentFilter.get());
0691         filter->setStartRow(ui.sbStartRow->value());
0692         filter->setEndRow(ui.sbEndRow->value());
0693         filter->setStartColumn(ui.sbStartColumn->value());
0694         filter->setEndColumn(ui.sbEndColumn->value());
0695 
0696         break;
0697     }
0698     case AbstractFileFilter::FileType::JSON: {
0699         DEBUG(" JSON");
0700         if (!m_currentFilter)
0701             m_currentFilter.reset(new JsonFilter);
0702         auto filter = static_cast<JsonFilter*>(m_currentFilter.get());
0703         m_jsonOptionsWidget->applyFilterSettings(filter, ui.tvJson->currentIndex());
0704 
0705         filter->setStartRow(ui.sbStartRow->value());
0706         filter->setEndRow(ui.sbEndRow->value());
0707         filter->setStartColumn(ui.sbStartColumn->value());
0708         filter->setEndColumn(ui.sbEndColumn->value());
0709 
0710         break;
0711     }
0712     case AbstractFileFilter::FileType::ROOT: {
0713         DEBUG(" ROOT");
0714         if (!m_currentFilter)
0715             m_currentFilter.reset(new ROOTFilter);
0716         auto filter = static_cast<ROOTFilter*>(m_currentFilter.get());
0717         QStringList names = selectedROOTNames();
0718         if (!names.isEmpty())
0719             filter->setCurrentObject(names.first());
0720 
0721         filter->setStartRow(m_rootOptionsWidget->startRow());
0722         filter->setEndRow(m_rootOptionsWidget->endRow());
0723         filter->setColumns(m_rootOptionsWidget->columns());
0724 
0725         break;
0726     }
0727     case AbstractFileFilter::FileType::NgspiceRawAscii: {
0728         DEBUG(" NgspiceRawAscii");
0729         if (!m_currentFilter)
0730             m_currentFilter.reset(new NgspiceRawAsciiFilter);
0731         auto filter = static_cast<NgspiceRawAsciiFilter*>(m_currentFilter.get());
0732         filter->setStartRow(ui.sbStartRow->value());
0733         filter->setEndRow(ui.sbEndRow->value());
0734 
0735         break;
0736     }
0737     case AbstractFileFilter::FileType::NgspiceRawBinary: {
0738         DEBUG(" NgspiceRawBinary");
0739         if (!m_currentFilter)
0740             m_currentFilter.reset(new NgspiceRawBinaryFilter);
0741         auto filter = static_cast<NgspiceRawBinaryFilter*>(m_currentFilter.get());
0742         filter->setStartRow(ui.sbStartRow->value());
0743         filter->setEndRow(ui.sbEndRow->value());
0744 
0745         break;
0746     }
0747     }
0748 
0749     return m_currentFilter.get();
0750 }
0751 
0752 /*!
0753     opens a file dialog and lets the user select the file data source.
0754 */
0755 void ImportFileWidget::selectFile() {
0756     DEBUG("ImportFileWidget::selectFile()")
0757     KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("ImportFileWidget"));
0758     const QString& dir = conf.readEntry(QLatin1String("LastDir"), "");
0759     const QString& path = QFileDialog::getOpenFileName(this, i18n("Select the File Data Source"), dir);
0760     DEBUG(" dir = " << STDSTRING(dir))
0761     DEBUG(" path = " << STDSTRING(path))
0762     if (path.isEmpty()) //cancel was clicked in the file-dialog
0763         return;
0764 
0765     int pos = path.lastIndexOf('/');
0766     if (pos != -1) {
0767         QString newDir = path.left(pos);
0768         if (newDir != dir)
0769             conf.writeEntry(QLatin1String("LastDir"), newDir);
0770     }
0771 
0772     //process all events after the FileDialog was closed to repaint the widget
0773     //before we start calculating the preview
0774     QApplication::processEvents(QEventLoop::AllEvents, 0);
0775 
0776     QStringList urls = m_cbFileName->urls();
0777     urls.insert(0, QUrl::fromLocalFile(path).url()); // add type of path
0778     m_cbFileName->setUrls(urls);
0779     m_cbFileName->setCurrentText(urls.first());
0780     DEBUG(" combobox text = " << STDSTRING(m_cbFileName->currentText()))
0781     fileNameChanged(path); // why do I have to call this function separately
0782 }
0783 
0784 /*!
0785     hides the MQTT related items of the widget
0786 */
0787 void ImportFileWidget::setMQTTVisible(bool visible) {
0788     ui.lConnections->setVisible(visible);
0789     ui.cbConnection->setVisible(visible);
0790     ui.bManageConnections->setVisible(visible);
0791 
0792     //topics
0793     if (ui.cbConnection->currentIndex() != -1 && visible) {
0794         ui.lTopics->setVisible(true);
0795         ui.frameSubscriptions->setVisible(true);
0796 #ifdef HAVE_MQTT
0797         m_subscriptionWidget->setVisible(true);
0798         m_subscriptionWidget->makeVisible(true);
0799 #endif
0800     } else {
0801         ui.lTopics->setVisible(false);
0802         ui.frameSubscriptions->setVisible(false);
0803 #ifdef HAVE_MQTT
0804         m_subscriptionWidget->setVisible(false);
0805         m_subscriptionWidget->makeVisible(false);
0806 #endif
0807     }
0808 
0809     //will message
0810     ui.lLWT->setVisible(visible);
0811     ui.bLWT->setVisible(visible);
0812 }
0813 
0814 /************** SLOTS **************************************************************/
0815 /*!
0816     called on file name changes.
0817     Determines the file format (ASCII, binary etc.), if the file exists,
0818     and activates the corresponding options.
0819 */
0820 void ImportFileWidget::fileNameChanged(const QString& name) {
0821     DEBUG("ImportFileWidget::fileNameChanged() : " << STDSTRING(name))
0822     const QString fileName = absolutePath(name);
0823 
0824     bool fileExists = QFile::exists(fileName);
0825     ui.gbOptions->setEnabled(fileExists);
0826     ui.bManageFilters->setEnabled(fileExists);
0827     ui.cbFilter->setEnabled(fileExists);
0828     ui.cbFileType->setEnabled(fileExists);
0829     ui.bFileInfo->setEnabled(fileExists);
0830     ui.gbUpdateOptions->setEnabled(fileExists);
0831     if (!fileExists) {
0832         //file doesn't exist -> delete the content preview that is still potentially
0833         //available from the previously selected file
0834         ui.tePreview->clear();
0835         m_twPreview->clear();
0836         initOptionsWidget();
0837 
0838         emit fileNameChanged();
0839         return;
0840     }
0841 
0842     if (currentSourceType() == LiveDataSource::SourceType::FileOrPipe) {
0843         const AbstractFileFilter::FileType fileType = AbstractFileFilter::fileType(fileName);
0844         for (int i = 0; i < ui.cbFileType->count(); ++i) {
0845             if (static_cast<AbstractFileFilter::FileType>(ui.cbFileType->itemData(i).toInt()) == fileType) {
0846                 // automatically select a new file type
0847                 if (ui.cbFileType->currentIndex() != i) {
0848                     ui.cbFileType->setCurrentIndex(i); // will call the slot fileTypeChanged which updates content and preview
0849 
0850                     //automatically set the comma separator if a csv file was selected
0851                     if (fileType == AbstractFileFilter::FileType::Ascii && name.endsWith(QLatin1String("csv"), Qt::CaseInsensitive))
0852                         m_asciiOptionsWidget->setSeparatingCharacter(QLatin1Char(','));
0853 
0854                     emit fileNameChanged();
0855                     return;
0856                 } else {
0857                     initOptionsWidget();
0858 
0859                     //automatically set the comma separator if a csv file was selected
0860                     if (fileType == AbstractFileFilter::FileType::Ascii && name.endsWith(QLatin1String("csv"), Qt::CaseInsensitive))
0861                         m_asciiOptionsWidget->setSeparatingCharacter(QLatin1Char(','));
0862 
0863                     updateContent(fileName);
0864                     break;
0865                 }
0866             }
0867         }
0868     }
0869 
0870     emit fileNameChanged();
0871     refreshPreview();
0872 }
0873 
0874 /*!
0875   saves the current filter settings
0876 */
0877 void ImportFileWidget::saveFilter() {
0878     bool ok;
0879     QString text = QInputDialog::getText(this, i18n("Save Filter Settings as"),
0880                                          i18n("Filter name:"), QLineEdit::Normal, i18n("new filter"), &ok);
0881     if (ok && !text.isEmpty()) {
0882         //TODO
0883         //AsciiFilter::saveFilter()
0884     }
0885 }
0886 
0887 /*!
0888   opens a dialog for managing all available predefined filters.
0889 */
0890 void ImportFileWidget::manageFilters() {
0891     //TODO
0892 }
0893 
0894 /*!
0895     Depending on the selected file type, activates the corresponding options in the data portion tab
0896     and populates the combobox with the available pre-defined filter settings for the selected type.
0897 */
0898 void ImportFileWidget::fileTypeChanged(int index) {
0899     Q_UNUSED(index);
0900     AbstractFileFilter::FileType fileType = currentFileType();
0901     DEBUG("ImportFileWidget::fileTypeChanged " << ENUM_TO_STRING(AbstractFileFilter, FileType, fileType));
0902     initOptionsWidget();
0903 
0904     //default
0905     ui.lFilter->show();
0906     ui.cbFilter->show();
0907 
0908     //different file types show different number of tabs in ui.tabWidget.
0909     //when switching from the previous file type we re-set the tab widget to its original state
0910     //and remove/add the required tabs further below
0911     for (int i = 0; i<ui.tabWidget->count(); ++i)
0912         ui.tabWidget->removeTab(0);
0913 
0914     ui.tabWidget->addTab(ui.tabDataFormat, i18n("Data format"));
0915     ui.tabWidget->addTab(ui.tabDataPreview, i18n("Preview"));
0916     if (!m_liveDataSource)
0917         ui.tabWidget->addTab(ui.tabDataPortion, i18n("Data portion to read"));
0918 
0919     ui.lPreviewLines->show();
0920     ui.sbPreviewLines->show();
0921     ui.lStartColumn->show();
0922     ui.sbStartColumn->show();
0923     ui.lEndColumn->show();
0924     ui.sbEndColumn->show();
0925 
0926     showJsonModel(false);
0927 
0928     switch (fileType) {
0929     case AbstractFileFilter::FileType::Ascii:
0930         break;
0931     case AbstractFileFilter::FileType::Binary:
0932         ui.lStartColumn->hide();
0933         ui.sbStartColumn->hide();
0934         ui.lEndColumn->hide();
0935         ui.sbEndColumn->hide();
0936         break;
0937     case AbstractFileFilter::FileType::ROOT:
0938         ui.tabWidget->removeTab(1);
0939     // falls through
0940     case AbstractFileFilter::FileType::HDF5:
0941     case AbstractFileFilter::FileType::NETCDF:
0942     case AbstractFileFilter::FileType::FITS:
0943         ui.lFilter->hide();
0944         ui.cbFilter->hide();
0945         // hide global preview tab. we have our own
0946         ui.tabWidget->setTabText(0, i18n("Data format && preview"));
0947         ui.tabWidget->removeTab(1);
0948         ui.tabWidget->setCurrentIndex(0);
0949         break;
0950     case AbstractFileFilter::FileType::Image:
0951         ui.lFilter->hide();
0952         ui.cbFilter->hide();
0953         ui.lPreviewLines->hide();
0954         ui.sbPreviewLines->hide();
0955         break;
0956     case AbstractFileFilter::FileType::NgspiceRawAscii:
0957     case AbstractFileFilter::FileType::NgspiceRawBinary:
0958         ui.lFilter->hide();
0959         ui.cbFilter->hide();
0960         ui.lStartColumn->hide();
0961         ui.sbStartColumn->hide();
0962         ui.lEndColumn->hide();
0963         ui.sbEndColumn->hide();
0964         ui.tabWidget->removeTab(0);
0965         ui.tabWidget->setCurrentIndex(0);
0966         break;
0967     case AbstractFileFilter::FileType::JSON:
0968         ui.lFilter->hide();
0969         ui.cbFilter->hide();
0970         showJsonModel(true);
0971         break;
0972     default:
0973         DEBUG("unknown file type");
0974     }
0975 
0976     int lastUsedFilterIndex = ui.cbFilter->currentIndex();
0977     ui.cbFilter->clear();
0978     ui.cbFilter->addItem( i18n("Automatic") );
0979     ui.cbFilter->addItem( i18n("Custom") );
0980 
0981     //TODO: populate the combobox with the available pre-defined filter settings for the selected type
0982     ui.cbFilter->setCurrentIndex(lastUsedFilterIndex);
0983     filterChanged(lastUsedFilterIndex);
0984 
0985     if (currentSourceType() == LiveDataSource::SourceType::FileOrPipe) {
0986         QString tempFileName = fileName();
0987         const QString& fileName = absolutePath(tempFileName);
0988         if (QFile::exists(fileName))
0989             updateContent(fileName);
0990     }
0991 
0992     //for file types other than ASCII and binary we support re-reading the whole file only
0993     //select "read whole file" and deactivate the combobox
0994     if (m_liveDataSource && (fileType != AbstractFileFilter::FileType::Ascii && fileType != AbstractFileFilter::FileType::Binary)) {
0995         ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::WholeFile));
0996         ui.cbReadingType->setEnabled(false);
0997     } else
0998         ui.cbReadingType->setEnabled(true);
0999 
1000     refreshPreview();
1001 }
1002 
1003 // file type specific option widgets
1004 void ImportFileWidget::initOptionsWidget() {
1005     DEBUG("ImportFileWidget::initOptionsWidget for " << ENUM_TO_STRING(AbstractFileFilter, FileType, currentFileType()));
1006     switch (currentFileType()) {
1007     case AbstractFileFilter::FileType::Ascii: {
1008         if (!m_asciiOptionsWidget) {
1009             QWidget* asciiw = new QWidget();
1010             m_asciiOptionsWidget = std::unique_ptr<AsciiOptionsWidget>(new AsciiOptionsWidget(asciiw));
1011             m_asciiOptionsWidget->loadSettings();
1012 
1013             //allow to add timestamp column for live data sources
1014             if (m_liveDataSource)
1015                 m_asciiOptionsWidget->showTimestampOptions(true);
1016             ui.swOptions->addWidget(asciiw);
1017         }
1018 
1019         ui.swOptions->setCurrentWidget(m_asciiOptionsWidget->parentWidget());
1020         break;
1021     }
1022     case AbstractFileFilter::FileType::Binary:
1023         if (!m_binaryOptionsWidget) {
1024             QWidget* binaryw = new QWidget();
1025             m_binaryOptionsWidget = std::unique_ptr<BinaryOptionsWidget>(new BinaryOptionsWidget(binaryw));
1026             ui.swOptions->addWidget(binaryw);
1027             m_binaryOptionsWidget->loadSettings();
1028         }
1029         ui.swOptions->setCurrentWidget(m_binaryOptionsWidget->parentWidget());
1030         break;
1031     case AbstractFileFilter::FileType::Image:
1032         if (!m_imageOptionsWidget) {
1033             QWidget* imagew = new QWidget();
1034             m_imageOptionsWidget = std::unique_ptr<ImageOptionsWidget>(new ImageOptionsWidget(imagew));
1035             ui.swOptions->addWidget(imagew);
1036             m_imageOptionsWidget->loadSettings();
1037         }
1038         ui.swOptions->setCurrentWidget(m_imageOptionsWidget->parentWidget());
1039         break;
1040     case AbstractFileFilter::FileType::HDF5:
1041         if (!m_hdf5OptionsWidget) {
1042             QWidget* hdf5w = new QWidget();
1043             m_hdf5OptionsWidget = std::unique_ptr<HDF5OptionsWidget>(new HDF5OptionsWidget(hdf5w, this));
1044             ui.swOptions->addWidget(hdf5w);
1045         } else
1046             m_hdf5OptionsWidget->clear();
1047         ui.swOptions->setCurrentWidget(m_hdf5OptionsWidget->parentWidget());
1048         break;
1049     case AbstractFileFilter::FileType::NETCDF:
1050         if (!m_netcdfOptionsWidget) {
1051             QWidget* netcdfw = new QWidget();
1052             m_netcdfOptionsWidget = std::unique_ptr<NetCDFOptionsWidget>(new NetCDFOptionsWidget(netcdfw, this));
1053             ui.swOptions->insertWidget(static_cast<int>(AbstractFileFilter::FileType::NETCDF), netcdfw);
1054         } else
1055             m_netcdfOptionsWidget->clear();
1056         ui.swOptions->setCurrentWidget(m_netcdfOptionsWidget->parentWidget());
1057         break;
1058     case AbstractFileFilter::FileType::FITS:
1059         if (!m_fitsOptionsWidget) {
1060             QWidget* fitsw = new QWidget();
1061             m_fitsOptionsWidget = std::unique_ptr<FITSOptionsWidget>(new FITSOptionsWidget(fitsw, this));
1062             ui.swOptions->addWidget(fitsw);
1063         } else
1064             m_fitsOptionsWidget->clear();
1065         ui.swOptions->setCurrentWidget(m_fitsOptionsWidget->parentWidget());
1066         break;
1067     case AbstractFileFilter::FileType::JSON:
1068         if (!m_jsonOptionsWidget) {
1069             QWidget* jsonw = new QWidget();
1070             m_jsonOptionsWidget = std::unique_ptr<JsonOptionsWidget>(new JsonOptionsWidget(jsonw, this));
1071             ui.tvJson->setModel(m_jsonOptionsWidget->model());
1072             ui.swOptions->addWidget(jsonw);
1073             m_jsonOptionsWidget->loadSettings();
1074         } else
1075             m_jsonOptionsWidget->clearModel();
1076         ui.swOptions->setCurrentWidget(m_jsonOptionsWidget->parentWidget());
1077         showJsonModel(true);
1078         break;
1079     case AbstractFileFilter::FileType::ROOT:
1080         if (!m_rootOptionsWidget) {
1081             QWidget* rootw = new QWidget();
1082             m_rootOptionsWidget = std::unique_ptr<ROOTOptionsWidget>(new ROOTOptionsWidget(rootw, this));
1083             ui.swOptions->addWidget(rootw);
1084         } else
1085             m_rootOptionsWidget->clear();
1086         ui.swOptions->setCurrentWidget(m_rootOptionsWidget->parentWidget());
1087         break;
1088     case AbstractFileFilter::FileType::NgspiceRawAscii:
1089     case AbstractFileFilter::FileType::NgspiceRawBinary:
1090         break;
1091     }
1092 }
1093 
1094 const QStringList ImportFileWidget::selectedHDF5Names() const {
1095     return m_hdf5OptionsWidget->selectedNames();
1096 }
1097 
1098 const QStringList ImportFileWidget::selectedNetCDFNames() const {
1099     return m_netcdfOptionsWidget->selectedNames();
1100 }
1101 
1102 const QStringList ImportFileWidget::selectedFITSExtensions() const {
1103     return m_fitsOptionsWidget->selectedExtensions();
1104 }
1105 
1106 const QStringList ImportFileWidget::selectedROOTNames() const {
1107     return m_rootOptionsWidget->selectedNames();
1108 }
1109 
1110 /*!
1111     shows the dialog with the information about the file(s) to be imported.
1112 */
1113 void ImportFileWidget::fileInfoDialog() {
1114     QStringList files = fileName().split(';');
1115     auto* dlg = new FileInfoDialog(this);
1116     dlg->setFiles(files);
1117     dlg->exec();
1118 }
1119 
1120 /*!
1121     enables the options if the filter "custom" was chosen. Disables the options otherwise.
1122 */
1123 void ImportFileWidget::filterChanged(int index) {
1124     // ignore filter for these formats
1125     AbstractFileFilter::FileType fileType = currentFileType();
1126     if (fileType != AbstractFileFilter::FileType::Ascii && fileType != AbstractFileFilter::FileType::Binary) {
1127         ui.swOptions->setEnabled(true);
1128         return;
1129     }
1130 
1131     if (index == 0) { // "automatic"
1132         ui.swOptions->setEnabled(false);
1133         ui.bSaveFilter->setEnabled(false);
1134     } else if (index == 1) { //custom
1135         ui.swOptions->setEnabled(true);
1136         ui.bSaveFilter->setEnabled(true);
1137     } else {
1138         // predefined filter settings were selected.
1139         //load and show them in the GUI.
1140         //TODO
1141     }
1142 }
1143 
1144 void ImportFileWidget::refreshPreview() {
1145     //don't generate any preview if it was explicitly suppressed
1146     //or if the options box together with the preview widget is not visible
1147     if (m_suppressRefresh || !ui.gbOptions->isVisible())
1148         return;
1149 
1150     DEBUG("ImportFileWidget::refreshPreview()");
1151     WAIT_CURSOR;
1152 
1153     QString tempFileName = fileName();
1154     QString fileName = absolutePath(tempFileName);
1155     AbstractFileFilter::FileType fileType = currentFileType();
1156     LiveDataSource::SourceType sourceType = currentSourceType();
1157     int lines = ui.sbPreviewLines->value();
1158 
1159     if (sourceType == LiveDataSource::SourceType::FileOrPipe)
1160         DEBUG(" file name = " << STDSTRING(fileName));
1161 
1162     // generic table widget
1163     if (fileType == AbstractFileFilter::FileType::Ascii || fileType == AbstractFileFilter::FileType::Binary
1164             || fileType == AbstractFileFilter::FileType::JSON || fileType == AbstractFileFilter::FileType::NgspiceRawAscii
1165             || fileType == AbstractFileFilter::FileType::NgspiceRawBinary)
1166         m_twPreview->show();
1167     else
1168         m_twPreview->hide();
1169 
1170     bool ok = true;
1171     QTableWidget* tmpTableWidget = m_twPreview;
1172     QVector<QStringList> importedStrings;
1173     QStringList vectorNameList;
1174     QVector<AbstractColumn::ColumnMode> columnModes;
1175     DEBUG("Data File Type: " << ENUM_TO_STRING(AbstractFileFilter, FileType, fileType));
1176     switch (fileType) {
1177     case AbstractFileFilter::FileType::Ascii: {
1178         ui.tePreview->clear();
1179 
1180         auto filter = static_cast<AsciiFilter*>(currentFileFilter());
1181 
1182         DEBUG("Data Source Type: " << ENUM_TO_STRING(LiveDataSource, SourceType, sourceType));
1183         switch (sourceType) {
1184         case LiveDataSource::SourceType::FileOrPipe: {
1185             importedStrings = filter->preview(fileName, lines);
1186             break;
1187         }
1188         case LiveDataSource::SourceType::LocalSocket: {
1189             QLocalSocket lsocket{this};
1190             DEBUG("Local socket: CONNECT PREVIEW");
1191             lsocket.connectToServer(fileName, QLocalSocket::ReadOnly);
1192             if (lsocket.waitForConnected()) {
1193                 DEBUG("connected to local socket " << STDSTRING(fileName));
1194                 if (lsocket.waitForReadyRead())
1195                     importedStrings = filter->preview(lsocket);
1196                 DEBUG("Local socket: DISCONNECT PREVIEW");
1197                 lsocket.disconnectFromServer();
1198                 // read-only socket is disconnected immediately (no waitForDisconnected())
1199             } else
1200                 DEBUG("failed connect to local socket " << STDSTRING(fileName) << " - " << STDSTRING(lsocket.errorString()));
1201 
1202             break;
1203         }
1204         case LiveDataSource::SourceType::NetworkTcpSocket: {
1205             QTcpSocket tcpSocket{this};
1206             tcpSocket.connectToHost(host(), port().toInt(), QTcpSocket::ReadOnly);
1207             if (tcpSocket.waitForConnected()) {
1208                 DEBUG("connected to TCP socket");
1209                 if ( tcpSocket.waitForReadyRead() )
1210                     importedStrings = filter->preview(tcpSocket);
1211 
1212                 tcpSocket.disconnectFromHost();
1213             } else
1214                 DEBUG("failed to connect to TCP socket " << " - " << STDSTRING(tcpSocket.errorString()));
1215 
1216             break;
1217         }
1218         case LiveDataSource::SourceType::NetworkUdpSocket: {
1219             QUdpSocket udpSocket{this};
1220             DEBUG("UDP Socket: CONNECT PREVIEW, state = " << udpSocket.state());
1221             udpSocket.bind(QHostAddress(host()), port().toInt());
1222             udpSocket.connectToHost(host(), 0, QUdpSocket::ReadOnly);
1223             if (udpSocket.waitForConnected()) {
1224                 DEBUG(" connected to UDP socket " << STDSTRING(host()) << ':' << port().toInt());
1225                 if (!udpSocket.waitForReadyRead(2000) )
1226                     DEBUG(" ERROR: not ready for read after 2 sec");
1227                 if (udpSocket.hasPendingDatagrams()) {
1228                     DEBUG(" has pending data");
1229                 } else {
1230                     DEBUG(" has no pending data");
1231                 }
1232                 importedStrings = filter->preview(udpSocket);
1233 
1234                 DEBUG("UDP Socket: DISCONNECT PREVIEW, state = " << udpSocket.state());
1235                 udpSocket.disconnectFromHost();
1236             } else
1237                 DEBUG("failed to connect to UDP socket " << " - " << STDSTRING(udpSocket.errorString()));
1238 
1239             break;
1240         }
1241         case LiveDataSource::SourceType::SerialPort: {
1242 #ifdef HAVE_QTSERIALPORT
1243             QSerialPort sPort{this};
1244             DEBUG(" Port: " << STDSTRING(serialPort()) << ", Settings: " << baudRate() << ',' << sPort.dataBits()
1245                   << ',' << sPort.parity() << ',' << sPort.stopBits());
1246             sPort.setPortName(serialPort());
1247             sPort.setBaudRate(baudRate());
1248 
1249             if (sPort.open(QIODevice::ReadOnly)) {
1250                 if (sPort.waitForReadyRead(2000))
1251                     importedStrings = filter->preview(sPort);
1252                 else
1253                     DEBUG(" ERROR: not ready for read after 2 sec");
1254 
1255                 sPort.close();
1256             } else
1257                 DEBUG(" ERROR: failed to open serial port. error: " << sPort.error());
1258 #endif
1259             break;
1260         }
1261         case LiveDataSource::SourceType::MQTT: {
1262 #ifdef HAVE_MQTT
1263             //show the preview for the currently selected topic
1264             auto* item = m_subscriptionWidget->currentItem();
1265             if (item && item->childCount() == 0) { //only preview if the lowest level (i.e. a topic) is selected
1266                 const QString& topicName = item->text(0);
1267                 auto i = m_lastMessage.find(topicName);
1268                 if (i != m_lastMessage.end())
1269                     importedStrings = filter->preview(i.value().payload().data());
1270                 else
1271                     importedStrings << QStringList{i18n("No data arrived yet for the selected topic")};
1272             }
1273 #endif
1274             break;
1275         }
1276         }
1277 
1278         vectorNameList = filter->vectorNames();
1279         columnModes = filter->columnModes();
1280         break;
1281     }
1282     case AbstractFileFilter::FileType::Binary: {
1283         ui.tePreview->clear();
1284         auto filter = static_cast<BinaryFilter*>(currentFileFilter());
1285         importedStrings = filter->preview(fileName, lines);
1286         break;
1287     }
1288     case AbstractFileFilter::FileType::Image: {
1289         ui.tePreview->clear();
1290 
1291         QImage image(fileName);
1292         QTextCursor cursor = ui.tePreview->textCursor();
1293         cursor.insertImage(image);
1294         RESET_CURSOR;
1295         return;
1296     }
1297     case AbstractFileFilter::FileType::HDF5: {
1298         DEBUG("ImportFileWidget::refreshPreview: HDF5");
1299         auto filter = static_cast<HDF5Filter*>(currentFileFilter());
1300         lines = m_hdf5OptionsWidget->lines();
1301 
1302         importedStrings = filter->readCurrentDataSet(fileName, nullptr, ok, AbstractFileFilter::ImportMode::Replace, lines);
1303         tmpTableWidget = m_hdf5OptionsWidget->previewWidget();
1304         break;
1305     }
1306     case AbstractFileFilter::FileType::NETCDF: {
1307         auto filter = static_cast<NetCDFFilter*>(currentFileFilter());
1308         lines = m_netcdfOptionsWidget->lines();
1309 
1310         importedStrings = filter->readCurrentVar(fileName, nullptr, AbstractFileFilter::ImportMode::Replace, lines);
1311         tmpTableWidget = m_netcdfOptionsWidget->previewWidget();
1312         break;
1313     }
1314     case AbstractFileFilter::FileType::FITS: {
1315         auto filter = static_cast<FITSFilter*>(currentFileFilter());
1316         lines = m_fitsOptionsWidget->lines();
1317 
1318         QString extensionName = m_fitsOptionsWidget->extensionName(&ok);
1319         if (!extensionName.isEmpty()) {
1320             DEBUG(" extension name = " << STDSTRING(extensionName));
1321             fileName = extensionName;
1322         }
1323 
1324         bool readFitsTableToMatrix;
1325         importedStrings = filter->readChdu(fileName, &readFitsTableToMatrix, lines);
1326         emit checkedFitsTableToMatrix(readFitsTableToMatrix);
1327 
1328         tmpTableWidget = m_fitsOptionsWidget->previewWidget();
1329         break;
1330     }
1331     case AbstractFileFilter::FileType::JSON: {
1332         ui.tePreview->clear();
1333         auto filter = static_cast<JsonFilter*>(currentFileFilter());
1334         m_jsonOptionsWidget->applyFilterSettings(filter, ui.tvJson->currentIndex());
1335         importedStrings = filter->preview(fileName, lines);
1336 
1337         vectorNameList = filter->vectorNames();
1338         columnModes = filter->columnModes();
1339         break;
1340     }
1341     case AbstractFileFilter::FileType::ROOT: {
1342         auto filter = static_cast<ROOTFilter*>(currentFileFilter());
1343         lines = m_rootOptionsWidget->lines();
1344         m_rootOptionsWidget->setNRows(filter->rowsInCurrentObject(fileName));
1345         importedStrings = filter->previewCurrentObject(
1346                               fileName,
1347                               m_rootOptionsWidget->startRow(),
1348                               qMin(m_rootOptionsWidget->startRow() + lines - 1,
1349                                    m_rootOptionsWidget->endRow())
1350                           );
1351         tmpTableWidget = m_rootOptionsWidget->previewWidget();
1352         // the last vector element contains the column names
1353         vectorNameList = importedStrings.last();
1354         importedStrings.removeLast();
1355         columnModes = QVector<AbstractColumn::ColumnMode>(vectorNameList.size(), AbstractColumn::ColumnMode::Numeric);
1356         break;
1357     }
1358     case AbstractFileFilter::FileType::NgspiceRawAscii: {
1359         ui.tePreview->clear();
1360         auto filter = static_cast<NgspiceRawAsciiFilter*>(currentFileFilter());
1361         importedStrings = filter->preview(fileName, lines);
1362         vectorNameList = filter->vectorNames();
1363         columnModes = filter->columnModes();
1364         break;
1365     }
1366     case AbstractFileFilter::FileType::NgspiceRawBinary: {
1367         ui.tePreview->clear();
1368         auto filter = static_cast<NgspiceRawBinaryFilter*>(currentFileFilter());
1369         importedStrings = filter->preview(fileName, lines);
1370         vectorNameList = filter->vectorNames();
1371         columnModes = filter->columnModes();
1372         break;
1373     }
1374     }
1375 
1376     // fill the table widget
1377     tmpTableWidget->setRowCount(0);
1378     tmpTableWidget->setColumnCount(0);
1379     if ( !importedStrings.isEmpty() ) {
1380         if (!ok) {
1381             // show imported strings as error message
1382             tmpTableWidget->setRowCount(1);
1383             tmpTableWidget->setColumnCount(1);
1384             auto* item = new QTableWidgetItem();
1385             item->setText(importedStrings[0][0]);
1386             tmpTableWidget->setItem(0, 0, item);
1387         } else {
1388             //TODO: maxrows not used
1389             const int rows = qMax(importedStrings.size(), 1);
1390             const int maxColumns = 300;
1391             tmpTableWidget->setRowCount(rows);
1392 
1393             for (int i = 0; i < rows; ++i) {
1394                 const int cols = importedStrings[i].size() > maxColumns ? maxColumns : importedStrings[i].size();
1395                 if (cols > tmpTableWidget->columnCount())
1396                     tmpTableWidget->setColumnCount(cols);
1397 
1398                 for (int j = 0; j < cols; ++j) {
1399                     auto* item = new QTableWidgetItem(importedStrings[i][j]);
1400                     tmpTableWidget->setItem(i, j, item);
1401                 }
1402             }
1403 
1404             // set header if columnMode available
1405             for (int i = 0; i < qMin(tmpTableWidget->columnCount(), columnModes.size()); ++i) {
1406                 QString columnName = QString::number(i+1);
1407                 if (i < vectorNameList.size())
1408                     columnName = vectorNameList[i];
1409 
1410                 auto* item = new QTableWidgetItem(columnName + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, columnModes[i]) + QLatin1String("}"));
1411                 item->setTextAlignment(Qt::AlignLeft);
1412                 item->setIcon(AbstractColumn::iconForMode(columnModes[i]));
1413 
1414                 tmpTableWidget->setHorizontalHeaderItem(i, item);
1415             }
1416         }
1417 
1418         tmpTableWidget->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
1419         m_fileEmpty = false;
1420     } else
1421         m_fileEmpty = true;
1422 
1423     RESET_CURSOR;
1424 }
1425 
1426 void ImportFileWidget::updateContent(const QString& fileName) {
1427     if (m_suppressRefresh)
1428         return;
1429 
1430     QApplication::processEvents(QEventLoop::AllEvents, 0);
1431     WAIT_CURSOR;
1432 
1433     QDEBUG("ImportFileWidget::updateContent(): file name = " << fileName);
1434     if (auto filter = currentFileFilter()) {
1435         switch (filter->type()) {
1436         case AbstractFileFilter::FileType::HDF5:
1437             m_hdf5OptionsWidget->updateContent(static_cast<HDF5Filter*>(filter), fileName);
1438             break;
1439         case AbstractFileFilter::FileType::NETCDF:
1440             m_netcdfOptionsWidget->updateContent(static_cast<NetCDFFilter*>(filter), fileName);
1441             break;
1442         case AbstractFileFilter::FileType::FITS:
1443 #ifdef HAVE_FITS
1444             m_fitsOptionsWidget->updateContent(static_cast<FITSFilter*>(filter), fileName);
1445 #endif
1446             break;
1447         case AbstractFileFilter::FileType::ROOT:
1448             m_rootOptionsWidget->updateContent(static_cast<ROOTFilter*>(filter), fileName);
1449             break;
1450         case AbstractFileFilter::FileType::JSON:
1451             m_jsonOptionsWidget->loadDocument(fileName);
1452             ui.tvJson->setExpanded( m_jsonOptionsWidget->model()->index(0, 0), true); //expand the root node
1453             break;
1454         case AbstractFileFilter::FileType::Ascii:
1455         case AbstractFileFilter::FileType::Binary:
1456         case AbstractFileFilter::FileType::Image:
1457         case AbstractFileFilter::FileType::NgspiceRawAscii:
1458         case AbstractFileFilter::FileType::NgspiceRawBinary:
1459             break;
1460         }
1461     }
1462     RESET_CURSOR;
1463 }
1464 
1465 void ImportFileWidget::updateTypeChanged(int idx) {
1466     const auto UpdateType = static_cast<LiveDataSource::UpdateType>(idx);
1467 
1468     switch (UpdateType) {
1469     case LiveDataSource::UpdateType::TimeInterval:
1470         ui.lUpdateInterval->show();
1471         ui.sbUpdateInterval->show();
1472         break;
1473     case LiveDataSource::UpdateType::NewData:
1474         ui.lUpdateInterval->hide();
1475         ui.sbUpdateInterval->hide();
1476     }
1477 }
1478 
1479 void ImportFileWidget::readingTypeChanged(int idx) {
1480     const auto readingType = static_cast<LiveDataSource::ReadingType>(idx);
1481     const LiveDataSource::SourceType sourceType = currentSourceType();
1482 
1483     if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::LocalSocket
1484             || sourceType == LiveDataSource::SourceType::SerialPort
1485             || readingType == LiveDataSource::ReadingType::TillEnd || readingType == LiveDataSource::ReadingType::WholeFile) {
1486         ui.lSampleSize->hide();
1487         ui.sbSampleSize->hide();
1488     } else {
1489         ui.lSampleSize->show();
1490         ui.sbSampleSize->show();
1491     }
1492 
1493     if (readingType == LiveDataSource::ReadingType::WholeFile) {
1494         ui.lKeepLastValues->hide();
1495         ui.sbKeepNValues->hide();
1496     } else {
1497         ui.lKeepLastValues->show();
1498         ui.sbKeepNValues->show();
1499     }
1500 }
1501 
1502 void ImportFileWidget::sourceTypeChanged(int idx) {
1503     const auto sourceType = static_cast<LiveDataSource::SourceType>(idx);
1504 
1505 #ifdef HAVE_MQTT
1506     //when switching from mqtt to another source type, make sure we disconnect from
1507     //the current broker, if connected, in order not to get any notification anymore
1508     if (sourceType != LiveDataSource::SourceType::MQTT)
1509         disconnectMqttConnection();
1510 #endif
1511 
1512     // enable/disable "on new data"-option
1513     const auto* model = qobject_cast<const QStandardItemModel*>(ui.cbUpdateType->model());
1514     QStandardItem* item = model->item(static_cast<int>(LiveDataSource::UpdateType::NewData));
1515 
1516     switch (sourceType) {
1517     case LiveDataSource::SourceType::FileOrPipe:
1518         ui.lFileName->show();
1519         m_cbFileName->show();
1520         ui.bFileInfo->show();
1521         ui.bOpen->show();
1522         if (m_liveDataSource) {
1523             ui.lRelativePath->show();
1524             ui.chbRelativePath->show();
1525         }
1526         ui.chbLinkFile->show();
1527 
1528         //option for sample size are available for "continuously fixed" and "from end" reading options
1529         if (ui.cbReadingType->currentIndex() < 2) {
1530             ui.lSampleSize->show();
1531             ui.sbSampleSize->show();
1532         } else {
1533             ui.lSampleSize->hide();
1534             ui.sbSampleSize->hide();
1535         }
1536 
1537         ui.cbBaudRate->hide();
1538         ui.lBaudRate->hide();
1539         ui.lHost->hide();
1540         ui.leHost->hide();
1541         ui.lPort->hide();
1542         ui.lePort->hide();
1543         ui.cbSerialPort->hide();
1544         ui.lSerialPort->hide();
1545 
1546         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1547 
1548         fileNameChanged(fileName());
1549         ui.cbFileType->show();
1550         ui.lFileType->show();
1551         setMQTTVisible(false);
1552         break;
1553     case LiveDataSource::SourceType::NetworkTcpSocket:
1554     case LiveDataSource::SourceType::NetworkUdpSocket:
1555         ui.lHost->show();
1556         ui.leHost->show();
1557         ui.lePort->show();
1558         ui.lPort->show();
1559         if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket) {
1560             ui.lSampleSize->hide();
1561             ui.sbSampleSize->hide();
1562         } else {
1563             ui.lSampleSize->show();
1564             ui.sbSampleSize->show();
1565         }
1566 
1567         ui.lBaudRate->hide();
1568         ui.cbBaudRate->hide();
1569         ui.lSerialPort->hide();
1570         ui.cbSerialPort->hide();
1571 
1572         ui.lFileName->hide();
1573         m_cbFileName->hide();
1574         ui.bFileInfo->hide();
1575         ui.bOpen->hide();
1576         ui.lRelativePath->hide();
1577         ui.chbRelativePath->hide();
1578         ui.chbLinkFile->hide();
1579 
1580         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
1581 
1582         ui.gbOptions->setEnabled(true);
1583         ui.bManageFilters->setEnabled(true);
1584         ui.cbFilter->setEnabled(true);
1585         ui.cbFileType->setEnabled(true);
1586         ui.cbFileType->show();
1587         ui.lFileType->show();
1588         setMQTTVisible(false);
1589         break;
1590     case LiveDataSource::SourceType::LocalSocket:
1591         ui.lFileName->show();
1592         m_cbFileName->show();
1593         ui.bFileInfo->hide();
1594         ui.bOpen->show();
1595         ui.lRelativePath->hide();
1596         ui.chbRelativePath->hide();
1597 
1598         ui.lSampleSize->hide();
1599         ui.sbSampleSize->hide();
1600         ui.cbBaudRate->hide();
1601         ui.lBaudRate->hide();
1602         ui.lHost->hide();
1603         ui.leHost->hide();
1604         ui.lPort->hide();
1605         ui.lePort->hide();
1606         ui.cbSerialPort->hide();
1607         ui.lSerialPort->hide();
1608         ui.chbLinkFile->hide();
1609 
1610         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1611 
1612         ui.gbOptions->setEnabled(true);
1613         ui.bManageFilters->setEnabled(true);
1614         ui.cbFilter->setEnabled(true);
1615         ui.cbFileType->setEnabled(true);
1616         ui.cbFileType->show();
1617         ui.lFileType->show();
1618         setMQTTVisible(false);
1619         break;
1620     case LiveDataSource::SourceType::SerialPort:
1621         ui.lBaudRate->show();
1622         ui.cbBaudRate->show();
1623         ui.lSerialPort->show();
1624         ui.cbSerialPort->show();
1625         ui.lSampleSize->show();
1626         ui.sbSampleSize->show();
1627 
1628         ui.lHost->hide();
1629         ui.leHost->hide();
1630         ui.lePort->hide();
1631         ui.lPort->hide();
1632 
1633         ui.lFileName->hide();
1634         m_cbFileName->hide();
1635         ui.bFileInfo->hide();
1636         ui.bOpen->hide();
1637         ui.lRelativePath->hide();
1638         ui.chbRelativePath->hide();
1639         ui.chbLinkFile->hide();
1640 
1641         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
1642 
1643         ui.cbFileType->setEnabled(true);
1644         ui.cbFileType->show();
1645         ui.gbOptions->setEnabled(true);
1646         ui.bManageFilters->setEnabled(true);
1647         ui.cbFilter->setEnabled(true);
1648         ui.lFileType->show();
1649         setMQTTVisible(false);
1650         break;
1651     case LiveDataSource::SourceType::MQTT:
1652 #ifdef HAVE_MQTT
1653         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1654 
1655         //for MQTT we read ascii data only, hide the file type options
1656         for (int i = 0; i < ui.cbFileType->count(); ++i) {
1657             if (static_cast<AbstractFileFilter::FileType>(ui.cbFileType->itemData(i).toInt()) == AbstractFileFilter::FileType::Ascii) {
1658                 if (ui.cbFileType->currentIndex() == i)
1659                     initOptionsWidget();
1660                 else
1661                     ui.cbFileType->setCurrentIndex(i);
1662 
1663                 break;
1664             }
1665         }
1666         ui.cbFileType->hide();
1667         ui.lFileType->hide();
1668 
1669         ui.lBaudRate->hide();
1670         ui.cbBaudRate->hide();
1671         ui.lSerialPort->hide();
1672         ui.cbSerialPort->hide();
1673         ui.lHost->hide();
1674         ui.leHost->hide();
1675         ui.lPort->hide();
1676         ui.lePort->hide();
1677         ui.lFileName->hide();
1678         m_cbFileName->hide();
1679         ui.bFileInfo->hide();
1680         ui.bOpen->hide();
1681         ui.lRelativePath->hide();
1682         ui.chbRelativePath->hide();
1683         ui.chbLinkFile->hide();
1684 
1685         setMQTTVisible(true);
1686 
1687         ui.cbFileType->setEnabled(true);
1688         ui.gbOptions->setEnabled(true);
1689         ui.bManageFilters->setEnabled(true);
1690         ui.cbFilter->setEnabled(true);
1691 
1692         //in case there are already connections defined,
1693         //show the available topics for the currently selected connection
1694         mqttConnectionChanged();
1695 #endif
1696         break;
1697     }
1698 
1699     //deactivate/activate options that are specific to file of pipe sources only
1700     auto* typeModel = qobject_cast<const QStandardItemModel*>(ui.cbFileType->model());
1701     if (sourceType != LiveDataSource::SourceType::FileOrPipe) {
1702         //deactivate file types other than ascii and binary
1703         for (int i = 2; i < ui.cbFileType->count(); ++i)
1704             typeModel->item(i)->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
1705         if (ui.cbFileType->currentIndex() > 1)
1706             ui.cbFileType->setCurrentIndex(1);
1707 
1708         //"whole file" read option is available for file or pipe only, disable it
1709         typeModel = qobject_cast<const QStandardItemModel*>(ui.cbReadingType->model());
1710         QStandardItem* item = typeModel->item(static_cast<int>(LiveDataSource::ReadingType::WholeFile));
1711         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
1712         if (static_cast<LiveDataSource::ReadingType>(ui.cbReadingType->currentIndex()) == LiveDataSource::ReadingType::WholeFile)
1713             ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::TillEnd));
1714 
1715         //"update options" groupbox can be deactivated for "file and pipe" if the file is invalid.
1716         //Activate the groupbox when switching from "file and pipe" to a different source type.
1717         ui.gbUpdateOptions->setEnabled(true);
1718     } else {
1719         for (int i = 2; i < ui.cbFileType->count(); ++i)
1720             typeModel->item(i)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1721 
1722         //enable "whole file" item for file or pipe
1723         typeModel = qobject_cast<const QStandardItemModel*>(ui.cbReadingType->model());
1724         QStandardItem* item = typeModel->item(static_cast<int>(LiveDataSource::ReadingType::WholeFile));
1725         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1726     }
1727 
1728     //disable the header options for non-file sources because:
1729     //* for sockets we allow to import one single value only at the moment
1730     //* for MQTT topics we don't allow to set the vector names since the different topics can have different number of columns
1731     //For files this option still can be usefull if the user have to re-read the whole file
1732     //and wants to use the header to set the column names or the user provides manually the column names.
1733     //TODO: adjust this logic later once we allow to import multiple columns from sockets,
1734     //it should be possible to provide the names of the columns
1735     bool visible = (currentSourceType() == LiveDataSource::SourceType::FileOrPipe);
1736     if (m_asciiOptionsWidget)
1737         m_asciiOptionsWidget->showAsciiHeaderOptions(visible);
1738 
1739     emit sourceTypeChanged();
1740     refreshPreview();
1741 }
1742 
1743 #ifdef HAVE_MQTT
1744 
1745 /*!
1746  *\brief called when a different MQTT connection is selected in the connection ComboBox.
1747  * connects to the MQTT broker according to the connection settings.
1748  */
1749 void ImportFileWidget::mqttConnectionChanged() {
1750     if (m_initialisingMQTT || ui.cbConnection->currentIndex() == -1) {
1751         ui.lLWT->hide();
1752         ui.bLWT->hide();
1753         ui.lTopics->hide();
1754         return;
1755     }
1756 
1757     WAIT_CURSOR;
1758     emit error(QString());
1759 
1760     //disconnected from the broker that was selected before
1761     disconnectMqttConnection();
1762 
1763     //determine the connection settings for the new broker and initialize the mqtt client
1764     KConfig config(m_configPath, KConfig::SimpleConfig);
1765     KConfigGroup group = config.group(ui.cbConnection->currentText());
1766 
1767     m_client = new QMqttClient;
1768     connect(m_client, &QMqttClient::connected, this, &ImportFileWidget::onMqttConnect);
1769     connect(m_client, &QMqttClient::disconnected, this, &ImportFileWidget::onMqttDisconnect);
1770     connect(m_client, &QMqttClient::messageReceived, this, &ImportFileWidget::mqttMessageReceived);
1771     connect(m_client, &QMqttClient::errorChanged, this, &ImportFileWidget::mqttErrorChanged);
1772 
1773     m_client->setHostname(group.readEntry("Host"));
1774     m_client->setPort(group.readEntry("Port").toUInt());
1775 
1776     const bool useID = group.readEntry("UseID").toUInt();
1777     if (useID)
1778         m_client->setClientId(group.readEntry("ClientID"));
1779 
1780     const bool useAuthentication = group.readEntry("UseAuthentication").toUInt();
1781     if (useAuthentication) {
1782         m_client->setUsername(group.readEntry("UserName"));
1783         m_client->setPassword(group.readEntry("Password"));
1784     }
1785 
1786     //connect to the selected broker
1787     QDEBUG("Connect to " << m_client->hostname() << ":" << m_client->port());
1788     if (!m_connectTimeoutTimer) {
1789         m_connectTimeoutTimer = new QTimer(this);
1790         m_connectTimeoutTimer->setInterval(6000);
1791         connect(m_connectTimeoutTimer, &QTimer::timeout, this, &ImportFileWidget::mqttConnectTimeout);
1792     }
1793     m_connectTimeoutTimer->start();
1794     m_client->connectToHost();
1795 }
1796 
1797 void ImportFileWidget::disconnectMqttConnection() {
1798     if (m_client && m_client->state() == QMqttClient::ClientState::Connected) {
1799         emit MQTTClearTopics();
1800         disconnect(m_client, &QMqttClient::disconnected, this, &ImportFileWidget::onMqttDisconnect);
1801         QDEBUG("Disconnecting from " << m_client->hostname());
1802         m_client->disconnectFromHost();
1803         delete m_client;
1804         m_client = nullptr;
1805     }
1806 }
1807 
1808 /*!
1809  * returns \c true if there is a valid connection to an MQTT broker and the user has subscribed to at least 1 topic,
1810  * returns \c false otherwise.
1811  */
1812 bool ImportFileWidget::isMqttValid() {
1813     if (!m_client)
1814         return false;
1815 
1816     bool connected = (m_client->state() == QMqttClient::ClientState::Connected);
1817     bool subscribed = (m_subscriptionWidget->subscriptionCount() > 0);
1818     bool fileTypeOk = false;
1819     if (this->currentFileType() == AbstractFileFilter::FileType::Ascii)
1820         fileTypeOk = true;
1821 
1822     return connected && subscribed && fileTypeOk;
1823 }
1824 
1825 /*!
1826  *\brief called when the client connects to the broker successfully.
1827  * subscribes to every topic (# wildcard) in order to later list every available topic
1828  */
1829 void ImportFileWidget::onMqttConnect() {
1830     m_connectTimeoutTimer->stop();
1831     if (m_client->error() == QMqttClient::NoError) {
1832         ui.frameSubscriptions->setVisible(true);
1833         m_subscriptionWidget->setVisible(true);
1834         m_subscriptionWidget->makeVisible(true);
1835 
1836         if (!m_client->subscribe(QMqttTopicFilter(QLatin1String("#")), 1))
1837             emit error(i18n("Couldn't subscribe to all available topics."));
1838         else {
1839             emit error(QString());
1840             ui.lLWT->show();
1841             ui.bLWT->show();
1842             ui.lTopics->show();
1843         }
1844     } else
1845         emit error("on mqtt connect error " + QString::number(m_client->error()));
1846 
1847     emit subscriptionsChanged();
1848     RESET_CURSOR;
1849 }
1850 
1851 /*!
1852  *\brief called when the client disconnects from the broker successfully
1853  * removes every information about the former connection
1854  */
1855 void ImportFileWidget::onMqttDisconnect() {
1856     DEBUG("Disconnected from " << STDSTRING(m_client->hostname()));
1857     m_connectTimeoutTimer->stop();
1858 
1859     ui.lTopics->hide();
1860     ui.frameSubscriptions->hide();
1861     ui.lLWT->hide();
1862     ui.bLWT->hide();
1863 
1864     ui.cbConnection->setCurrentIndex(-1);
1865 
1866     emit subscriptionsChanged();
1867     emit error(i18n("Disconnected from '%1'.", m_client->hostname()));
1868     RESET_CURSOR;
1869 }
1870 
1871 /*!
1872  *\brief called when the subscribe button is pressed
1873  * subscribes to the topic represented by the current item of twTopics
1874  */
1875 void ImportFileWidget::subscribeTopic(const QString& name, uint QoS) {
1876     const QMqttTopicFilter filter {name};
1877     QMqttSubscription* tempSubscription = m_client->subscribe(filter, static_cast<quint8>(QoS) );
1878 
1879     if (tempSubscription) {
1880         m_mqttSubscriptions.push_back(tempSubscription);
1881         connect(tempSubscription, &QMqttSubscription::messageReceived, this, &ImportFileWidget::mqttSubscriptionMessageReceived);
1882         emit subscriptionsChanged();
1883     }
1884 }
1885 
1886 /*!
1887  *\brief Unsubscribes from the given topic, and removes any data connected to it
1888  *
1889  * \param topicName the name of a topic we want to unsubscribe from
1890  */
1891 void ImportFileWidget::unsubscribeTopic(const QString& topicName, QVector<QTreeWidgetItem*> children) {
1892     if (topicName.isEmpty())
1893         return;
1894 
1895     for (int i = 0; i< m_mqttSubscriptions.count(); ++i) {
1896         if (m_mqttSubscriptions[i]->topic().filter() == topicName) {
1897             //explicitely disconnect from the signal, callling QMqttClient::unsubscribe() below is not enough
1898             disconnect(m_mqttSubscriptions.at(i), &QMqttSubscription::messageReceived, this, &ImportFileWidget::mqttSubscriptionMessageReceived);
1899             m_mqttSubscriptions.remove(i);
1900             break;
1901         }
1902     }
1903 
1904     QMqttTopicFilter filter{topicName};
1905     m_client->unsubscribe(filter);
1906 
1907     QMapIterator<QMqttTopicName, QMqttMessage> j(m_lastMessage);
1908     while (j.hasNext()) {
1909         j.next();
1910         if (MQTTSubscriptionWidget::checkTopicContains(topicName, j.key().name()))
1911             m_lastMessage.remove(j.key());
1912     }
1913 
1914     if (m_willSettings.willTopic == topicName) {
1915         if (m_subscriptionWidget->subscriptionCount() > 0)
1916             m_willSettings.willTopic = children[0]->text(0);
1917         else
1918             m_willSettings.willTopic.clear();
1919     }
1920 
1921     //signals that there was a change among the subscribed topics
1922     emit subscriptionsChanged();
1923     refreshPreview();
1924 }
1925 
1926 /*!
1927  *\brief called when the client receives a message
1928  * if the message arrived from a new topic, the topic is put in twTopics
1929  */
1930 void ImportFileWidget::mqttMessageReceived(const QByteArray& message, const QMqttTopicName& topic) {
1931     Q_UNUSED(message);
1932 //  qDebug()<<"received " << topic.name();
1933     if (m_addedTopics.contains(topic.name()))
1934         return;
1935 
1936     m_addedTopics.push_back(topic.name());
1937     m_subscriptionWidget->setTopicTreeText(i18n("Available (%1)", m_addedTopics.size()));
1938     QStringList name;
1939     QString rootName;
1940     const QChar sep = '/';
1941 
1942     if (topic.name().contains(sep)) {
1943         const QStringList& list = topic.name().split(sep, QString::SkipEmptyParts);
1944 
1945         if (!list.isEmpty()) {
1946             rootName = list.at(0);
1947             name.append(list.at(0));
1948             int topItemIdx = -1;
1949             //check whether the first level of the topic can be found in twTopics
1950             for (int i = 0; i < m_subscriptionWidget->topicCount(); ++i) {
1951                 if (m_subscriptionWidget->topLevelTopic(i)->text(0) == list.at(0)) {
1952                     topItemIdx = i;
1953                     break;
1954                 }
1955             }
1956 
1957             //if not we simply add every level of the topic to the tree
1958             if (topItemIdx < 0) {
1959                 auto* currentItem = new QTreeWidgetItem(name);
1960                 m_subscriptionWidget->addTopic(currentItem);
1961                 for (int i = 1; i < list.size(); ++i) {
1962                     name.clear();
1963                     name.append(list.at(i));
1964                     currentItem->addChild(new QTreeWidgetItem(name));
1965                     currentItem = currentItem->child(0);
1966                 }
1967             }
1968             //otherwise we search for the first level that isn't part of the tree,
1969             //then add every level of the topic to the tree from that certain level
1970             else {
1971                 QTreeWidgetItem* currentItem = m_subscriptionWidget->topLevelTopic(topItemIdx);
1972                 int listIdx = 1;
1973                 for (; listIdx < list.size(); ++listIdx) {
1974                     QTreeWidgetItem* childItem = nullptr;
1975                     bool found = false;
1976                     for (int j = 0; j < currentItem->childCount(); ++j) {
1977                         childItem = currentItem->child(j);
1978                         if (childItem->text(0) == list.at(listIdx)) {
1979                             found = true;
1980                             currentItem = childItem;
1981                             break;
1982                         }
1983                     }
1984                     if (!found) {
1985                         //this is the level that isn't present in the tree
1986                         break;
1987                     }
1988                 }
1989 
1990                 //add every level to the tree starting with the first level that isn't part of the tree
1991                 for (; listIdx < list.size(); ++listIdx) {
1992                     name.clear();
1993                     name.append(list.at(listIdx));
1994                     currentItem->addChild(new QTreeWidgetItem(name));
1995                     currentItem = currentItem->child(currentItem->childCount() - 1);
1996                 }
1997             }
1998         }
1999     } else {
2000         rootName = topic.name();
2001         name.append(topic.name());
2002         m_subscriptionWidget->addTopic(new QTreeWidgetItem(name));
2003     }
2004 
2005     //if a subscribed topic contains the new topic, we have to update twSubscriptions
2006     for (int i = 0; i < m_subscriptionWidget->subscriptionCount(); ++i) {
2007         const QStringList subscriptionName = m_subscriptionWidget->topLevelSubscription(i)->text(0).split(sep, QString::SkipEmptyParts);
2008         if (!subscriptionName.isEmpty()) {
2009             if (rootName == subscriptionName.first()) {
2010                 QVector<QString> subscriptions;
2011                 for (const auto& sub : m_mqttSubscriptions)
2012                     subscriptions.push_back(sub->topic().filter());
2013                 emit updateSubscriptionTree(subscriptions);
2014                 break;
2015             }
2016         }
2017     }
2018 
2019     //signals that a newTopic was added, in order to fill the completer of leTopics
2020     emit newTopic(rootName);
2021 }
2022 
2023 /*!
2024  *\brief called when the client receives a message from a subscribed topic (that isn't the "#" wildcard)
2025  */
2026 void ImportFileWidget::mqttSubscriptionMessageReceived(const QMqttMessage &msg) {
2027     QDEBUG("message received from: " << msg.topic().name());
2028 
2029     //update the last message for the topic
2030     m_lastMessage[msg.topic()] = msg;
2031 }
2032 
2033 /*!
2034  *\brief called when the clientError of the MQTT client changes
2035  *
2036  * \param clientError the current error of the client
2037  */
2038 void ImportFileWidget::mqttErrorChanged(QMqttClient::ClientError clientError) {
2039     switch (clientError) {
2040     case QMqttClient::BadUsernameOrPassword:
2041         emit error(i18n("Wrong username or password"));
2042         break;
2043     case QMqttClient::IdRejected:
2044         emit error(i18n("The client ID wasn't accepted"));
2045         break;
2046     case QMqttClient::ServerUnavailable:
2047     case QMqttClient::TransportInvalid:
2048         emit error(i18n("The broker %1 couldn't be reached.", m_client->hostname()));
2049         break;
2050     case QMqttClient::NotAuthorized:
2051         emit error(i18n("The client is not authorized to connect."));
2052         break;
2053     case QMqttClient::UnknownError:
2054         emit error(i18n("An unknown error occurred."));
2055         break;
2056     case QMqttClient::NoError:
2057     case QMqttClient::InvalidProtocolVersion:
2058     case QMqttClient::ProtocolViolation:
2059     case QMqttClient::Mqtt5SpecificError:
2060         emit error(i18n("An error occurred."));
2061         break;
2062     default:
2063         emit error(i18n("An error occurred."));
2064         break;
2065     }
2066     m_connectTimeoutTimer->stop();
2067 }
2068 
2069 /*!
2070  *\brief called when m_connectTimeoutTimer ticks,
2071  *       meaning that the client couldn't connect to the broker in 5 seconds
2072  *       disconnects the client, stops the timer, and warns the user
2073  */
2074 void ImportFileWidget::mqttConnectTimeout() {
2075     m_client->disconnectFromHost();
2076     m_connectTimeoutTimer->stop();
2077     emit error(i18n("Connecting to '%1:%2' timed out.", m_client->hostname(), m_client->port()));
2078     RESET_CURSOR;
2079 }
2080 
2081 /*!
2082     Shows the MQTT connection manager where the connections are created and edited.
2083     The selected connection is selected in the connection combo box in this widget.
2084 */
2085 void ImportFileWidget::showMQTTConnectionManager() {
2086     bool previousConnectionChanged = false;
2087     auto* dlg = new MQTTConnectionManagerDialog(this, ui.cbConnection->currentText(), previousConnectionChanged);
2088 
2089     if (dlg->exec() == QDialog::Accepted) {
2090         //re-read the available connections to be in sync with the changes in MQTTConnectionManager
2091         m_initialisingMQTT = true;
2092         const QString& prevConn = ui.cbConnection->currentText();
2093         ui.cbConnection->clear();
2094         readMQTTConnections();
2095         m_initialisingMQTT = false;
2096 
2097         //select the connection the user has selected in MQTTConnectionManager
2098         const QString& conn = dlg->connection();
2099 
2100         int index = ui.cbConnection->findText(conn);
2101         if (conn != prevConn) {//Current connection isn't the previous one
2102             if (ui.cbConnection->currentIndex() != index)
2103                 ui.cbConnection->setCurrentIndex(index);
2104             else
2105                 mqttConnectionChanged();
2106         } else if (dlg->initialConnectionChanged()) {//Current connection is the same with previous one but it changed
2107             if (ui.cbConnection->currentIndex() == index)
2108                 mqttConnectionChanged();
2109             else
2110                 ui.cbConnection->setCurrentIndex(index);
2111         } else { //Previous connection wasn't changed
2112             m_initialisingMQTT = true;
2113             ui.cbConnection->setCurrentIndex(index);
2114             m_initialisingMQTT = false;
2115         }
2116     }
2117     delete dlg;
2118 }
2119 
2120 /*!
2121     loads all available saved MQTT nconnections
2122 */
2123 void ImportFileWidget::readMQTTConnections() {
2124     DEBUG("ImportFileWidget: reading available MQTT connections");
2125     KConfig config(m_configPath, KConfig::SimpleConfig);
2126     for (const auto& name : config.groupList())
2127         ui.cbConnection->addItem(name);
2128 }
2129 
2130 /*!
2131  * \brief Shows the mqtt will settings widget, which allows the user to modify the will settings
2132  */
2133 void ImportFileWidget::showWillSettings() {
2134     QMenu menu;
2135 
2136     QVector<QTreeWidgetItem*> children;
2137     for (int i = 0; i < m_subscriptionWidget->subscriptionCount(); ++i)
2138         MQTTSubscriptionWidget::findSubscriptionLeafChildren(children, m_subscriptionWidget->topLevelSubscription(i));
2139 
2140     QVector<QString> topics;
2141     for (const auto& child : children)
2142         topics.append(child->text(0));
2143 
2144     MQTTWillSettingsWidget willSettingsWidget(&menu, m_willSettings, topics);
2145 
2146     connect(&willSettingsWidget, &MQTTWillSettingsWidget::applyClicked, [this, &menu, &willSettingsWidget]() {
2147         m_willSettings = willSettingsWidget.will();
2148         menu.close();
2149     });
2150     auto* widgetAction = new QWidgetAction(this);
2151     widgetAction->setDefaultWidget(&willSettingsWidget);
2152     menu.addAction(widgetAction);
2153 
2154     const QPoint pos(ui.bLWT->sizeHint().width(),ui.bLWT->sizeHint().height());
2155     menu.exec(ui.bLWT->mapToGlobal(pos));
2156 }
2157 
2158 void ImportFileWidget::enableWill(bool enable) {
2159     if (enable) {
2160         if (!ui.bLWT->isEnabled())
2161             ui.bLWT->setEnabled(enable);
2162     } else
2163         ui.bLWT->setEnabled(enable);
2164 }
2165 
2166 
2167 /*!
2168     saves the settings to the MQTTClient \c client.
2169 */
2170 void ImportFileWidget::saveMQTTSettings(MQTTClient* client) const {
2171     DEBUG("ImportFileWidget::saveMQTTSettings");
2172     auto updateType = static_cast<MQTTClient::UpdateType>(ui.cbUpdateType->currentIndex());
2173     auto readingType = static_cast<MQTTClient::ReadingType>(ui.cbReadingType->currentIndex());
2174 
2175     currentFileFilter();
2176     client->setFilter(static_cast<AsciiFilter*>(m_currentFilter.release())); // pass ownership of the filter to MQTTClient
2177 
2178     client->setReadingType(readingType);
2179 
2180     if (updateType == MQTTClient::UpdateType::TimeInterval)
2181         client->setUpdateInterval(ui.sbUpdateInterval->value());
2182 
2183     client->setKeepNValues(ui.sbKeepNValues->value());
2184     client->setUpdateType(updateType);
2185 
2186     if (readingType != MQTTClient::ReadingType::TillEnd)
2187         client->setSampleSize(ui.sbSampleSize->value());
2188 
2189     client->setMQTTClientHostPort(m_client->hostname(), m_client->port());
2190 
2191     KConfig config(m_configPath, KConfig::SimpleConfig);
2192     KConfigGroup group = config.group(ui.cbConnection->currentText());
2193 
2194     bool useID = group.readEntry("UseID").toUInt();
2195     bool useAuthentication = group.readEntry("UseAuthentication").toUInt();
2196 
2197     client->setMQTTUseAuthentication(useAuthentication);
2198     if (useAuthentication)
2199         client->setMQTTClientAuthentication(m_client->username(), m_client->password());
2200 
2201     client->setMQTTUseID(useID);
2202     if (useID)
2203         client->setMQTTClientId(m_client->clientId());
2204 
2205     for (int i = 0; i < m_mqttSubscriptions.count(); ++i)
2206         client->addInitialMQTTSubscriptions(m_mqttSubscriptions[i]->topic(), m_mqttSubscriptions[i]->qos());
2207 
2208     const bool retain = group.readEntry("Retain").toUInt();
2209     client->setMQTTRetain(retain);
2210 
2211     if (m_willSettings.enabled)
2212         client->setWillSettings(m_willSettings);
2213 }
2214 #endif