File indexing completed on 2024-05-12 15:28:08

0001 /***************************************************************************
0002     File             : XYFourierFilterCurveDock.cpp
0003     Project          : LabPlot
0004     --------------------------------------------------------------------
0005     Copyright        : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn)
0006     Description      : widget for editing properties of Fourier filter curves
0007 
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "XYFourierFilterCurveDock.h"
0030 #include "backend/core/AspectTreeModel.h"
0031 #include "backend/core/Project.h"
0032 #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h"
0033 #include "commonfrontend/widgets/TreeViewComboBox.h"
0034 
0035 #include <KMessageBox>
0036 
0037 #include <QMenu>
0038 #include <QWidgetAction>
0039 
0040 /*!
0041   \class XYFourierFilterCurveDock
0042  \brief  Provides a widget for editing the properties of the XYFourierFilterCurves
0043         (2D-curves defined by a Fourier filter) currently selected in
0044         the project explorer.
0045 
0046   If more then one curves are set, the properties of the first column are shown.
0047   The changes of the properties are applied to all curves.
0048   The exclusions are the name, the comment and the datasets (columns) of
0049   the curves  - these properties can only be changed if there is only one single curve.
0050 
0051   \ingroup kdefrontend
0052 */
0053 
0054 XYFourierFilterCurveDock::XYFourierFilterCurveDock(QWidget* parent) : XYCurveDock(parent) {
0055 }
0056 
0057 /*!
0058  *  // Tab "General"
0059  */
0060 void XYFourierFilterCurveDock::setupGeneral() {
0061     QWidget* generalTab = new QWidget(ui.tabGeneral);
0062     uiGeneralTab.setupUi(generalTab);
0063     m_leName = uiGeneralTab.leName;
0064     m_leComment = uiGeneralTab.leComment;
0065 
0066     auto* gridLayout = static_cast<QGridLayout*>(generalTab->layout());
0067     gridLayout->setContentsMargins(2,2,2,2);
0068     gridLayout->setHorizontalSpacing(2);
0069     gridLayout->setVerticalSpacing(2);
0070 
0071     uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet"));
0072     uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve"));
0073 
0074     cbDataSourceCurve = new TreeViewComboBox(generalTab);
0075     gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3);
0076     cbXDataColumn = new TreeViewComboBox(generalTab);
0077     gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2);
0078     cbYDataColumn = new TreeViewComboBox(generalTab);
0079     gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2);
0080 
0081     for (int i = 0; i < NSL_FILTER_TYPE_COUNT; i++)
0082         uiGeneralTab.cbType->addItem(i18n(nsl_filter_type_name[i]));
0083 
0084     for (int i = 0; i < NSL_FILTER_FORM_COUNT; i++)
0085         uiGeneralTab.cbForm->addItem(i18n(nsl_filter_form_name[i]));
0086 
0087     for (int i = 0; i < NSL_FILTER_CUTOFF_UNIT_COUNT; i++) {
0088         uiGeneralTab.cbUnit->addItem(i18n(nsl_filter_cutoff_unit_name[i]));
0089         uiGeneralTab.cbUnit2->addItem(i18n(nsl_filter_cutoff_unit_name[i]));
0090     }
0091 
0092     //TODO: use line edits
0093     uiGeneralTab.sbMin->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
0094     uiGeneralTab.sbMax->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
0095 
0096     uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build"));
0097 
0098     auto* layout = new QHBoxLayout(ui.tabGeneral);
0099     layout->setMargin(0);
0100     layout->addWidget(generalTab);
0101 
0102     //Slots
0103     connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::nameChanged );
0104     connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::commentChanged );
0105     connect(uiGeneralTab.chkVisible, &QCheckBox::clicked, this, &XYFourierFilterCurveDock::visibilityChanged);
0106     connect(uiGeneralTab.cbDataSourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::dataSourceTypeChanged);
0107     connect(uiGeneralTab.cbAutoRange, &QCheckBox::clicked, this, &XYFourierFilterCurveDock::autoRangeChanged);
0108     connect(uiGeneralTab.sbMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::xRangeMinChanged);
0109     connect(uiGeneralTab.sbMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::xRangeMaxChanged);
0110 
0111     connect(uiGeneralTab.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::typeChanged);
0112     connect(uiGeneralTab.cbForm, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::formChanged);
0113     connect(uiGeneralTab.sbOrder, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYFourierFilterCurveDock::orderChanged);
0114     connect(uiGeneralTab.sbCutoff, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::enableRecalculate);
0115     connect(uiGeneralTab.sbCutoff2, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::enableRecalculate);
0116     connect(uiGeneralTab.cbUnit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::unitChanged);
0117     connect(uiGeneralTab.cbUnit2, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::unit2Changed);
0118     connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYFourierFilterCurveDock::recalculateClicked);
0119 
0120     connect(cbDataSourceCurve, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierFilterCurveDock::dataSourceCurveChanged);
0121     connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierFilterCurveDock::xDataColumnChanged);
0122     connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierFilterCurveDock::yDataColumnChanged);
0123 }
0124 
0125 void XYFourierFilterCurveDock::initGeneralTab() {
0126     //if there are more then one curve in the list, disable the tab "general"
0127     if (m_curvesList.size() == 1) {
0128         uiGeneralTab.lName->setEnabled(true);
0129         uiGeneralTab.leName->setEnabled(true);
0130         uiGeneralTab.lComment->setEnabled(true);
0131         uiGeneralTab.leComment->setEnabled(true);
0132 
0133         uiGeneralTab.leName->setText(m_curve->name());
0134         uiGeneralTab.leComment->setText(m_curve->comment());
0135     } else {
0136         uiGeneralTab.lName->setEnabled(false);
0137         uiGeneralTab.leName->setEnabled(false);
0138         uiGeneralTab.lComment->setEnabled(false);
0139         uiGeneralTab.leComment->setEnabled(false);
0140 
0141         uiGeneralTab.leName->setText(QString());
0142         uiGeneralTab.leComment->setText(QString());
0143     }
0144 
0145     auto* analysisCurve = dynamic_cast<XYAnalysisCurve*>(m_curve);
0146     checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath());
0147     checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath());
0148 
0149     //show the properties of the first curve
0150     m_filterCurve = dynamic_cast<XYFourierFilterCurve*>(m_curve);
0151 
0152     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(m_filterCurve->dataSourceType()));
0153     this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex());
0154     XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_filterCurve->dataSourceCurve());
0155     XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_filterCurve->xDataColumn());
0156     XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_filterCurve->yDataColumn());
0157     uiGeneralTab.cbAutoRange->setChecked(m_filterData.autoRange);
0158     uiGeneralTab.sbMin->setValue(m_filterData.xRange.first());
0159     uiGeneralTab.sbMax->setValue(m_filterData.xRange.last());
0160     this->autoRangeChanged();
0161 
0162     uiGeneralTab.cbType->setCurrentIndex(m_filterData.type);
0163     this->typeChanged();
0164     uiGeneralTab.cbForm->setCurrentIndex(m_filterData.form);
0165     this->formChanged();
0166     uiGeneralTab.sbOrder->setValue((int)m_filterData.order);
0167     uiGeneralTab.cbUnit->setCurrentIndex(m_filterData.unit);
0168     this->unitChanged();
0169     // after unit has set
0170     uiGeneralTab.sbCutoff->setValue(m_filterData.cutoff);
0171     uiGeneralTab.cbUnit2->setCurrentIndex(m_filterData.unit2);
0172     this->unit2Changed();
0173     // after unit has set
0174     uiGeneralTab.sbCutoff2->setValue(m_filterData.cutoff2);
0175     this->showFilterResult();
0176 
0177     uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() );
0178 
0179     //Slots
0180     connect(m_filterCurve, &XYFourierFilterCurve::aspectDescriptionChanged, this, &XYFourierFilterCurveDock::curveDescriptionChanged);
0181     connect(m_filterCurve, &XYFourierFilterCurve::dataSourceTypeChanged, this, &XYFourierFilterCurveDock::curveDataSourceTypeChanged);
0182     connect(m_filterCurve, &XYFourierFilterCurve::dataSourceCurveChanged, this, &XYFourierFilterCurveDock::curveDataSourceCurveChanged);
0183     connect(m_filterCurve, &XYFourierFilterCurve::xDataColumnChanged, this, &XYFourierFilterCurveDock::curveXDataColumnChanged);
0184     connect(m_filterCurve, &XYFourierFilterCurve::yDataColumnChanged, this, &XYFourierFilterCurveDock::curveYDataColumnChanged);
0185     connect(m_filterCurve, &XYFourierFilterCurve::filterDataChanged, this, &XYFourierFilterCurveDock::curveFilterDataChanged);
0186     connect(m_filterCurve, &XYFourierFilterCurve::sourceDataChanged, this, &XYFourierFilterCurveDock::enableRecalculate);
0187     connect(m_filterCurve, QOverload<bool>::of(&XYCurve::visibilityChanged), this, &XYFourierFilterCurveDock::curveVisibilityChanged);
0188 }
0189 
0190 void XYFourierFilterCurveDock::setModel() {
0191     QList<AspectType> list{AspectType::Folder, AspectType::Datapicker, AspectType::Worksheet,
0192                             AspectType::CartesianPlot, AspectType::XYCurve, AspectType::XYAnalysisCurve};
0193     cbDataSourceCurve->setTopLevelClasses(list);
0194 
0195     QList<const AbstractAspect*> hiddenAspects;
0196     for (auto* curve : m_curvesList)
0197         hiddenAspects << curve;
0198     cbDataSourceCurve->setHiddenAspects(hiddenAspects);
0199 
0200     list = {AspectType::Folder, AspectType::Workbook, AspectType::Datapicker,
0201             AspectType::DatapickerCurve, AspectType::Spreadsheet, AspectType::LiveDataSource,
0202             AspectType::Column, AspectType::Worksheet, AspectType::CartesianPlot, AspectType::XYFitCurve
0203            };
0204     cbXDataColumn->setTopLevelClasses(list);
0205     cbYDataColumn->setTopLevelClasses(list);
0206 
0207     cbDataSourceCurve->setModel(m_aspectTreeModel);
0208     cbXDataColumn->setModel(m_aspectTreeModel);
0209     cbYDataColumn->setModel(m_aspectTreeModel);
0210 
0211     XYCurveDock::setModel();
0212 }
0213 
0214 /*!
0215   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
0216 */
0217 void XYFourierFilterCurveDock::setCurves(QList<XYCurve*> list) {
0218     m_initializing = true;
0219     m_curvesList = list;
0220     m_curve = list.first();
0221     m_aspect = m_curve;
0222     m_filterCurve = dynamic_cast<XYFourierFilterCurve*>(m_curve);
0223     m_aspectTreeModel = new AspectTreeModel(m_curve->project());
0224     this->setModel();
0225     m_filterData = m_filterCurve->filterData();
0226 
0227     SET_NUMBER_LOCALE
0228     uiGeneralTab.sbMin->setLocale(numberLocale);
0229     uiGeneralTab.sbMax->setLocale(numberLocale);
0230     uiGeneralTab.sbCutoff->setLocale(numberLocale);
0231     uiGeneralTab.sbCutoff2->setLocale(numberLocale);
0232 
0233     initGeneralTab();
0234     initTabs();
0235     m_initializing = false;
0236 }
0237 
0238 //*************************************************************
0239 //**** SLOTs for changes triggered in XYFitCurveDock *****
0240 //*************************************************************
0241 void XYFourierFilterCurveDock::dataSourceTypeChanged(int index) {
0242     auto type = (XYAnalysisCurve::DataSourceType)index;
0243     if (type == XYAnalysisCurve::DataSourceType::Spreadsheet) {
0244         uiGeneralTab.lDataSourceCurve->hide();
0245         cbDataSourceCurve->hide();
0246         uiGeneralTab.lXColumn->show();
0247         cbXDataColumn->show();
0248         uiGeneralTab.lYColumn->show();
0249         cbYDataColumn->show();
0250     } else {
0251         uiGeneralTab.lDataSourceCurve->show();
0252         cbDataSourceCurve->show();
0253         uiGeneralTab.lXColumn->hide();
0254         cbXDataColumn->hide();
0255         uiGeneralTab.lYColumn->hide();
0256         cbYDataColumn->hide();
0257     }
0258 
0259     if (m_initializing)
0260         return;
0261 
0262     for (auto* curve : m_curvesList)
0263         dynamic_cast<XYFourierFilterCurve*>(curve)->setDataSourceType(type);
0264 }
0265 
0266 void XYFourierFilterCurveDock::dataSourceCurveChanged(const QModelIndex& index) {
0267     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0268     XYCurve* dataSourceCurve{};
0269     if (aspect)
0270         dataSourceCurve = dynamic_cast<XYCurve*>(aspect);
0271 
0272     // update range of cutoff spin boxes (like a unit change)
0273     unitChanged();
0274     unit2Changed();
0275 
0276     if (m_initializing)
0277         return;
0278 
0279     for (auto* curve : m_curvesList)
0280         dynamic_cast<XYFourierFilterCurve*>(curve)->setDataSourceCurve(dataSourceCurve);
0281 }
0282 
0283 void XYFourierFilterCurveDock::xDataColumnChanged(const QModelIndex& index) {
0284     if (m_initializing)
0285         return;
0286 
0287     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0288     auto* column = dynamic_cast<AbstractColumn*>(aspect);
0289 
0290     for (auto* curve : m_curvesList)
0291         dynamic_cast<XYFourierFilterCurve*>(curve)->setXDataColumn(column);
0292 
0293     // update range of cutoff spin boxes (like a unit change)
0294     unitChanged();
0295     unit2Changed();
0296 
0297     if (column != nullptr) {
0298         if (uiGeneralTab.cbAutoRange->isChecked()) {
0299             uiGeneralTab.sbMin->setValue(column->minimum());
0300             uiGeneralTab.sbMax->setValue(column->maximum());
0301         }
0302     }
0303 
0304     cbXDataColumn->useCurrentIndexText(true);
0305     cbXDataColumn->setInvalid(false);
0306 }
0307 
0308 void XYFourierFilterCurveDock::yDataColumnChanged(const QModelIndex& index) {
0309     if (m_initializing)
0310         return;
0311 
0312     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0313     auto* column = dynamic_cast<AbstractColumn*>(aspect);
0314 
0315     for (auto* curve : m_curvesList)
0316         dynamic_cast<XYFourierFilterCurve*>(curve)->setYDataColumn(column);
0317 
0318     cbYDataColumn->useCurrentIndexText(true);
0319     cbYDataColumn->setInvalid(false);
0320 }
0321 
0322 void XYFourierFilterCurveDock::autoRangeChanged() {
0323     bool autoRange = uiGeneralTab.cbAutoRange->isChecked();
0324     m_filterData.autoRange = autoRange;
0325 
0326     if (autoRange) {
0327         uiGeneralTab.lMin->setEnabled(false);
0328         uiGeneralTab.sbMin->setEnabled(false);
0329         uiGeneralTab.lMax->setEnabled(false);
0330         uiGeneralTab.sbMax->setEnabled(false);
0331 
0332         const AbstractColumn* xDataColumn = nullptr;
0333         if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0334             xDataColumn = m_filterCurve->xDataColumn();
0335         else {
0336             if (m_filterCurve->dataSourceCurve())
0337                 xDataColumn = m_filterCurve->dataSourceCurve()->xColumn();
0338         }
0339 
0340         if (xDataColumn) {
0341             uiGeneralTab.sbMin->setValue(xDataColumn->minimum());
0342             uiGeneralTab.sbMax->setValue(xDataColumn->maximum());
0343         }
0344     } else {
0345         uiGeneralTab.lMin->setEnabled(true);
0346         uiGeneralTab.sbMin->setEnabled(true);
0347         uiGeneralTab.lMax->setEnabled(true);
0348         uiGeneralTab.sbMax->setEnabled(true);
0349     }
0350 
0351 }
0352 void XYFourierFilterCurveDock::xRangeMinChanged() {
0353     double xMin = uiGeneralTab.sbMin->value();
0354 
0355     m_filterData.xRange.first() = xMin;
0356     uiGeneralTab.pbRecalculate->setEnabled(true);
0357 }
0358 
0359 void XYFourierFilterCurveDock::xRangeMaxChanged() {
0360     double xMax = uiGeneralTab.sbMax->value();
0361 
0362     m_filterData.xRange.last() = xMax;
0363     uiGeneralTab.pbRecalculate->setEnabled(true);
0364 }
0365 
0366 void XYFourierFilterCurveDock::typeChanged() {
0367     auto type = (nsl_filter_type)uiGeneralTab.cbType->currentIndex();
0368     m_filterData.type = type;
0369 
0370     switch (type) {
0371     case nsl_filter_type_low_pass:
0372     case nsl_filter_type_high_pass:
0373         uiGeneralTab.lCutoff->setText(i18n("Cutoff:"));
0374         uiGeneralTab.lCutoff2->setVisible(false);
0375         uiGeneralTab.sbCutoff2->setVisible(false);
0376         uiGeneralTab.cbUnit2->setVisible(false);
0377         break;
0378     case nsl_filter_type_band_pass:
0379     case nsl_filter_type_band_reject:
0380         uiGeneralTab.lCutoff2->setVisible(true);
0381         uiGeneralTab.lCutoff->setText(i18n("Lower cutoff:"));
0382         uiGeneralTab.lCutoff2->setText(i18n("Upper cutoff:"));
0383         uiGeneralTab.sbCutoff2->setVisible(true);
0384         uiGeneralTab.cbUnit2->setVisible(true);
0385         break;
0386 //TODO
0387 /*  case nsl_filter_type_threshold:
0388         uiGeneralTab.lCutoff->setText(i18n("Value:"));
0389         uiGeneralTab.lCutoff2->setVisible(false);
0390         uiGeneralTab.sbCutoff2->setVisible(false);
0391         uiGeneralTab.cbUnit2->setVisible(false);
0392 */
0393     }
0394 
0395     enableRecalculate();
0396 }
0397 
0398 void XYFourierFilterCurveDock::formChanged() {
0399     auto form = (nsl_filter_form)uiGeneralTab.cbForm->currentIndex();
0400     m_filterData.form = form;
0401 
0402     switch (form) {
0403     case nsl_filter_form_ideal:
0404         uiGeneralTab.sbOrder->setVisible(false);
0405         uiGeneralTab.lOrder->setVisible(false);
0406         break;
0407     case nsl_filter_form_butterworth:
0408     case nsl_filter_form_chebyshev_i:
0409     case nsl_filter_form_chebyshev_ii:
0410     case nsl_filter_form_legendre:
0411     case nsl_filter_form_bessel:
0412         uiGeneralTab.sbOrder->setVisible(true);
0413         uiGeneralTab.lOrder->setVisible(true);
0414         break;
0415     }
0416 
0417     enableRecalculate();
0418 }
0419 
0420 void XYFourierFilterCurveDock::orderChanged() {
0421     m_filterData.order = (unsigned int)uiGeneralTab.sbOrder->value();
0422 
0423     enableRecalculate();
0424 }
0425 
0426 void XYFourierFilterCurveDock::unitChanged() {
0427     auto unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit->currentIndex();
0428     nsl_filter_cutoff_unit oldUnit = m_filterData.unit;
0429     double oldValue = uiGeneralTab.sbCutoff->value();
0430     m_filterData.unit = unit;
0431 
0432     int n = 100;
0433     double f = 1.0; // sample frequency
0434     const AbstractColumn* xDataColumn = nullptr;
0435     if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0436         xDataColumn = m_filterCurve->xDataColumn();
0437     else {
0438         if (m_filterCurve->dataSourceCurve())
0439             xDataColumn = m_filterCurve->dataSourceCurve()->xColumn();
0440     }
0441 
0442     if (xDataColumn != nullptr) {
0443         n = xDataColumn->rowCount();
0444         double range = xDataColumn->maximum() - xDataColumn->minimum();
0445         f = (n-1)/range/2.;
0446         DEBUG(" n =" << n << " sample frequency =" << f);
0447     }
0448 
0449     switch (unit) {
0450     case nsl_filter_cutoff_unit_frequency:
0451         uiGeneralTab.sbCutoff->setDecimals(6);
0452         uiGeneralTab.sbCutoff->setMaximum(f);
0453         uiGeneralTab.sbCutoff->setSingleStep(0.01*f);
0454         uiGeneralTab.sbCutoff->setSuffix(" Hz");
0455         switch (oldUnit) {
0456         case nsl_filter_cutoff_unit_frequency:
0457             break;
0458         case nsl_filter_cutoff_unit_fraction:
0459             uiGeneralTab.sbCutoff->setValue(oldValue*f);
0460             break;
0461         case nsl_filter_cutoff_unit_index:
0462             uiGeneralTab.sbCutoff->setValue(oldValue*f/n);
0463             break;
0464         }
0465         break;
0466     case nsl_filter_cutoff_unit_fraction:
0467         uiGeneralTab.sbCutoff->setDecimals(6);
0468         uiGeneralTab.sbCutoff->setMaximum(1.0);
0469         uiGeneralTab.sbCutoff->setSingleStep(0.01);
0470         uiGeneralTab.sbCutoff->setSuffix(QString());
0471         switch (oldUnit) {
0472         case nsl_filter_cutoff_unit_frequency:
0473             uiGeneralTab.sbCutoff->setValue(oldValue/f);
0474             break;
0475         case nsl_filter_cutoff_unit_fraction:
0476             break;
0477         case nsl_filter_cutoff_unit_index:
0478             uiGeneralTab.sbCutoff->setValue(oldValue/n);
0479             break;
0480         }
0481         break;
0482     case nsl_filter_cutoff_unit_index:
0483         uiGeneralTab.sbCutoff->setDecimals(0);
0484         uiGeneralTab.sbCutoff->setSingleStep(1);
0485         uiGeneralTab.sbCutoff->setMaximum(n);
0486         uiGeneralTab.sbCutoff->setSuffix(QString());
0487         switch (oldUnit) {
0488         case nsl_filter_cutoff_unit_frequency:
0489             uiGeneralTab.sbCutoff->setValue(oldValue*n/f);
0490             break;
0491         case nsl_filter_cutoff_unit_fraction:
0492             uiGeneralTab.sbCutoff->setValue(oldValue*n);
0493             break;
0494         case nsl_filter_cutoff_unit_index:
0495             break;
0496         }
0497         break;
0498     }
0499 
0500     enableRecalculate();
0501 }
0502 
0503 void XYFourierFilterCurveDock::unit2Changed() {
0504     auto unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit2->currentIndex();
0505     nsl_filter_cutoff_unit oldUnit = m_filterData.unit2;
0506     double oldValue = uiGeneralTab.sbCutoff2->value();
0507     m_filterData.unit2 = unit;
0508 
0509     int n = 100;
0510     double f = 1.0; // sample frequency
0511     const AbstractColumn* xDataColumn = nullptr;
0512     if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0513         xDataColumn = m_filterCurve->xDataColumn();
0514     else {
0515         if (m_filterCurve->dataSourceCurve())
0516             xDataColumn = m_filterCurve->dataSourceCurve()->xColumn();
0517     }
0518 
0519     if (xDataColumn != nullptr) {
0520         n = xDataColumn->rowCount();
0521         double range = xDataColumn->maximum() - xDataColumn->minimum();
0522         f = (n-1)/range/2.;
0523         DEBUG(" n =" << n << " sample frequency =" << f);
0524     }
0525 
0526     switch (unit) {
0527     case nsl_filter_cutoff_unit_frequency:
0528         uiGeneralTab.sbCutoff2->setDecimals(6);
0529         uiGeneralTab.sbCutoff2->setMaximum(f);
0530         uiGeneralTab.sbCutoff2->setSingleStep(0.01*f);
0531         uiGeneralTab.sbCutoff2->setSuffix(" Hz");
0532         switch (oldUnit) {
0533         case nsl_filter_cutoff_unit_frequency:
0534             break;
0535         case nsl_filter_cutoff_unit_fraction:
0536             uiGeneralTab.sbCutoff2->setValue(oldValue*f);
0537             break;
0538         case nsl_filter_cutoff_unit_index:
0539             uiGeneralTab.sbCutoff2->setValue(oldValue*f/n);
0540             break;
0541         }
0542         break;
0543     case nsl_filter_cutoff_unit_fraction:
0544         uiGeneralTab.sbCutoff2->setDecimals(6);
0545         uiGeneralTab.sbCutoff2->setMaximum(1.0);
0546         uiGeneralTab.sbCutoff2->setSingleStep(0.01);
0547         uiGeneralTab.sbCutoff2->setSuffix(QString());
0548         switch (oldUnit) {
0549         case nsl_filter_cutoff_unit_frequency:
0550             uiGeneralTab.sbCutoff2->setValue(oldValue/f);
0551             break;
0552         case nsl_filter_cutoff_unit_fraction:
0553             break;
0554         case nsl_filter_cutoff_unit_index:
0555             uiGeneralTab.sbCutoff2->setValue(oldValue/n);
0556             break;
0557         }
0558         break;
0559     case nsl_filter_cutoff_unit_index:
0560         uiGeneralTab.sbCutoff2->setDecimals(0);
0561         uiGeneralTab.sbCutoff2->setSingleStep(1);
0562         uiGeneralTab.sbCutoff2->setMaximum(n);
0563         uiGeneralTab.sbCutoff2->setSuffix(QString());
0564         switch (oldUnit) {
0565         case nsl_filter_cutoff_unit_frequency:
0566             uiGeneralTab.sbCutoff2->setValue(oldValue*n/f);
0567             break;
0568         case nsl_filter_cutoff_unit_fraction:
0569             uiGeneralTab.sbCutoff2->setValue(oldValue*n);
0570             break;
0571         case nsl_filter_cutoff_unit_index:
0572             break;
0573         }
0574         break;
0575     }
0576 
0577     enableRecalculate();
0578 }
0579 
0580 void XYFourierFilterCurveDock::recalculateClicked() {
0581     m_filterData.cutoff = uiGeneralTab.sbCutoff->value();
0582     m_filterData.cutoff2 = uiGeneralTab.sbCutoff2->value();
0583 
0584     if ((m_filterData.type == nsl_filter_type_band_pass || m_filterData.type == nsl_filter_type_band_reject)
0585             && m_filterData.cutoff2 <= m_filterData.cutoff) {
0586         KMessageBox::sorry(this, i18n("The band width is <= 0 since lower cutoff value is not smaller than upper cutoff value. Please fix this."),
0587                                i18n("band width <= 0") );
0588         return;
0589     }
0590 
0591     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0592     for (auto* curve : m_curvesList)
0593         dynamic_cast<XYFourierFilterCurve*>(curve)->setFilterData(m_filterData);
0594 
0595     uiGeneralTab.pbRecalculate->setEnabled(false);
0596     emit info(i18n("Fourier-Filter status: %1", m_filterCurve->filterResult().status));
0597     QApplication::restoreOverrideCursor();
0598 }
0599 
0600 void XYFourierFilterCurveDock::enableRecalculate() const {
0601     if (m_initializing)
0602         return;
0603 
0604     //no filtering possible without the x- and y-data
0605     bool hasSourceData = false;
0606     if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) {
0607         AbstractAspect* aspectX = static_cast<AbstractAspect*>(cbXDataColumn->currentModelIndex().internalPointer());
0608         AbstractAspect* aspectY = static_cast<AbstractAspect*>(cbYDataColumn->currentModelIndex().internalPointer());
0609         hasSourceData = (aspectX != nullptr && aspectY != nullptr);
0610         if (aspectX) {
0611             cbXDataColumn->useCurrentIndexText(true);
0612             cbXDataColumn->setInvalid(false);
0613         }
0614         if (aspectY) {
0615             cbYDataColumn->useCurrentIndexText(true);
0616             cbYDataColumn->setInvalid(false);
0617         }
0618     } else {
0619          hasSourceData = (m_filterCurve->dataSourceCurve() != nullptr);
0620     }
0621 
0622     uiGeneralTab.pbRecalculate->setEnabled(hasSourceData);
0623 }
0624 
0625 /*!
0626  * show the result and details of the filter
0627  */
0628 void XYFourierFilterCurveDock::showFilterResult() {
0629     const XYFourierFilterCurve::FilterResult& filterResult = m_filterCurve->filterResult();
0630     if (!filterResult.available) {
0631         uiGeneralTab.teResult->clear();
0632         return;
0633     }
0634 
0635     QString str = i18n("status: %1", filterResult.status) + "<br>";
0636 
0637     if (!filterResult.valid) {
0638         uiGeneralTab.teResult->setText(str);
0639         return; //result is not valid, there was an error which is shown in the status-string, nothing to show more.
0640     }
0641 
0642     SET_NUMBER_LOCALE
0643     if (filterResult.elapsedTime > 1000)
0644         str += i18n("calculation time: %1 s", numberLocale.toString(filterResult.elapsedTime/1000)) + "<br>";
0645     else
0646         str += i18n("calculation time: %1 ms", numberLocale.toString(filterResult.elapsedTime)) + "<br>";
0647 
0648     str += "<br><br>";
0649 
0650     uiGeneralTab.teResult->setText(str);
0651 
0652     //enable the "recalculate"-button if the source data was changed since the last filter
0653     uiGeneralTab.pbRecalculate->setEnabled(m_filterCurve->isSourceDataChangedSinceLastRecalc());
0654 }
0655 
0656 //*************************************************************
0657 //*********** SLOTs for changes triggered in XYCurve **********
0658 //*************************************************************
0659 //General-Tab
0660 void XYFourierFilterCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) {
0661     if (m_curve != aspect)
0662         return;
0663 
0664     m_initializing = true;
0665     if (aspect->name() != uiGeneralTab.leName->text())
0666         uiGeneralTab.leName->setText(aspect->name());
0667     else if (aspect->comment() != uiGeneralTab.leComment->text())
0668         uiGeneralTab.leComment->setText(aspect->comment());
0669     m_initializing = false;
0670 }
0671 
0672 void XYFourierFilterCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) {
0673     m_initializing = true;
0674     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(type));
0675     m_initializing = false;
0676 }
0677 
0678 void XYFourierFilterCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) {
0679     m_initializing = true;
0680     XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve);
0681     m_initializing = false;
0682 }
0683 
0684 void XYFourierFilterCurveDock::curveXDataColumnChanged(const AbstractColumn* column) {
0685     m_initializing = true;
0686     XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column);
0687     m_initializing = false;
0688 }
0689 
0690 void XYFourierFilterCurveDock::curveYDataColumnChanged(const AbstractColumn* column) {
0691     m_initializing = true;
0692     XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column);
0693     m_initializing = false;
0694 }
0695 
0696 void XYFourierFilterCurveDock::curveFilterDataChanged(const XYFourierFilterCurve::FilterData& filterData) {
0697     m_initializing = true;
0698     m_filterData = filterData;
0699     uiGeneralTab.cbType->setCurrentIndex(m_filterData.type);
0700     this->typeChanged();
0701 
0702     this->showFilterResult();
0703     m_initializing = false;
0704 }
0705 
0706 void XYFourierFilterCurveDock::dataChanged() {
0707     this->enableRecalculate();
0708 }
0709 
0710 void XYFourierFilterCurveDock::curveVisibilityChanged(bool on) {
0711     m_initializing = true;
0712     uiGeneralTab.chkVisible->setChecked(on);
0713     m_initializing = false;
0714 }