"Fossies" - the Fresh Open Source Software Archive

Member "labplot-2.8.2/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp" (24 Feb 2021, 55412 Bytes) of package /linux/privat/labplot-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "XYFitCurveDock.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8.1_vs_2.8.2.

    1 /***************************************************************************
    2     File             : XYFitCurveDock.cpp
    3     Project          : LabPlot
    4     --------------------------------------------------------------------
    5     Copyright        : (C) 2014-2020 Alexander Semke (alexander.semke@web.de)
    6     Copyright        : (C) 2016-2020 Stefan Gerlach (stefan.gerlach@uni.kn)
    7     Description      : widget for editing properties of fit curves
    8 
    9  ***************************************************************************/
   10 
   11 /***************************************************************************
   12  *                                                                         *
   13  *  This program is free software; you can redistribute it and/or modify   *
   14  *  it under the terms of the GNU General Public License as published by   *
   15  *  the Free Software Foundation; either version 2 of the License, or      *
   16  *  (at your option) any later version.                                    *
   17  *                                                                         *
   18  *  This program is distributed in the hope that it will be useful,        *
   19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
   20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
   21  *  GNU General Public License for more details.                           *
   22  *                                                                         *
   23  *   You should have received a copy of the GNU General Public License     *
   24  *   along with this program; if not, write to the Free Software           *
   25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
   26  *   Boston, MA  02110-1301  USA                                           *
   27  *                                                                         *
   28  ***************************************************************************/
   29 
   30 #include "XYFitCurveDock.h"
   31 #include "backend/core/AspectTreeModel.h"
   32 #include "backend/core/Project.h"
   33 #include "backend/lib/macros.h"
   34 #include "backend/gsl/ExpressionParser.h"
   35 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
   36 #include "commonfrontend/widgets/TreeViewComboBox.h"
   37 #include "kdefrontend/widgets/ConstantsWidget.h"
   38 #include "kdefrontend/widgets/FunctionsWidget.h"
   39 #include "kdefrontend/widgets/FitOptionsWidget.h"
   40 #include "kdefrontend/widgets/FitParametersWidget.h"
   41 
   42 #include <KMessageWidget>
   43 
   44 #include <QMenu>
   45 #include <QWidgetAction>
   46 #include <QStandardItemModel>
   47 #include <QStandardPaths>
   48 #include <QClipboard>
   49 
   50 extern "C" {
   51 #include "backend/nsl/nsl_sf_stats.h"
   52 }
   53 
   54 /*!
   55   \class XYFitCurveDock
   56   \brief  Provides a widget for editing the properties of the XYFitCurves
   57         (2D-curves defined by a fit model) currently selected in
   58         the project explorer.
   59 
   60   If more then one curves are set, the properties of the first column are shown.
   61   The changes of the properties are applied to all curves.
   62   The exclusions are the name, the comment and the datasets (columns) of
   63   the curves  - these properties can only be changed if there is only one single curve.
   64 
   65   \ingroup kdefrontend
   66 */
   67 
   68 XYFitCurveDock::XYFitCurveDock(QWidget* parent) : XYCurveDock(parent) {
   69 }
   70 
   71 /*!
   72  *  set up "General" tab
   73  */
   74 void XYFitCurveDock::setupGeneral() {
   75     DEBUG("XYFitCurveDock::setupGeneral()");
   76     QWidget* generalTab = new QWidget(ui.tabGeneral);
   77     uiGeneralTab.setupUi(generalTab);
   78     m_leName = uiGeneralTab.leName;
   79     m_leComment = uiGeneralTab.leComment;
   80 
   81     auto* gridLayout = static_cast<QGridLayout*>(generalTab->layout());
   82     gridLayout->setContentsMargins(2, 2, 2, 2);
   83     gridLayout->setHorizontalSpacing(2);
   84     gridLayout->setVerticalSpacing(2);
   85 
   86     uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet"));
   87     uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve"));
   88 
   89     cbDataSourceCurve = new TreeViewComboBox(generalTab);
   90     gridLayout->addWidget(cbDataSourceCurve, 5, 3, 1, 4);
   91 
   92     cbXDataColumn = new TreeViewComboBox(generalTab);
   93     gridLayout->addWidget(cbXDataColumn, 6, 3, 1, 4);
   94 
   95     cbXErrorColumn = new TreeViewComboBox(generalTab);
   96     cbXErrorColumn->setEnabled(false);
   97     uiGeneralTab.hlXError->addWidget(cbXErrorColumn);
   98 
   99     cbYDataColumn = new TreeViewComboBox(generalTab);
  100     gridLayout->addWidget(cbYDataColumn, 7, 3, 1, 4);
  101 
  102     cbYErrorColumn = new TreeViewComboBox(generalTab);
  103     cbYErrorColumn->setEnabled(false);
  104     uiGeneralTab.hlYWeight->addWidget(cbYErrorColumn);
  105 
  106     // X/Y-Weight
  107     for (int i = 0; i < NSL_FIT_WEIGHT_TYPE_COUNT; i++) {
  108         uiGeneralTab.cbXWeight->addItem(nsl_fit_weight_type_name[i]);
  109         uiGeneralTab.cbYWeight->addItem(nsl_fit_weight_type_name[i]);
  110     }
  111     uiGeneralTab.cbXWeight->setCurrentIndex(nsl_fit_weight_no);
  112     uiGeneralTab.cbYWeight->setCurrentIndex(nsl_fit_weight_no);
  113 
  114     for (int i = 0; i < NSL_FIT_MODEL_CATEGORY_COUNT; i++)
  115         uiGeneralTab.cbCategory->addItem(nsl_fit_model_category_name[i]);
  116 
  117     uiGeneralTab.teEquation->setMaximumHeight(uiGeneralTab.leName->sizeHint().height() * 2);
  118 
  119     fitParametersWidget = new FitParametersWidget(uiGeneralTab.frameParameters);
  120     auto* l = new QVBoxLayout();
  121     l->setContentsMargins(0, 0, 0, 0);
  122     l->addWidget(fitParametersWidget);
  123     uiGeneralTab.frameParameters->setLayout(l);
  124 
  125     //use white background in the preview label
  126     QPalette p;
  127     p.setColor(QPalette::Window, Qt::white);
  128     uiGeneralTab.lFuncPic->setAutoFillBackground(true);
  129     uiGeneralTab.lFuncPic->setPalette(p);
  130 
  131     uiGeneralTab.tbConstants->setIcon(QIcon::fromTheme("labplot-format-text-symbol"));
  132     uiGeneralTab.tbFunctions->setIcon(QIcon::fromTheme("preferences-desktop-font"));
  133     uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build"));
  134 
  135     // TODO: setting checked background color to unchecked color
  136 //  p = uiGeneralTab.lData->palette();
  137     // QWidget::palette().color(QWidget::backgroundRole())
  138     // not working with 'transparent'
  139 //  p.setColor(QPalette::Base, Qt::transparent);
  140 //  uiGeneralTab.lData->setPalette(p);
  141     // see https://forum.qt.io/topic/41325/solved-background-of-checked-qpushbutton-with-stylesheet/2
  142     // Styles not usable (here: text color not theme dependent). see https://forum.qt.io/topic/60546/qpushbutton-default-windows-style-sheet/9
  143 //  uiGeneralTab.lData->setStyleSheet("QToolButton:checked{background-color: transparent;border: 3px transparent;padding: 3px;}");
  144 
  145 //  uiGeneralTab.lData->setAutoFillBackground(true);
  146 
  147     uiGeneralTab.twLog->setEditTriggers(QAbstractItemView::NoEditTriggers);
  148     uiGeneralTab.twParameters->setEditTriggers(QAbstractItemView::NoEditTriggers);
  149     uiGeneralTab.twGoodness->setEditTriggers(QAbstractItemView::NoEditTriggers);
  150 
  151     //don't allow word wrapping in the log-table for the multi-line iterations string
  152     uiGeneralTab.twLog->setWordWrap(false);
  153 
  154     //header labels
  155     QStringList headerLabels;
  156     headerLabels << QString() << i18n("Value") << i18n("Error") << i18n("Error, %") << i18n("t statistic") << QLatin1String("P > |t|")
  157         << i18n("Lower") << i18n("Upper");
  158     uiGeneralTab.twParameters->setHorizontalHeaderLabels(headerLabels);
  159 
  160     // show all options per default
  161     showDataOptions(true);
  162     showFitOptions(true);
  163     showWeightsOptions(true);
  164     showParameters(true);
  165     showResults(true);
  166 
  167     //CTRL+C copies only the last cell in the selection, we want to copy the whole selection.
  168     //install event filters to handle CTRL+C key events.
  169     uiGeneralTab.twParameters->installEventFilter(this);
  170     uiGeneralTab.twGoodness->installEventFilter(this);
  171     uiGeneralTab.twLog->installEventFilter(this);
  172 
  173     // context menus
  174     uiGeneralTab.twParameters->setContextMenuPolicy(Qt::CustomContextMenu);
  175     uiGeneralTab.twGoodness->setContextMenuPolicy(Qt::CustomContextMenu);
  176     uiGeneralTab.twLog->setContextMenuPolicy(Qt::CustomContextMenu);
  177     connect(uiGeneralTab.twParameters, &QTableWidget::customContextMenuRequested,
  178             this, &XYFitCurveDock::resultParametersContextMenuRequest);
  179     connect(uiGeneralTab.twGoodness, &QTableWidget::customContextMenuRequested,
  180             this, &XYFitCurveDock::resultGoodnessContextMenuRequest);
  181     connect(uiGeneralTab.twLog, &QTableWidget::customContextMenuRequested,
  182             this, &XYFitCurveDock::resultLogContextMenuRequest);
  183 
  184     uiGeneralTab.twLog->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
  185     uiGeneralTab.twGoodness->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
  186     // append symbols
  187     uiGeneralTab.twGoodness->item(0, 0)->setText(uiGeneralTab.twGoodness->item(0, 0)->text() + UTF8_QSTRING(" (χ²)"));
  188     uiGeneralTab.twGoodness->item(1, 0)->setText(uiGeneralTab.twGoodness->item(1, 0)->text() + UTF8_QSTRING(" (χ²/dof)"));
  189     uiGeneralTab.twGoodness->item(3, 0)->setText(uiGeneralTab.twGoodness->item(3, 0)->text() + UTF8_QSTRING(" (R²)"));
  190     uiGeneralTab.twGoodness->item(4, 0)->setText(uiGeneralTab.twGoodness->item(4, 0)->text() + UTF8_QSTRING(" (R̄²)"));
  191     uiGeneralTab.twGoodness->item(5, 0)->setText(UTF8_QSTRING("χ²-") + i18n("test") + UTF8_QSTRING(" ( P > χ²)"));
  192 
  193     auto* layout = new QHBoxLayout(ui.tabGeneral);
  194     layout->setMargin(0);
  195     layout->addWidget(generalTab);
  196 
  197     //Slots
  198     connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFitCurveDock::nameChanged);
  199     connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFitCurveDock::commentChanged);
  200     connect(uiGeneralTab.chkVisible, &QCheckBox::clicked, this, &XYFitCurveDock::visibilityChanged);
  201     connect(uiGeneralTab.cbDataSourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFitCurveDock::dataSourceTypeChanged);
  202     connect(uiGeneralTab.lWeights, &QPushButton::clicked, this, &XYFitCurveDock::showWeightsOptions);
  203     connect(uiGeneralTab.cbXWeight, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFitCurveDock::xWeightChanged);
  204     connect(uiGeneralTab.cbYWeight, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFitCurveDock::yWeightChanged);
  205     connect(uiGeneralTab.cbCategory, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFitCurveDock::categoryChanged);
  206     connect(uiGeneralTab.cbModel, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFitCurveDock::modelTypeChanged);
  207     connect(uiGeneralTab.sbDegree, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYFitCurveDock::updateModelEquation);
  208     connect(uiGeneralTab.teEquation, &ExpressionTextEdit::expressionChanged, this, &XYFitCurveDock::expressionChanged);
  209     connect(uiGeneralTab.tbConstants, &QToolButton::clicked, this, &XYFitCurveDock::showConstants);
  210     connect(uiGeneralTab.tbFunctions, &QToolButton::clicked, this, &XYFitCurveDock::showFunctions);
  211     connect(uiGeneralTab.pbOptions, &QPushButton::clicked, this, &XYFitCurveDock::showOptions);
  212     connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYFitCurveDock::recalculateClicked);
  213     connect(uiGeneralTab.lData, &QPushButton::clicked, this, &XYFitCurveDock::showDataOptions);
  214     connect(uiGeneralTab.lFit, &QPushButton::clicked, this, &XYFitCurveDock::showFitOptions);
  215     connect(uiGeneralTab.lParameters, &QPushButton::clicked, this, &XYFitCurveDock::showParameters);
  216     connect(uiGeneralTab.lResults, &QPushButton::clicked, this, &XYFitCurveDock::showResults);
  217 
  218     connect(cbDataSourceCurve, &TreeViewComboBox::currentModelIndexChanged, this, &XYFitCurveDock::dataSourceCurveChanged);
  219     connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFitCurveDock::xDataColumnChanged);
  220     connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFitCurveDock::yDataColumnChanged);
  221     connect(cbXErrorColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFitCurveDock::xErrorColumnChanged);
  222     connect(cbYErrorColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFitCurveDock::yErrorColumnChanged);
  223 }
  224 
  225 /*
  226  * load curve settings
  227  */
  228 void XYFitCurveDock::initGeneralTab() {
  229     //if there are more then one curve in the list, disable the tab "general"
  230     if (m_curvesList.size() == 1) {
  231         uiGeneralTab.lName->setEnabled(true);
  232         uiGeneralTab.leName->setEnabled(true);
  233         uiGeneralTab.lComment->setEnabled(true);
  234         uiGeneralTab.leComment->setEnabled(true);
  235 
  236         uiGeneralTab.leName->setText(m_curve->name());
  237         uiGeneralTab.leComment->setText(m_curve->comment());
  238     } else {
  239         uiGeneralTab.lName->setEnabled(false);
  240         uiGeneralTab.leName->setEnabled(false);
  241         uiGeneralTab.lComment->setEnabled(false);
  242         uiGeneralTab.leComment->setEnabled(false);
  243 
  244         uiGeneralTab.leName->setText(QString());
  245         uiGeneralTab.leComment->setText(QString());
  246     }
  247 
  248     auto* fitCurve = static_cast<XYFitCurve*>(m_curve);
  249     checkColumnAvailability(cbXDataColumn, fitCurve->xDataColumn(), fitCurve->xDataColumnPath());
  250     checkColumnAvailability(cbYDataColumn, fitCurve->yDataColumn(), fitCurve->yDataColumnPath());
  251     checkColumnAvailability(cbXErrorColumn, fitCurve->xErrorColumn(), fitCurve->xErrorColumnPath());
  252     checkColumnAvailability(cbYErrorColumn, fitCurve->yErrorColumn(), fitCurve->yErrorColumnPath());
  253 
  254     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(m_fitCurve->dataSourceType()));
  255     this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex());
  256     XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_fitCurve->dataSourceCurve());
  257     XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_fitCurve->xDataColumn());
  258     XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_fitCurve->yDataColumn());
  259     XYCurveDock::setModelIndexFromAspect(cbXErrorColumn, m_fitCurve->xErrorColumn());
  260     XYCurveDock::setModelIndexFromAspect(cbYErrorColumn, m_fitCurve->yErrorColumn());
  261 
  262     int tmpModelType = m_fitData.modelType; // save type because it's reset when category changes
  263     if (m_fitData.modelCategory == nsl_fit_model_custom)
  264         uiGeneralTab.cbCategory->setCurrentIndex(uiGeneralTab.cbCategory->count() - 1);
  265     else
  266         uiGeneralTab.cbCategory->setCurrentIndex(m_fitData.modelCategory);
  267     categoryChanged(m_fitData.modelCategory);   // fill model types
  268 
  269     m_fitData.modelType = tmpModelType;
  270     if (m_fitData.modelCategory != nsl_fit_model_custom)
  271         uiGeneralTab.cbModel->setCurrentIndex(m_fitData.modelType);
  272 
  273     uiGeneralTab.cbXWeight->setCurrentIndex(m_fitData.xWeightsType);
  274     uiGeneralTab.cbYWeight->setCurrentIndex(m_fitData.yWeightsType);
  275     uiGeneralTab.sbDegree->setValue(m_fitData.degree);
  276 
  277     if (m_fitData.paramStartValues.size() > 0)
  278         DEBUG(Q_FUNC_INFO << ", start value 1 = " << m_fitData.paramStartValues.at(0));
  279 
  280     DEBUG(Q_FUNC_INFO << ", model degree = " << m_fitData.degree);
  281 
  282     uiGeneralTab.chkVisible->setChecked(m_curve->isVisible());
  283 
  284     //Slots
  285     connect(m_fitCurve, &XYFitCurve::aspectDescriptionChanged, this, &XYFitCurveDock::curveDescriptionChanged);
  286     connect(m_fitCurve, &XYFitCurve::dataSourceTypeChanged, this, &XYFitCurveDock::curveDataSourceTypeChanged);
  287     connect(m_fitCurve, &XYFitCurve::dataSourceCurveChanged, this, &XYFitCurveDock::curveDataSourceCurveChanged);
  288     connect(m_fitCurve, &XYFitCurve::xDataColumnChanged, this, &XYFitCurveDock::curveXDataColumnChanged);
  289     connect(m_fitCurve, &XYFitCurve::yDataColumnChanged, this, &XYFitCurveDock::curveYDataColumnChanged);
  290     connect(m_fitCurve, &XYFitCurve::xErrorColumnChanged, this, &XYFitCurveDock::curveXErrorColumnChanged);
  291     connect(m_fitCurve, &XYFitCurve::yErrorColumnChanged, this, &XYFitCurveDock::curveYErrorColumnChanged);
  292     connect(m_fitCurve, &XYFitCurve::fitDataChanged, this, &XYFitCurveDock::curveFitDataChanged);
  293     connect(m_fitCurve, &XYFitCurve::sourceDataChanged, this, &XYFitCurveDock::enableRecalculate);
  294     connect(m_fitCurve, QOverload<bool>::of(&XYCurve::visibilityChanged), this, &XYFitCurveDock::curveVisibilityChanged);
  295 
  296     connect(fitParametersWidget, &FitParametersWidget::parametersChanged, this, &XYFitCurveDock::parametersChanged);
  297     connect(fitParametersWidget, &FitParametersWidget::parametersValid, this, &XYFitCurveDock::parametersValid);
  298 }
  299 
  300 void XYFitCurveDock::setModel() {
  301     QList<AspectType> list{AspectType::Folder, AspectType::Datapicker, AspectType::Worksheet,
  302                             AspectType::CartesianPlot, AspectType::XYCurve, AspectType::XYAnalysisCurve};
  303     cbDataSourceCurve->setTopLevelClasses(list);
  304 
  305     QList<const AbstractAspect*> hiddenAspects;
  306     for (auto* curve : m_curvesList)
  307         hiddenAspects << curve;
  308     cbDataSourceCurve->setHiddenAspects(hiddenAspects);
  309 
  310     list = {AspectType::Folder, AspectType::Workbook, AspectType::Spreadsheet, AspectType::LiveDataSource,
  311             AspectType::Column, AspectType::CantorWorksheet, AspectType::Datapicker};
  312     cbXDataColumn->setTopLevelClasses(list);
  313     cbYDataColumn->setTopLevelClasses(list);
  314     cbXErrorColumn->setTopLevelClasses(list);
  315     cbYErrorColumn->setTopLevelClasses(list);
  316 
  317     cbDataSourceCurve->setModel(m_aspectTreeModel);
  318     cbXDataColumn->setModel(m_aspectTreeModel);
  319     cbYDataColumn->setModel(m_aspectTreeModel);
  320     cbXErrorColumn->setModel(m_aspectTreeModel);
  321     cbYErrorColumn->setModel(m_aspectTreeModel);
  322 
  323     XYCurveDock::setModel();
  324 }
  325 
  326 /*!
  327   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
  328 */
  329 void XYFitCurveDock::setCurves(QList<XYCurve*> list) {
  330     m_initializing = true;
  331     m_curvesList = list;
  332     m_curve = list.first();
  333     m_aspect = m_curve;
  334     m_fitCurve = dynamic_cast<XYFitCurve*>(m_curve);
  335     m_aspectTreeModel = new AspectTreeModel(m_curve->project());
  336     this->setModel();
  337     m_fitData = m_fitCurve->fitData();
  338 
  339     DEBUG(Q_FUNC_INFO << ", model type = " << m_fitData.modelType);
  340     DEBUG(Q_FUNC_INFO << ", model = " << STDSTRING(m_fitData.model));
  341     DEBUG(Q_FUNC_INFO << ", model degree = " << m_fitData.degree);
  342     DEBUG(Q_FUNC_INFO << ", # params = " << m_fitData.paramNames.size());
  343     DEBUG(Q_FUNC_INFO << ", # start values = " << m_fitData.paramStartValues.size());
  344     //for (auto startValue: m_fitData.paramStartValues)
  345     //  DEBUG("XYFitCurveDock::setCurves()  start value = " << startValue);
  346 
  347     fitParametersWidget->setFitData(&m_fitData);
  348 
  349     initGeneralTab();
  350     initTabs();
  351 
  352     if (m_messageWidget && m_messageWidget->isVisible())
  353         m_messageWidget->close();
  354 
  355     showFitResult();
  356     enableRecalculate();
  357 
  358     m_initializing = false;
  359 
  360     //init parameter list when not available
  361     if (m_fitData.paramStartValues.size() == 0)
  362         updateModelEquation();
  363 }
  364 
  365 bool XYFitCurveDock::eventFilter(QObject* obj, QEvent* event) {
  366     if (event->type() == QEvent::KeyPress
  367         && (obj == uiGeneralTab.twParameters || obj == uiGeneralTab.twGoodness || obj == uiGeneralTab.twLog)) {
  368         auto* key_event = static_cast<QKeyEvent*>(event);
  369         if (key_event->matches(QKeySequence::Copy)) {
  370             resultCopy();
  371             return true;
  372         }
  373     }
  374     return QWidget::eventFilter(obj, event);
  375 }
  376 
  377 //*************************************************************
  378 //**** SLOTs for changes triggered in XYFitCurveDock *****
  379 //*************************************************************
  380 void XYFitCurveDock::dataSourceTypeChanged(int index) {
  381     const auto type = (XYAnalysisCurve::DataSourceType)index;
  382     if (type == XYAnalysisCurve::DataSourceType::Spreadsheet) {
  383         uiGeneralTab.lDataSourceCurve->hide();
  384         cbDataSourceCurve->hide();
  385         uiGeneralTab.lXColumn->show();
  386         cbXDataColumn->show();
  387         uiGeneralTab.lYColumn->show();
  388         cbYDataColumn->show();
  389     } else {
  390         uiGeneralTab.lDataSourceCurve->show();
  391         cbDataSourceCurve->show();
  392         uiGeneralTab.lXColumn->hide();
  393         cbXDataColumn->hide();
  394         uiGeneralTab.lYColumn->hide();
  395         cbYDataColumn->hide();
  396     }
  397 
  398     if (m_initializing)
  399         return;
  400 
  401     for (auto* curve : m_curvesList)
  402         dynamic_cast<XYFitCurve*>(curve)->setDataSourceType(type);
  403 }
  404 
  405 void XYFitCurveDock::dataSourceCurveChanged(const QModelIndex& index) {
  406     if (m_initializing)
  407         return;
  408 
  409     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
  410     auto* dataSourceCurve = dynamic_cast<XYCurve*>(aspect);
  411 
  412     for (auto* curve : m_curvesList)
  413         dynamic_cast<XYFitCurve*>(curve)->setDataSourceCurve(dataSourceCurve);
  414 }
  415 
  416 void XYFitCurveDock::xDataColumnChanged(const QModelIndex& index) {
  417     if (m_initializing)
  418         return;
  419 
  420     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
  421     auto* column = dynamic_cast<AbstractColumn*>(aspect);
  422 
  423     for (auto* curve : m_curvesList)
  424         dynamic_cast<XYFitCurve*>(curve)->setXDataColumn(column);
  425 
  426     // set model dependent start values from new data
  427     XYFitCurve::initStartValues(m_fitData, m_curve);
  428 
  429     // update model limits depending on number of points
  430     modelTypeChanged(uiGeneralTab.cbModel->currentIndex());
  431 
  432     cbXDataColumn->useCurrentIndexText(true);
  433     cbXDataColumn->setInvalid(false);
  434 }
  435 
  436 void XYFitCurveDock::yDataColumnChanged(const QModelIndex& index) {
  437     if (m_initializing)
  438         return;
  439 
  440     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
  441     auto* column = dynamic_cast<AbstractColumn*>(aspect);
  442 
  443     for (auto* curve : m_curvesList)
  444         dynamic_cast<XYFitCurve*>(curve)->setYDataColumn(column);
  445 
  446     // set model dependent start values from new data
  447     XYFitCurve::initStartValues(m_fitData, m_curve);
  448 
  449     cbYDataColumn->useCurrentIndexText(true);
  450     cbYDataColumn->setInvalid(false);
  451 }
  452 
  453 void XYFitCurveDock::xErrorColumnChanged(const QModelIndex& index) {
  454     if (m_initializing)
  455         return;
  456 
  457     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
  458     auto* column = dynamic_cast<AbstractColumn*>(aspect);
  459 
  460     for (auto* curve : m_curvesList)
  461         dynamic_cast<XYFitCurve*>(curve)->setXErrorColumn(column);
  462 
  463     cbXErrorColumn->useCurrentIndexText(true);
  464     cbXErrorColumn->setInvalid(false);
  465 }
  466 
  467 void XYFitCurveDock::yErrorColumnChanged(const QModelIndex& index) {
  468     if (m_initializing)
  469         return;
  470 
  471     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
  472     auto* column = dynamic_cast<AbstractColumn*>(aspect);
  473 
  474     for (auto* curve : m_curvesList)
  475         dynamic_cast<XYFitCurve*>(curve)->setYErrorColumn(column);
  476 
  477     cbYErrorColumn->useCurrentIndexText(true);
  478     cbYErrorColumn->setInvalid(false);
  479 }
  480 
  481 ///////////////////////// fold/unfold options //////////////////////////////////////////////////
  482 
  483 void XYFitCurveDock::showDataOptions(bool checked) {
  484     if (checked) {
  485         uiGeneralTab.lData->setIcon(QIcon::fromTheme("arrow-down"));
  486         uiGeneralTab.lDataSourceType->show();
  487         uiGeneralTab.cbDataSourceType->show();
  488         // select options for current source type
  489         dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex());
  490     } else {
  491         uiGeneralTab.lData->setIcon(QIcon::fromTheme("arrow-right"));
  492         uiGeneralTab.lDataSourceType->hide();
  493         uiGeneralTab.cbDataSourceType->hide();
  494         uiGeneralTab.lXColumn->hide();
  495         cbXDataColumn->hide();
  496         uiGeneralTab.lYColumn->hide();
  497         cbYDataColumn->hide();
  498         uiGeneralTab.lDataSourceCurve->hide();
  499         cbDataSourceCurve->hide();
  500     }
  501 }
  502 
  503 void XYFitCurveDock::showWeightsOptions(bool checked) {
  504     if (checked) {
  505         uiGeneralTab.lWeights->setIcon(QIcon::fromTheme("arrow-down"));
  506         uiGeneralTab.lXWeight->show();
  507         uiGeneralTab.cbXWeight->show();
  508         uiGeneralTab.lXErrorCol->show();
  509         cbXErrorColumn->show();
  510         uiGeneralTab.lYWeight->show();
  511         uiGeneralTab.cbYWeight->show();
  512         uiGeneralTab.lYErrorCol->show();
  513         cbYErrorColumn->show();
  514     } else {
  515         uiGeneralTab.lWeights->setIcon(QIcon::fromTheme("arrow-right"));
  516         uiGeneralTab.lXWeight->hide();
  517         uiGeneralTab.cbXWeight->hide();
  518         uiGeneralTab.lXErrorCol->hide();
  519         cbXErrorColumn->hide();
  520         uiGeneralTab.lYWeight->hide();
  521         uiGeneralTab.cbYWeight->hide();
  522         uiGeneralTab.lYErrorCol->hide();
  523         cbYErrorColumn->hide();
  524     }
  525 }
  526 
  527 void XYFitCurveDock::showFitOptions(bool checked) {
  528     if (checked) {
  529         uiGeneralTab.lFit->setIcon(QIcon::fromTheme("arrow-down"));
  530         uiGeneralTab.lCategory->show();
  531         uiGeneralTab.cbCategory->show();
  532         uiGeneralTab.lModel->show();
  533         uiGeneralTab.cbModel->show();
  534         uiGeneralTab.lEquation->show();
  535 
  536         m_initializing = true;  // do not change start parameter
  537         modelTypeChanged(uiGeneralTab.cbModel->currentIndex());
  538         m_initializing = false;
  539     } else {
  540         uiGeneralTab.lFit->setIcon(QIcon::fromTheme("arrow-right"));
  541         uiGeneralTab.lCategory->hide();
  542         uiGeneralTab.cbCategory->hide();
  543         uiGeneralTab.lModel->hide();
  544         uiGeneralTab.cbModel->hide();
  545         uiGeneralTab.lDegree->hide();
  546         uiGeneralTab.sbDegree->hide();
  547         uiGeneralTab.lEquation->hide();
  548         uiGeneralTab.lFuncPic->hide();
  549         uiGeneralTab.teEquation->hide();
  550         uiGeneralTab.tbFunctions->hide();
  551         uiGeneralTab.tbConstants->hide();
  552     }
  553 }
  554 
  555 void XYFitCurveDock::showParameters(bool checked) {
  556     if (checked) {
  557         uiGeneralTab.lParameters->setIcon(QIcon::fromTheme("arrow-down"));
  558         uiGeneralTab.frameParameters->show();
  559     } else {
  560         uiGeneralTab.lParameters->setIcon(QIcon::fromTheme("arrow-right"));
  561         uiGeneralTab.frameParameters->hide();
  562     }
  563 }
  564 
  565 void XYFitCurveDock::showResults(bool checked) {
  566     if (checked) {
  567         uiGeneralTab.lResults->setIcon(QIcon::fromTheme("arrow-down"));
  568         uiGeneralTab.twResults->show();
  569     } else {
  570         uiGeneralTab.lResults->setIcon(QIcon::fromTheme("arrow-right"));
  571         uiGeneralTab.twResults->hide();
  572     }
  573 }
  574 
  575 ///////////////////////////////////////////////////////////////////////////
  576 
  577 void XYFitCurveDock::xWeightChanged(int index) {
  578     DEBUG(Q_FUNC_INFO << ", weight = " << nsl_fit_weight_type_name[index]);
  579 
  580     m_fitData.xWeightsType = (nsl_fit_weight_type)index;
  581 
  582     // enable/disable weight column
  583     switch ((nsl_fit_weight_type)index) {
  584     case nsl_fit_weight_no:
  585     case nsl_fit_weight_statistical:
  586     case nsl_fit_weight_statistical_fit:
  587     case nsl_fit_weight_relative:
  588     case nsl_fit_weight_relative_fit:
  589         cbXErrorColumn->setEnabled(false);
  590         uiGeneralTab.lXErrorCol->setEnabled(false);
  591         break;
  592     case nsl_fit_weight_instrumental:
  593     case nsl_fit_weight_direct:
  594     case nsl_fit_weight_inverse:
  595         cbXErrorColumn->setEnabled(true);
  596         uiGeneralTab.lXErrorCol->setEnabled(true);
  597         break;
  598     }
  599     enableRecalculate();
  600 }
  601 
  602 void XYFitCurveDock::yWeightChanged(int index) {
  603     DEBUG(Q_FUNC_INFO << ", weight = " << nsl_fit_weight_type_name[index]);
  604 
  605     m_fitData.yWeightsType = (nsl_fit_weight_type)index;
  606 
  607     // enable/disable weight column
  608     switch ((nsl_fit_weight_type)index) {
  609     case nsl_fit_weight_no:
  610     case nsl_fit_weight_statistical:
  611     case nsl_fit_weight_statistical_fit:
  612     case nsl_fit_weight_relative:
  613     case nsl_fit_weight_relative_fit:
  614         cbYErrorColumn->setEnabled(false);
  615         uiGeneralTab.lYErrorCol->setEnabled(false);
  616         break;
  617     case nsl_fit_weight_instrumental:
  618     case nsl_fit_weight_direct:
  619     case nsl_fit_weight_inverse:
  620         cbYErrorColumn->setEnabled(true);
  621         uiGeneralTab.lYErrorCol->setEnabled(true);
  622         break;
  623     }
  624     enableRecalculate();
  625 }
  626 
  627 /*!
  628  * called when the fit model category (basic functions, peak functions etc.) was changed.
  629  * In the combobox for the model type shows the model types for the current category \index and calls \c modelTypeChanged()
  630  * to update the model type dependent widgets in the general-tab.
  631  */
  632 void XYFitCurveDock::categoryChanged(int index) {
  633     if (index == nsl_fit_model_custom) {
  634         DEBUG(Q_FUNC_INFO << ", category = \"nsl_fit_model_custom\"");
  635     } else {
  636         DEBUG(Q_FUNC_INFO << ", category = \"" << nsl_fit_model_category_name[index] << "\"");
  637     }
  638 
  639     bool hasChanged = true;
  640     // nothing has changed when ...
  641     if (m_fitData.modelCategory == (nsl_fit_model_category)index || (m_fitData.modelCategory == nsl_fit_model_custom && index == uiGeneralTab.cbCategory->count() - 1) )
  642         hasChanged = false;
  643 
  644     if (uiGeneralTab.cbCategory->currentIndex() == uiGeneralTab.cbCategory->count() - 1)
  645         m_fitData.modelCategory = nsl_fit_model_custom;
  646     else
  647         m_fitData.modelCategory = (nsl_fit_model_category)index;
  648 
  649     uiGeneralTab.cbModel->clear();
  650     uiGeneralTab.cbModel->show();
  651     uiGeneralTab.lModel->show();
  652 
  653     switch (m_fitData.modelCategory) {
  654     case nsl_fit_model_basic:
  655         for (int i = 0; i < NSL_FIT_MODEL_BASIC_COUNT; i++)
  656             uiGeneralTab.cbModel->addItem(nsl_fit_model_basic_name[i]);
  657         break;
  658     case nsl_fit_model_peak: {
  659         for (int i = 0; i < NSL_FIT_MODEL_PEAK_COUNT; i++)
  660             uiGeneralTab.cbModel->addItem(nsl_fit_model_peak_name[i]);
  661 #if defined(_MSC_VER)
  662         // disable voigt model
  663         const QStandardItemModel* model = qobject_cast<const QStandardItemModel*>(uiGeneralTab.cbModel->model());
  664         QStandardItem* item = model->item(nsl_fit_model_voigt);
  665         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
  666 #endif
  667         break;
  668     }
  669     case nsl_fit_model_growth:
  670         for (int i = 0; i < NSL_FIT_MODEL_GROWTH_COUNT; i++)
  671             uiGeneralTab.cbModel->addItem(nsl_fit_model_growth_name[i]);
  672         break;
  673     case nsl_fit_model_distribution: {
  674         for (int i = 0; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++)
  675             uiGeneralTab.cbModel->addItem(nsl_sf_stats_distribution_name[i]);
  676 
  677         // not-used items are disabled here
  678         const auto* model = qobject_cast<const QStandardItemModel*>(uiGeneralTab.cbModel->model());
  679 
  680         for (int i = 1; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) {
  681             // unused distributions
  682             if (i == nsl_sf_stats_levy_alpha_stable || i == nsl_sf_stats_levy_skew_alpha_stable || i == nsl_sf_stats_bernoulli) {
  683                     QStandardItem* item = model->item(i);
  684                     item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
  685             }
  686         }
  687         break;
  688     }
  689     case nsl_fit_model_custom:
  690         uiGeneralTab.cbModel->addItem(i18n("Custom"));
  691         uiGeneralTab.cbModel->hide();
  692         uiGeneralTab.lModel->hide();
  693     }
  694 
  695     if (hasChanged) {
  696         //show the fit-model for the currently selected default (first) fit-model
  697         uiGeneralTab.cbModel->setCurrentIndex(0);
  698         uiGeneralTab.sbDegree->setValue(1);
  699         // when model type does not change, call it here
  700         updateModelEquation();
  701     }
  702 
  703     enableRecalculate();
  704 }
  705 
  706 /*!
  707  * called when the fit model type (depends on category) was changed.
  708  * Updates the model type dependent widgets in the general-tab and calls \c updateModelEquation() to update the preview pixmap.
  709  */
  710 void XYFitCurveDock::modelTypeChanged(int index) {
  711     DEBUG("modelTypeChanged() type = " << (unsigned int)index << ", initializing = " << m_initializing << ", current type = " << m_fitData.modelType);
  712     // leave if there is no selection
  713     if (index == -1)
  714         return;
  715 
  716     bool custom = false;
  717     if (m_fitData.modelCategory == nsl_fit_model_custom)
  718         custom = true;
  719     uiGeneralTab.teEquation->setReadOnly(!custom);
  720     uiGeneralTab.lModel->setVisible(!custom);
  721     uiGeneralTab.cbModel->setVisible(!custom);
  722     uiGeneralTab.tbFunctions->setVisible(custom);
  723     uiGeneralTab.tbConstants->setVisible(custom);
  724 
  725     // default settings
  726     uiGeneralTab.lDegree->setText(i18n("Degree:"));
  727     if (m_fitData.modelType != index)
  728         uiGeneralTab.sbDegree->setValue(1);
  729 
  730     const AbstractColumn* xColumn = nullptr;
  731     if (m_fitCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) {
  732         DEBUG(" data source: Spreadsheet")
  733         //auto* aspect = static_cast<AbstractAspect*>(cbXDataColumn->currentModelIndex().internalPointer());
  734         //xColumn = dynamic_cast<AbstractColumn*>(aspect);
  735         xColumn = m_fitCurve->xDataColumn();
  736     } else {
  737         DEBUG(" data source: Curve")
  738         if (m_fitCurve->dataSourceCurve() != nullptr)
  739             xColumn = m_fitCurve->dataSourceCurve()->xColumn();
  740     }
  741     // with no xColumn: show all models (assume 100 data points)
  742     const int availableRowCount = (xColumn != nullptr) ? xColumn->availableRowCount() : 100;
  743     DEBUG(" available row count = " << availableRowCount)
  744 
  745     bool disableFit = false;
  746     switch (m_fitData.modelCategory) {
  747     case nsl_fit_model_basic:
  748         switch (index) {
  749         case nsl_fit_model_polynomial:
  750             uiGeneralTab.lDegree->setVisible(true);
  751             uiGeneralTab.sbDegree->setVisible(true);
  752             uiGeneralTab.sbDegree->setMaximum(qMin(availableRowCount - 1, 10));
  753             break;
  754         case nsl_fit_model_fourier:
  755             if (availableRowCount < 4) {    // too few data points
  756                 uiGeneralTab.lDegree->setVisible(false);
  757                 uiGeneralTab.sbDegree->setVisible(false);
  758                 disableFit = true;
  759             } else {
  760                 uiGeneralTab.lDegree->setVisible(true);
  761                 uiGeneralTab.sbDegree->setVisible(true);
  762                 uiGeneralTab.sbDegree->setMaximum(qMin(availableRowCount/2 - 1, 10));
  763             }
  764             break;
  765         case nsl_fit_model_power:
  766             uiGeneralTab.lDegree->setVisible(true);
  767             uiGeneralTab.sbDegree->setVisible(true);
  768             uiGeneralTab.sbDegree->setMaximum(2);
  769             //TODO: limit degree depending on availableRowCount
  770             break;
  771         case nsl_fit_model_exponential:
  772             uiGeneralTab.lDegree->setVisible(true);
  773             uiGeneralTab.sbDegree->setVisible(true);
  774             uiGeneralTab.sbDegree->setMaximum(10);
  775             //TODO: limit degree depending on availableRowCount
  776             break;
  777         default:
  778             uiGeneralTab.lDegree->setVisible(false);
  779             uiGeneralTab.sbDegree->setVisible(false);
  780         }
  781         break;
  782     case nsl_fit_model_peak:    // all models support multiple peaks
  783         uiGeneralTab.lDegree->setText(i18n("Number of peaks:"));
  784         uiGeneralTab.lDegree->setVisible(true);
  785         uiGeneralTab.sbDegree->setVisible(true);
  786         uiGeneralTab.sbDegree->setMaximum(9);
  787         break;
  788     case nsl_fit_model_growth:
  789     case nsl_fit_model_distribution:
  790     case nsl_fit_model_custom:
  791         uiGeneralTab.lDegree->setVisible(false);
  792         uiGeneralTab.sbDegree->setVisible(false);
  793     }
  794 
  795     m_fitData.modelType = index;
  796 
  797     updateModelEquation();
  798 
  799     if (disableFit)
  800         uiGeneralTab.pbRecalculate->setEnabled(false);
  801 }
  802 
  803 /*!
  804  * Show the preview pixmap of the fit model expression for the current model category and type.
  805  * Called when the model type or the degree of the model were changed.
  806  */
  807 void XYFitCurveDock::updateModelEquation() {
  808 
  809     if (m_fitData.modelCategory == nsl_fit_model_custom) {
  810         DEBUG("XYFitCurveDock::updateModelEquation() category = nsl_fit_model_custom, type = " << m_fitData.modelType);
  811     } else {
  812         DEBUG("XYFitCurveDock::updateModelEquation() category = " << nsl_fit_model_category_name[m_fitData.modelCategory] << ", type = " << m_fitData.modelType);
  813     }
  814 
  815     //this function can also be called when the value for the degree was changed -> update the fit data structure
  816     int degree = uiGeneralTab.sbDegree->value();
  817     if (!m_initializing) {
  818         m_fitData.degree = degree;
  819         XYFitCurve::initFitData(m_fitData);
  820         // set model dependent start values from curve data
  821         XYFitCurve::initStartValues(m_fitData, m_curve);
  822         // udpate parameter widget
  823         fitParametersWidget->setFitData(&m_fitData);
  824     }
  825 
  826     // variables/parameter that are known
  827     QStringList vars = {"x"};
  828     vars << m_fitData.paramNames;
  829     uiGeneralTab.teEquation->setVariables(vars);
  830 
  831     // set formula picture
  832     uiGeneralTab.lEquation->setText(QLatin1String("f(x) ="));
  833     QString file;
  834     switch (m_fitData.modelCategory) {
  835     case nsl_fit_model_basic: {
  836         // formula pic depends on degree
  837         QString numSuffix = QString::number(degree);
  838         if (degree > 4)
  839             numSuffix = '4';
  840         if ((nsl_fit_model_type_basic)m_fitData.modelType == nsl_fit_model_power && degree > 2)
  841             numSuffix = '2';
  842         file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/"
  843             + QString(nsl_fit_model_basic_pic_name[m_fitData.modelType]) + numSuffix + ".png");
  844         break;
  845     }
  846     case nsl_fit_model_peak: {
  847         // formula pic depends on number of peaks
  848         QString numSuffix = QString::number(degree);
  849         if (degree > 4)
  850             numSuffix = '4';
  851         file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/"
  852             + QString(nsl_fit_model_peak_pic_name[m_fitData.modelType]) + numSuffix + ".png");
  853         break;
  854     }
  855     case nsl_fit_model_growth:
  856         file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/"
  857             + QString(nsl_fit_model_growth_pic_name[m_fitData.modelType]) + ".png");
  858         break;
  859     case nsl_fit_model_distribution:
  860         file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/gsl_distributions/"
  861             + QString(nsl_sf_stats_distribution_pic_name[m_fitData.modelType]) + ".png");
  862         // change label
  863         if (m_fitData.modelType == nsl_sf_stats_poisson)
  864             uiGeneralTab.lEquation->setText(QLatin1String("f(k)/A ="));
  865         else
  866             uiGeneralTab.lEquation->setText(QLatin1String("f(x)/A ="));
  867         break;
  868     case nsl_fit_model_custom:
  869         uiGeneralTab.lFuncPic->hide();
  870         uiGeneralTab.teEquation->show();
  871         uiGeneralTab.teEquation->setPlainText(m_fitData.model);
  872     }
  873 
  874     if (m_fitData.modelCategory != nsl_fit_model_custom) {
  875         DEBUG("Model pixmap path = " << STDSTRING(file));
  876         uiGeneralTab.lFuncPic->setPixmap(file);
  877         uiGeneralTab.lFuncPic->show();
  878         uiGeneralTab.teEquation->hide();
  879     }
  880 
  881     enableRecalculate();
  882 }
  883 
  884 void XYFitCurveDock::showConstants() {
  885     QMenu menu;
  886     ConstantsWidget constants(&menu);
  887 
  888     connect(&constants, &ConstantsWidget::constantSelected, this, &XYFitCurveDock::insertConstant);
  889     connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close);
  890     connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close);
  891 
  892     auto* widgetAction = new QWidgetAction(this);
  893     widgetAction->setDefaultWidget(&constants);
  894     menu.addAction(widgetAction);
  895 
  896     QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbConstants->width(), -menu.sizeHint().height());
  897     menu.exec(uiGeneralTab.tbConstants->mapToGlobal(pos));
  898 }
  899 
  900 void XYFitCurveDock::showFunctions() {
  901     QMenu menu;
  902     FunctionsWidget functions(&menu);
  903     connect(&functions, &FunctionsWidget::functionSelected, this, &XYFitCurveDock::insertFunction);
  904     connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close);
  905     connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close);
  906 
  907     auto* widgetAction = new QWidgetAction(this);
  908     widgetAction->setDefaultWidget(&functions);
  909     menu.addAction(widgetAction);
  910 
  911     QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbFunctions->width(), -menu.sizeHint().height());
  912     menu.exec(uiGeneralTab.tbFunctions->mapToGlobal(pos));
  913 }
  914 
  915 /*!
  916  * Update parameter by parsing expression
  917  * Only called for custom fit model
  918  */
  919 void XYFitCurveDock::updateParameterList() {
  920     DEBUG("XYFitCurveDock::updateParameterList()");
  921     // use current model function
  922     m_fitData.model = uiGeneralTab.teEquation->toPlainText();
  923 
  924     ExpressionParser* parser = ExpressionParser::getInstance();
  925     QStringList vars; // variables that are known
  926     vars << "x";    //TODO: generalize when we support other XYEquationCurve::EquationType
  927     m_fitData.paramNames = m_fitData.paramNamesUtf8 = parser->getParameter(m_fitData.model, vars);
  928 
  929     // if number of parameter changed
  930     int oldNumberOfParameter = m_fitData.paramStartValues.size();
  931     int numberOfParameter = m_fitData.paramNames.size();
  932     DEBUG(" old number of parameter: " << oldNumberOfParameter << " new number of parameter: " << numberOfParameter);
  933     if (numberOfParameter != oldNumberOfParameter) {
  934         m_fitData.paramStartValues.resize(numberOfParameter);
  935         m_fitData.paramFixed.resize(numberOfParameter);
  936         m_fitData.paramLowerLimits.resize(numberOfParameter);
  937         m_fitData.paramUpperLimits.resize(numberOfParameter);
  938     }
  939     if (numberOfParameter > oldNumberOfParameter) {
  940         for (int i = oldNumberOfParameter; i < numberOfParameter; ++i) {
  941             m_fitData.paramStartValues[i] = 1.0;
  942             m_fitData.paramFixed[i] = false;
  943             m_fitData.paramLowerLimits[i] = -std::numeric_limits<double>::max();
  944             m_fitData.paramUpperLimits[i] = std::numeric_limits<double>::max();
  945         }
  946     }
  947 
  948     parametersChanged();
  949 }
  950 
  951 /*!
  952  * called when parameter names and/or start values for the model were changed
  953  * also called from parameter widget
  954  */
  955 void XYFitCurveDock::parametersChanged(bool updateParameterWidget) {
  956     DEBUG("XYFitCurveDock::parametersChanged() m_initializing = " << m_initializing);
  957 
  958     //parameter names were (probably) changed -> set the new vars in ExpressionTextEdit teEquation
  959     QStringList vars{m_fitData.paramNames};
  960     vars << "x";    //TODO: generalize when we support other XYEquationCurve::EquationType
  961     uiGeneralTab.teEquation->setVariables(vars);
  962 
  963     if (m_initializing)
  964         return;
  965 
  966     if (updateParameterWidget)
  967         fitParametersWidget->setFitData(&m_fitData);
  968 
  969     enableRecalculate();
  970 }
  971 void XYFitCurveDock::parametersValid(bool valid) {
  972     DEBUG("XYFitCurveDock::parametersValid() valid = " << valid);
  973     m_parametersValid = valid;
  974 }
  975 
  976 void XYFitCurveDock::showOptions() {
  977     QMenu menu;
  978     FitOptionsWidget w(&menu, &m_fitData, m_fitCurve);
  979     connect(&w, &FitOptionsWidget::finished, &menu, &QMenu::close);
  980     connect(&w, &FitOptionsWidget::optionsChanged, this, &XYFitCurveDock::enableRecalculate);
  981 
  982     auto* widgetAction = new QWidgetAction(this);
  983     widgetAction->setDefaultWidget(&w);
  984     menu.addAction(widgetAction);
  985     menu.setTearOffEnabled(true);
  986 
  987     //menu.setWindowFlags(menu.windowFlags() & Qt::MSWindowsFixedSizeDialogHint);
  988 
  989     QPoint pos(-menu.sizeHint().width() + uiGeneralTab.pbOptions->width(), 0);
  990     menu.exec(uiGeneralTab.pbOptions->mapToGlobal(pos));
  991 }
  992 
  993 void XYFitCurveDock::insertFunction(const QString& functionName) const {
  994     uiGeneralTab.teEquation->insertPlainText(functionName + ExpressionParser::functionArgumentString(functionName, XYEquationCurve::EquationType::Cartesian));
  995 }
  996 
  997 void XYFitCurveDock::insertConstant(const QString& constantsName) const {
  998     uiGeneralTab.teEquation->insertPlainText(constantsName);
  999 }
 1000 
 1001 /*!
 1002  * When a custom evaluate range is specified, set the plot range too.
 1003  */
 1004 void XYFitCurveDock::setPlotXRange() {
 1005     if (m_fitData.autoEvalRange || m_curve == nullptr)
 1006         return;
 1007 
 1008     auto* plot = dynamic_cast<CartesianPlot*>(m_curve->parentAspect());
 1009     if (plot != nullptr) {
 1010         const Range<double> range{ m_fitData.evalRange };
 1011         const double extend{ range.size() * 0.05 }; // + 5 %
 1012         if (!range.isZero()) {
 1013             plot->setXMin(range.min() - extend);
 1014             plot->setXMax(range.max() + extend);
 1015         }
 1016     }
 1017 }
 1018 
 1019 void XYFitCurveDock::recalculateClicked() {
 1020     DEBUG("XYFitCurveDock::recalculateClicked()");
 1021     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
 1022     m_fitData.degree = uiGeneralTab.sbDegree->value();
 1023     if (m_fitData.modelCategory == nsl_fit_model_custom)
 1024         updateParameterList();
 1025 
 1026     for (XYCurve* curve: m_curvesList)
 1027         dynamic_cast<XYFitCurve*>(curve)->setFitData(m_fitData);
 1028 
 1029     m_fitCurve->recalculate();
 1030     setPlotXRange();
 1031 
 1032     //update fitParametersWidget
 1033     if (m_fitData.useResults) {
 1034         DEBUG(" nr of param names = " << m_fitData.paramNames.size())
 1035         DEBUG(" size of start values = " << m_fitData.paramStartValues.size())
 1036         DEBUG(" size of param values = " << m_fitCurve->fitResult().paramValues.size())
 1037         if (m_fitCurve->fitResult().paramValues.size() > 0) {   // may be 0 if fit fails
 1038             for (int i = 0; i < m_fitData.paramNames.size(); i++)
 1039                 m_fitData.paramStartValues[i] = m_fitCurve->fitResult().paramValues.at(i);
 1040             fitParametersWidget->setFitData(&m_fitData);
 1041         } else {
 1042             DEBUG(" WARNING: no fit result available!")
 1043         }
 1044     }
 1045 
 1046     this->showFitResult();
 1047     uiGeneralTab.pbRecalculate->setEnabled(false);
 1048 
 1049     //show the warning/error message, if available
 1050     const XYFitCurve::FitResult& fitResult = m_fitCurve->fitResult();
 1051     const QString& status = fitResult.status;
 1052     if (status != i18n("Success")) {
 1053         emit info(i18n("Fit status: %1", fitResult.status));
 1054         if (!m_messageWidget) {
 1055             m_messageWidget = new KMessageWidget(this);
 1056             uiGeneralTab.gridLayout_2->addWidget(m_messageWidget, 25, 3, 1, 4);
 1057         }
 1058 
 1059         if (!fitResult.valid)
 1060             m_messageWidget->setMessageType(KMessageWidget::Error);
 1061         else
 1062             m_messageWidget->setMessageType(KMessageWidget::Warning);
 1063         m_messageWidget->setText(status);
 1064         m_messageWidget->animatedShow();
 1065     } else {
 1066         if (m_messageWidget && m_messageWidget->isVisible())
 1067             m_messageWidget->close();
 1068     }
 1069 
 1070     QApplication::restoreOverrideCursor();
 1071     DEBUG("XYFitCurveDock::recalculateClicked() DONE");
 1072 }
 1073 
 1074 void XYFitCurveDock::expressionChanged() {
 1075     DEBUG("XYFitCurveDock::expressionChanged()");
 1076     if (m_initializing)
 1077         return;
 1078 
 1079     // update parameter list for custom model
 1080     if (m_fitData.modelCategory == nsl_fit_model_custom)
 1081         updateParameterList();
 1082 
 1083     enableRecalculate();
 1084 }
 1085 
 1086 void XYFitCurveDock::enableRecalculate() {
 1087     DEBUG("XYFitCurveDock::enableRecalculate()");
 1088     if (m_initializing || m_fitCurve == nullptr)
 1089         return;
 1090 
 1091     //no fitting possible without the x- and y-data
 1092     bool hasSourceData = false;
 1093     if (m_fitCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) {
 1094         auto* aspectX = static_cast<AbstractAspect*>(cbXDataColumn->currentModelIndex().internalPointer());
 1095         auto* aspectY = static_cast<AbstractAspect*>(cbYDataColumn->currentModelIndex().internalPointer());
 1096         hasSourceData = (aspectX != nullptr && aspectY != nullptr);
 1097         if (aspectX) {
 1098             cbXDataColumn->useCurrentIndexText(true);
 1099             cbXDataColumn->setInvalid(false);
 1100         }
 1101         if (aspectY) {
 1102             cbYDataColumn->useCurrentIndexText(true);
 1103             cbYDataColumn->setInvalid(false);
 1104         }
 1105     } else {
 1106         hasSourceData = (m_fitCurve->dataSourceCurve() != nullptr);
 1107     }
 1108 
 1109     uiGeneralTab.pbRecalculate->setEnabled(hasSourceData && m_parametersValid);
 1110 
 1111     // PREVIEW as soon as recalculate is enabled (does not need source data)
 1112     if (m_parametersValid && m_fitData.previewEnabled) {
 1113         DEBUG(" EVALUATE WITH PREVIEW ENABLED");
 1114         // use recent fit data
 1115         m_fitCurve->setFitData(m_fitData);
 1116         // calculate fit function
 1117         m_fitCurve->evaluate(true);
 1118         setPlotXRange();
 1119     }
 1120     else {
 1121         DEBUG(" PREVIEW DISABLED");
 1122     }
 1123     DEBUG("XYFitCurveDock::enableRecalculate() DONE");
 1124 }
 1125 
 1126 void XYFitCurveDock::resultCopy(bool copyAll) {
 1127     QTableWidget* tw{nullptr};
 1128     int currentTab = uiGeneralTab.twResults->currentIndex();
 1129     if (currentTab == 0)
 1130         tw = uiGeneralTab.twParameters;
 1131     else if (currentTab == 1)
 1132         tw = uiGeneralTab.twGoodness;
 1133     else if (currentTab == 2)
 1134         tw = uiGeneralTab.twLog;
 1135     else
 1136         return;
 1137 
 1138     QString str;
 1139     QString rowStr;
 1140 
 1141     //copy the header of the parameters table if we copy everything in this table
 1142     if (copyAll && tw == uiGeneralTab.twParameters) {
 1143         for (int i = 1; i < tw->columnCount(); ++i)
 1144             str += QLatin1Char('\t') + tw->horizontalHeaderItem(i)->text();
 1145     }
 1146 
 1147     //copy the content of the table
 1148     for (int i = 0; i < tw->rowCount(); ++i) {
 1149         for (int j = 0; j < tw->columnCount(); ++j) {
 1150             if (!tw->item(i, j))
 1151                 continue;
 1152             if (!copyAll && !tw->item(i, j)->isSelected())
 1153                 continue;
 1154 
 1155             if (!rowStr.isEmpty())
 1156                 rowStr += QLatin1Char('\t');
 1157 
 1158             rowStr += tw->item(i, j)->text();
 1159         }
 1160         if (!rowStr.isEmpty()) {
 1161             if (!str.isEmpty())
 1162                 str += QLatin1Char('\n');
 1163             str += rowStr;
 1164             rowStr.clear();
 1165         }
 1166     }
 1167 
 1168     QApplication::clipboard()->setText(str);
 1169     DEBUG(STDSTRING(QApplication::clipboard()->text()));
 1170 }
 1171 
 1172 void XYFitCurveDock::resultCopyAll() {
 1173     resultCopy(true);
 1174 }
 1175 
 1176 void XYFitCurveDock::resultParametersContextMenuRequest(QPoint pos) {
 1177     auto* contextMenu = new QMenu;
 1178     contextMenu->addAction(i18n("Copy Selection"), this, &XYFitCurveDock::resultCopy, QKeySequence::Copy);
 1179     contextMenu->addAction(i18n("Copy All"), this, &XYFitCurveDock::resultCopyAll);
 1180     contextMenu->exec(uiGeneralTab.twParameters->mapToGlobal(pos));
 1181 }
 1182 void XYFitCurveDock::resultGoodnessContextMenuRequest(QPoint pos) {
 1183     auto* contextMenu = new QMenu;
 1184     contextMenu->addAction(i18n("Copy Selection"), this, &XYFitCurveDock::resultCopy, QKeySequence::Copy);
 1185     contextMenu->addAction(i18n("Copy All"), this, &XYFitCurveDock::resultCopyAll);
 1186     contextMenu->exec(uiGeneralTab.twGoodness->mapToGlobal(pos));
 1187 }
 1188 void XYFitCurveDock::resultLogContextMenuRequest(QPoint pos) {
 1189     auto* contextMenu = new QMenu;
 1190     contextMenu->addAction(i18n("Copy Selection"), this, &XYFitCurveDock::resultCopy, QKeySequence::Copy);
 1191     contextMenu->addAction(i18n("Copy All"), this, &XYFitCurveDock::resultCopyAll);
 1192     contextMenu->exec(uiGeneralTab.twLog->mapToGlobal(pos));
 1193 }
 1194 
 1195 /*!
 1196  * show the result and details of the fit
 1197  */
 1198 void XYFitCurveDock::showFitResult() {
 1199     //clear the previous result
 1200     uiGeneralTab.twParameters->setRowCount(0);
 1201     for (int row = 0; row < uiGeneralTab.twGoodness->rowCount(); ++row)
 1202         uiGeneralTab.twGoodness->item(row, 1)->setText(QString());
 1203     for (int row = 0; row < uiGeneralTab.twLog->rowCount(); ++row)
 1204         uiGeneralTab.twLog->item(row, 1)->setText(QString());
 1205 
 1206     const auto& fitResult = m_fitCurve->fitResult();
 1207 
 1208     if (!fitResult.available) {
 1209         DEBUG(Q_FUNC_INFO << ", fit result not available");
 1210         return;
 1211     }
 1212 
 1213     // Log
 1214     uiGeneralTab.twLog->item(0, 1)->setText(fitResult.status);
 1215 
 1216     if (!fitResult.valid) {
 1217         DEBUG(Q_FUNC_INFO << ", fit result not valid");
 1218         return;
 1219     }
 1220 
 1221     SET_NUMBER_LOCALE
 1222     // used confidence interval
 1223     double confidenceInterval{m_fitData.confidenceInterval};
 1224     uiGeneralTab.twParameters->horizontalHeaderItem(6)->setToolTip(i18n("%1 % lower confidence level", numberLocale.toString(confidenceInterval, 'g', 7)));
 1225     uiGeneralTab.twParameters->horizontalHeaderItem(7)->setToolTip(i18n("%1 % upper confidence level", numberLocale.toString(confidenceInterval, 'g', 7)));
 1226 
 1227     // log
 1228     uiGeneralTab.twLog->item(1, 1)->setText(numberLocale.toString(fitResult.iterations));
 1229     uiGeneralTab.twLog->item(2, 1)->setText(numberLocale.toString(m_fitData.eps));
 1230     if (fitResult.elapsedTime > 1000)
 1231         uiGeneralTab.twLog->item(3, 1)->setText(numberLocale.toString(fitResult.elapsedTime/1000) + " s");
 1232     else
 1233         uiGeneralTab.twLog->item(3, 1)->setText(numberLocale.toString(fitResult.elapsedTime) + " ms");
 1234 
 1235     uiGeneralTab.twLog->item(4, 1)->setText(numberLocale.toString(fitResult.dof));
 1236     uiGeneralTab.twLog->item(5, 1)->setText(numberLocale.toString(fitResult.paramValues.size()));
 1237     uiGeneralTab.twLog->item(6, 1)->setText(m_fitData.fitRange.toString());
 1238 
 1239     const int np = m_fitData.paramNames.size();
 1240 
 1241     // correlation matrix
 1242     QString sCorr;
 1243     for (const auto &s : m_fitData.paramNamesUtf8)
 1244         sCorr += '\t' + s;
 1245     int index{0};
 1246     DEBUG(Q_FUNC_INFO << ", correlation values size = " << fitResult.correlationMatrix.size())
 1247     for (int i = 0; i < np; i++) {
 1248         sCorr += '\n' + m_fitData.paramNamesUtf8.at(i);
 1249         for (int j = 0; j <= i; j++)
 1250             sCorr += '\t' + numberLocale.toString(fitResult.correlationMatrix.at(index++), 'f');
 1251     }
 1252     uiGeneralTab.twLog->item(7, 1)->setText(sCorr);
 1253 
 1254     // iterations
 1255     QString sIter;
 1256     for (const auto &s : m_fitData.paramNamesUtf8)
 1257         sIter += s + '\t';
 1258     sIter += UTF8_QSTRING("χ²");
 1259 
 1260     const QStringList iterations = fitResult.solverOutput.split(';');
 1261     for (const auto &s : iterations)
 1262         if (!s.isEmpty())
 1263             sIter += '\n' + s;
 1264     uiGeneralTab.twLog->item(8, 1)->setText(sIter);
 1265     uiGeneralTab.twLog->resizeRowsToContents();
 1266 
 1267     // Parameters
 1268     uiGeneralTab.twParameters->setRowCount(np);
 1269 
 1270     for (int i = 0; i < np; i++) {
 1271         const double paramValue = fitResult.paramValues.at(i);
 1272         const double errorValue = fitResult.errorValues.at(i);
 1273 
 1274         auto* item = new QTableWidgetItem(m_fitData.paramNamesUtf8.at(i));
 1275         item->setBackground(QApplication::palette().color(QPalette::Window));
 1276         uiGeneralTab.twParameters->setItem(i, 0, item);
 1277         item = new QTableWidgetItem(numberLocale.toString(paramValue));
 1278         uiGeneralTab.twParameters->setItem(i, 1, item);
 1279 
 1280         if (!m_fitData.paramFixed.at(i)) {
 1281             if (!std::isnan(errorValue)) {
 1282                 item = new QTableWidgetItem(numberLocale.toString(errorValue));
 1283                 uiGeneralTab.twParameters->setItem(i, 2, item);
 1284                 item = new QTableWidgetItem(numberLocale.toString(100.*errorValue/std::abs(paramValue), 'g', 3));
 1285                 uiGeneralTab.twParameters->setItem(i, 3, item);
 1286             } else {
 1287                 item = new QTableWidgetItem(UTF8_QSTRING("∞"));
 1288                 uiGeneralTab.twParameters->setItem(i, 2, item);
 1289                 item = new QTableWidgetItem(UTF8_QSTRING("∞"));
 1290                 uiGeneralTab.twParameters->setItem(i, 3, item);
 1291             }
 1292 
 1293             // t values
 1294             QString tdistValueString;
 1295             if (fitResult.tdist_tValues.at(i) < std::numeric_limits<double>::max())
 1296                 tdistValueString = numberLocale.toString(fitResult.tdist_tValues.at(i), 'g', 3);
 1297             else
 1298                 tdistValueString = UTF8_QSTRING("∞");
 1299             item = new QTableWidgetItem(tdistValueString);
 1300             uiGeneralTab.twParameters->setItem(i, 4, item);
 1301 
 1302             // p values
 1303             const double p = fitResult.tdist_pValues.at(i);
 1304             item = new QTableWidgetItem(numberLocale.toString(p, 'g', 3));
 1305             // color p values depending on value
 1306             if (p > 0.05)
 1307                 item->setForeground(QBrush(QApplication::palette().color(QPalette::LinkVisited)));
 1308             else if (p > 0.01)
 1309                 item->setForeground(QBrush(Qt::darkGreen));
 1310             else if (p > 0.001)
 1311                 item->setForeground(QBrush(Qt::darkCyan));
 1312             else if (p > 0.0001)
 1313                 item->setForeground(QBrush(QApplication::palette().color(QPalette::Link)));
 1314             else
 1315                 item->setForeground(QBrush(QApplication::palette().color(QPalette::Highlight)));
 1316             uiGeneralTab.twParameters->setItem(i, 5, item);
 1317 
 1318             // Conf. interval
 1319             if (!std::isnan(errorValue)) {
 1320                 const double margin = fitResult.tdist_marginValues.at(i);
 1321                 //TODO: if (fitResult.tdist_tValues.at(i) > 1.e6)
 1322                 //  item = new QTableWidgetItem(i18n("too small"));
 1323 
 1324                 item = new QTableWidgetItem(numberLocale.toString(paramValue - margin));
 1325                 uiGeneralTab.twParameters->setItem(i, 6, item);
 1326                 item = new QTableWidgetItem(numberLocale.toString(paramValue + margin));
 1327                 uiGeneralTab.twParameters->setItem(i, 7, item);
 1328             }
 1329         }
 1330     }
 1331 
 1332     // Goodness of fit
 1333     uiGeneralTab.twGoodness->item(0, 1)->setText(numberLocale.toString(fitResult.sse));
 1334 
 1335     if (fitResult.dof != 0) {
 1336         uiGeneralTab.twGoodness->item(1, 1)->setText(numberLocale.toString(fitResult.rms));
 1337         uiGeneralTab.twGoodness->item(2, 1)->setText(numberLocale.toString(fitResult.rsd));
 1338 
 1339         uiGeneralTab.twGoodness->item(3, 1)->setText(numberLocale.toString(fitResult.rsquare));
 1340         uiGeneralTab.twGoodness->item(4, 1)->setText(numberLocale.toString(fitResult.rsquareAdj));
 1341 
 1342         // chi^2 and F test p-values
 1343         uiGeneralTab.twGoodness->item(5, 1)->setText(numberLocale.toString(fitResult.chisq_p, 'g', 3));
 1344         uiGeneralTab.twGoodness->item(6, 1)->setText(numberLocale.toString(fitResult.fdist_F, 'g', 3));
 1345         uiGeneralTab.twGoodness->item(7, 1)->setText(numberLocale.toString(fitResult.fdist_p, 'g', 3));
 1346         uiGeneralTab.twGoodness->item(9, 1)->setText(numberLocale.toString(fitResult.aic, 'g', 3));
 1347         uiGeneralTab.twGoodness->item(10, 1)->setText(numberLocale.toString(fitResult.bic, 'g', 3));
 1348     }
 1349 
 1350     uiGeneralTab.twGoodness->item(8, 1)->setText(numberLocale.toString(fitResult.mae));
 1351 
 1352     //resize the table headers to fit the new content
 1353     uiGeneralTab.twLog->resizeColumnsToContents();
 1354     uiGeneralTab.twParameters->resizeColumnsToContents();
 1355     //twGoodness doesn't have any header -> resize sections
 1356     uiGeneralTab.twGoodness->resizeColumnToContents(0);
 1357     uiGeneralTab.twGoodness->resizeColumnToContents(1);
 1358 
 1359     //enable the "recalculate"-button if the source data was changed since the last fit
 1360     uiGeneralTab.pbRecalculate->setEnabled(m_fitCurve->isSourceDataChangedSinceLastRecalc());
 1361 }
 1362 
 1363 //*************************************************************
 1364 //*********** SLOTs for changes triggered in XYCurve **********
 1365 //*************************************************************
 1366 //General-Tab
 1367 void XYFitCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) {
 1368     if (m_curve != aspect)
 1369         return;
 1370 
 1371     m_initializing = true;
 1372     if (aspect->name() != uiGeneralTab.leName->text())
 1373         uiGeneralTab.leName->setText(aspect->name());
 1374     else if (aspect->comment() != uiGeneralTab.leComment->text())
 1375         uiGeneralTab.leComment->setText(aspect->comment());
 1376     m_initializing = false;
 1377 }
 1378 
 1379 void XYFitCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) {
 1380     m_initializing = true;
 1381     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(type));
 1382     m_initializing = false;
 1383 }
 1384 
 1385 void XYFitCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) {
 1386     m_initializing = true;
 1387     XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve);
 1388     m_initializing = false;
 1389 }
 1390 
 1391 void XYFitCurveDock::curveXDataColumnChanged(const AbstractColumn* column) {
 1392     m_initializing = true;
 1393     XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column);
 1394     m_initializing = false;
 1395 }
 1396 
 1397 void XYFitCurveDock::curveYDataColumnChanged(const AbstractColumn* column) {
 1398     m_initializing = true;
 1399     XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column);
 1400     m_initializing = false;
 1401 }
 1402 
 1403 void XYFitCurveDock::curveXErrorColumnChanged(const AbstractColumn* column) {
 1404     m_initializing = true;
 1405     XYCurveDock::setModelIndexFromAspect(cbXErrorColumn, column);
 1406     m_initializing = false;
 1407 }
 1408 
 1409 void XYFitCurveDock::curveYErrorColumnChanged(const AbstractColumn* column) {
 1410     m_initializing = true;
 1411     XYCurveDock::setModelIndexFromAspect(cbYErrorColumn, column);
 1412     m_initializing = false;
 1413 }
 1414 
 1415 /*!
 1416  * called when fit data of fit curve changes
 1417  */
 1418 void XYFitCurveDock::curveFitDataChanged(const XYFitCurve::FitData& fitData) {
 1419     m_initializing = true;
 1420     m_fitData = fitData;
 1421 
 1422     if (m_fitData.modelCategory != nsl_fit_model_custom)
 1423         uiGeneralTab.cbModel->setCurrentIndex(m_fitData.modelType);
 1424 
 1425     uiGeneralTab.sbDegree->setValue(m_fitData.degree);
 1426     m_initializing = false;
 1427 }
 1428 
 1429 void XYFitCurveDock::dataChanged() {
 1430     this->enableRecalculate();
 1431 }
 1432 
 1433 void XYFitCurveDock::curveVisibilityChanged(bool on) {
 1434     m_initializing = true;
 1435     uiGeneralTab.chkVisible->setChecked(on);
 1436     m_initializing = false;
 1437 }