File indexing completed on 2025-09-14 03:43:12

0001 /*
0002     File                 : XYCurveDock.cpp
0003     Project              : LabPlot
0004     Description          : widget for XYCurve properties
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2010-2024 Alexander Semke <alexander.semke@web.de>
0007     SPDX-FileCopyrightText: 2012-2022 Stefan Gerlach <stefan.gerlach@uni-konstanz.de>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "XYCurveDock.h"
0013 #include "backend/core/Project.h"
0014 #include "backend/core/Settings.h"
0015 #include "backend/core/column/Column.h"
0016 #include "backend/core/datatypes/DateTime2StringFilter.h"
0017 #include "backend/core/datatypes/Double2StringFilter.h"
0018 #include "backend/worksheet/Worksheet.h"
0019 #include "backend/worksheet/plots/cartesian/ErrorBarStyle.h"
0020 #include "backend/worksheet/plots/cartesian/XYCurve.h"
0021 #include "commonfrontend/widgets/TreeViewComboBox.h"
0022 #include "kdefrontend/GuiTools.h"
0023 #include "kdefrontend/TemplateHandler.h"
0024 #include "kdefrontend/widgets/BackgroundWidget.h"
0025 #include "kdefrontend/widgets/ErrorBarStyleWidget.h"
0026 #include "kdefrontend/widgets/LineWidget.h"
0027 #include "kdefrontend/widgets/SymbolWidget.h"
0028 
0029 #include <QPainter>
0030 
0031 #include <KConfig>
0032 #include <KConfigGroup>
0033 #include <KLocalizedString>
0034 
0035 /*!
0036   \class XYCurveDock
0037   \brief  Provides a widget for editing the properties of the XYCurves (2D-curves) currently selected in the project explorer.
0038 
0039   If more than one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves.
0040   The exclusions are the name, the comment and the datasets (columns) of the curves  - these properties can only be changed if there is only one single curve.
0041 
0042   \ingroup kdefrontend
0043 */
0044 
0045 XYCurveDock::XYCurveDock(QWidget* parent)
0046     : BaseDock(parent) {
0047     ui.setupUi(this);
0048 
0049     // Tab "Line"
0050     auto* gridLayout = qobject_cast<QGridLayout*>(ui.tabLine->layout());
0051     lineWidget = new LineWidget(ui.tabLine);
0052     gridLayout->addWidget(lineWidget, 5, 0, 1, 3);
0053 
0054     dropLineWidget = new LineWidget(ui.tabLine);
0055     gridLayout->addWidget(dropLineWidget, 8, 0, 1, 3);
0056 
0057     // Tab "Symbol"
0058     auto* hboxLayout = new QHBoxLayout(ui.tabSymbol);
0059     symbolWidget = new SymbolWidget(ui.tabSymbol);
0060     hboxLayout->addWidget(symbolWidget);
0061     hboxLayout->setContentsMargins(2, 2, 2, 2);
0062     hboxLayout->setSpacing(2);
0063 
0064     // Tab "Values"
0065     gridLayout = qobject_cast<QGridLayout*>(ui.tabValues->layout());
0066     cbValuesColumn = new TreeViewComboBox(ui.tabValues);
0067     gridLayout->addWidget(cbValuesColumn, 2, 2, 1, 1);
0068 
0069     // add formats for numeric values
0070     ui.cbValuesNumericFormat->addItem(i18n("Decimal"), QVariant('f'));
0071     ui.cbValuesNumericFormat->addItem(i18n("Scientific (e)"), QVariant('e'));
0072     ui.cbValuesNumericFormat->addItem(i18n("Scientific (E)"), QVariant('E'));
0073     ui.cbValuesNumericFormat->addItem(i18n("Automatic (e)"), QVariant('g'));
0074     ui.cbValuesNumericFormat->addItem(i18n("Automatic (E)"), QVariant('G'));
0075 
0076     // add format for date, time and datetime values
0077     for (const auto& s : AbstractColumn::dateTimeFormats())
0078         ui.cbValuesDateTimeFormat->addItem(s, QVariant(s));
0079 
0080     ui.cbValuesDateTimeFormat->setEditable(true);
0081 
0082     // Tab "Filling"
0083     auto* layout = static_cast<QHBoxLayout*>(ui.tabAreaFilling->layout());
0084     backgroundWidget = new BackgroundWidget(ui.tabAreaFilling);
0085     layout->insertWidget(0, backgroundWidget);
0086 
0087     // Tab "Error Bars"
0088     const KConfigGroup group = Settings::group(QStringLiteral("Settings_General"));
0089     if (group.readEntry(QStringLiteral("GUMTerms"), false)) {
0090         ui.tabWidget->setTabText(ui.tabWidget->indexOf(ui.tabErrorBars), i18n("Uncertainty Bars"));
0091         ui.lErrorBarX->setText(i18n("X Uncertainty"));
0092         ui.lErrorBarY->setText(i18n("Y Uncertainty"));
0093     }
0094 
0095     gridLayout = qobject_cast<QGridLayout*>(ui.tabErrorBars->layout());
0096 
0097     cbXErrorPlusColumn = new TreeViewComboBox(ui.tabErrorBars);
0098     gridLayout->addWidget(cbXErrorPlusColumn, 2, 2, 1, 1);
0099 
0100     cbXErrorMinusColumn = new TreeViewComboBox(ui.tabErrorBars);
0101     gridLayout->addWidget(cbXErrorMinusColumn, 3, 2, 1, 1);
0102 
0103     cbYErrorPlusColumn = new TreeViewComboBox(ui.tabErrorBars);
0104     gridLayout->addWidget(cbYErrorPlusColumn, 7, 2, 1, 1);
0105 
0106     cbYErrorMinusColumn = new TreeViewComboBox(ui.tabErrorBars);
0107     gridLayout->addWidget(cbYErrorMinusColumn, 8, 2, 1, 1);
0108 
0109     errorBarStyleWidget = new ErrorBarStyleWidget(ui.tabErrorBars);
0110     gridLayout->addWidget(errorBarStyleWidget, 11, 0, 1, 3);
0111 
0112     // Tab "Margin Plots"
0113     ui.cbRugOrientation->addItem(i18n("Vertical"));
0114     ui.cbRugOrientation->addItem(i18n("Horizontal"));
0115     ui.cbRugOrientation->addItem(i18n("Both"));
0116 
0117     // adjust layouts in the tabs
0118     for (int i = 0; i < ui.tabWidget->count(); ++i) {
0119         auto* layout = dynamic_cast<QGridLayout*>(ui.tabWidget->widget(i)->layout());
0120         if (!layout)
0121             continue;
0122 
0123         layout->setContentsMargins(2, 2, 2, 2);
0124         layout->setHorizontalSpacing(2);
0125         layout->setVerticalSpacing(2);
0126     }
0127 
0128     XYCurveDock::updateLocale();
0129 
0130     // Slots
0131 
0132     // Lines
0133     connect(ui.cbLineType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::lineTypeChanged);
0134     connect(ui.sbLineInterpolationPointsCount, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYCurveDock::lineInterpolationPointsCountChanged);
0135     connect(ui.chkLineSkipGaps, &QCheckBox::clicked, this, &XYCurveDock::lineSkipGapsChanged);
0136     connect(ui.chkLineIncreasingXOnly, &QCheckBox::clicked, this, &XYCurveDock::lineIncreasingXOnlyChanged);
0137 
0138     // Values
0139     connect(ui.cbValuesType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::valuesTypeChanged);
0140     connect(cbValuesColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::valuesColumnChanged);
0141     connect(ui.cbValuesPosition, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::valuesPositionChanged);
0142     connect(ui.sbValuesDistance, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &XYCurveDock::valuesDistanceChanged);
0143     connect(ui.sbValuesRotation, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYCurveDock::valuesRotationChanged);
0144     connect(ui.sbValuesOpacity, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYCurveDock::valuesOpacityChanged);
0145     connect(ui.cbValuesNumericFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::valuesNumericFormatChanged);
0146     connect(ui.sbValuesPrecision, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYCurveDock::valuesPrecisionChanged);
0147     connect(ui.cbValuesDateTimeFormat, &QComboBox::currentTextChanged, this, &XYCurveDock::valuesDateTimeFormatChanged);
0148     connect(ui.leValuesPrefix, &QLineEdit::textChanged, this, &XYCurveDock::valuesPrefixChanged);
0149     connect(ui.leValuesSuffix, &QLineEdit::textChanged, this, &XYCurveDock::valuesSuffixChanged);
0150     connect(ui.kfrValuesFont, &KFontRequester::fontSelected, this, &XYCurveDock::valuesFontChanged);
0151     connect(ui.kcbValuesColor, &KColorButton::changed, this, &XYCurveDock::valuesColorChanged);
0152 
0153     // Error bars
0154     connect(ui.cbXErrorType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::xErrorTypeChanged);
0155     connect(cbXErrorPlusColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::xErrorPlusColumnChanged);
0156     connect(cbXErrorMinusColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::xErrorMinusColumnChanged);
0157     connect(ui.cbYErrorType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::yErrorTypeChanged);
0158     connect(cbYErrorPlusColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::yErrorPlusColumnChanged);
0159     connect(cbYErrorMinusColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::yErrorMinusColumnChanged);
0160 
0161     // Margin Plots
0162     connect(ui.chkRugEnabled, &QCheckBox::toggled, this, &XYCurveDock::rugEnabledChanged);
0163     connect(ui.cbRugOrientation, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYCurveDock::rugOrientationChanged);
0164     connect(ui.sbRugLength, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &XYCurveDock::rugLengthChanged);
0165     connect(ui.sbRugWidth, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &XYCurveDock::rugWidthChanged);
0166     connect(ui.sbRugOffset, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &XYCurveDock::rugOffsetChanged);
0167 
0168     // template handler
0169     auto* frame = new QFrame(this);
0170     layout = new QHBoxLayout(frame);
0171     layout->setContentsMargins(0, 11, 0, 11);
0172 
0173     auto* templateHandler = new TemplateHandler(this, QLatin1String("XYCurve"));
0174     layout->addWidget(templateHandler);
0175     connect(templateHandler, &TemplateHandler::loadConfigRequested, this, &XYCurveDock::loadConfigFromTemplate);
0176     connect(templateHandler, &TemplateHandler::saveConfigRequested, this, &XYCurveDock::saveConfigAsTemplate);
0177     connect(templateHandler, &TemplateHandler::info, this, &XYCurveDock::info);
0178 
0179     ui.verticalLayout->addWidget(frame);
0180 
0181     retranslateUi();
0182     init();
0183 }
0184 
0185 XYCurveDock::~XYCurveDock() {
0186     delete m_valuesModel;
0187 }
0188 
0189 void XYCurveDock::setupGeneral() {
0190     auto* generalTab = new QWidget(ui.tabGeneral);
0191     uiGeneralTab.setupUi(generalTab);
0192     setPlotRangeCombobox(uiGeneralTab.cbPlotRanges);
0193     setBaseWidgets(uiGeneralTab.leName, uiGeneralTab.teComment);
0194     setVisibilityWidgets(uiGeneralTab.chkVisible, uiGeneralTab.chkLegendVisible);
0195 
0196     auto* layout = new QHBoxLayout(ui.tabGeneral);
0197     layout->setContentsMargins(0, 0, 0, 0);
0198     layout->addWidget(generalTab);
0199 
0200     // Tab "General" (see xycurvedockgeneraltab.ui)
0201     auto* gridLayout = qobject_cast<QGridLayout*>(generalTab->layout());
0202 
0203     cbXColumn = new TreeViewComboBox(generalTab);
0204     cbXColumn->useCurrentIndexText(false);
0205     gridLayout->addWidget(cbXColumn, 4, 2, 1, 1);
0206 
0207     cbYColumn = new TreeViewComboBox(generalTab);
0208     cbYColumn->useCurrentIndexText(false);
0209     gridLayout->addWidget(cbYColumn, 5, 2, 1, 1);
0210 
0211     // General
0212     connect(cbXColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::xColumnChanged);
0213     connect(cbYColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYCurveDock::yColumnChanged);
0214 }
0215 
0216 void XYCurveDock::init() {
0217     m_initializing = true;
0218 
0219     // Line
0220     ui.cbLineType->addItem(i18n("None"));
0221     ui.cbLineType->addItem(i18n("Line"));
0222     ui.cbLineType->addItem(i18n("Horiz. Start"));
0223     ui.cbLineType->addItem(i18n("Vert. Start"));
0224     ui.cbLineType->addItem(i18n("Horiz. Midpoint"));
0225     ui.cbLineType->addItem(i18n("Vert. Midpoint"));
0226     ui.cbLineType->addItem(i18n("2-segments"));
0227     ui.cbLineType->addItem(i18n("3-segments"));
0228     ui.cbLineType->addItem(i18n("Cubic Spline (Natural)"));
0229     ui.cbLineType->addItem(i18n("Cubic Spline (Periodic)"));
0230     ui.cbLineType->addItem(i18n("Akima-spline (Natural)"));
0231     ui.cbLineType->addItem(i18n("Akima-spline (Periodic)"));
0232 
0233     QPainter pa;
0234     // TODO size of the icon depending on the actual height of the combobox?
0235     int iconSize = 20;
0236     QPixmap pm(iconSize, iconSize);
0237     ui.cbLineType->setIconSize(QSize(iconSize, iconSize));
0238 
0239     QPen pen(Qt::SolidPattern, 0);
0240     const QColor& color = GuiTools::isDarkMode() ? Qt::white : Qt::black;
0241     pen.setColor(color);
0242     pa.setPen(pen);
0243 
0244     // no line
0245     pm.fill(Qt::transparent);
0246     pa.begin(&pm);
0247     pa.setPen(pen);
0248     pa.setRenderHint(QPainter::Antialiasing);
0249     pa.drawEllipse(1, 1, 4, 4);
0250     pa.drawEllipse(15, 15, 4, 4);
0251     pa.end();
0252     ui.cbLineType->setItemIcon(0, pm);
0253 
0254     // line
0255     pm.fill(Qt::transparent);
0256     pa.begin(&pm);
0257     pa.setPen(pen);
0258     pa.setRenderHint(QPainter::Antialiasing);
0259     pa.drawEllipse(1, 1, 4, 4);
0260     pa.drawEllipse(15, 15, 4, 4);
0261     pa.drawLine(3, 3, 17, 17);
0262     pa.end();
0263     ui.cbLineType->setItemIcon(1, pm);
0264 
0265     pm.fill(Qt::transparent);
0266     pa.begin(&pm);
0267     pa.setPen(pen);
0268     pa.setRenderHint(QPainter::Antialiasing);
0269     pa.drawEllipse(1, 1, 4, 4);
0270     pa.drawEllipse(15, 15, 4, 4);
0271     pa.drawLine(3, 3, 17, 3);
0272     pa.drawLine(17, 3, 17, 17);
0273     pa.end();
0274     ui.cbLineType->setItemIcon(2, pm);
0275 
0276     pm.fill(Qt::transparent);
0277     pa.begin(&pm);
0278     pa.setPen(pen);
0279     pa.setRenderHint(QPainter::Antialiasing);
0280     pa.drawEllipse(1, 1, 4, 4);
0281     pa.drawEllipse(15, 15, 4, 4);
0282     pa.drawLine(3, 3, 3, 17);
0283     pa.drawLine(3, 17, 17, 17);
0284     pa.end();
0285     ui.cbLineType->setItemIcon(3, pm);
0286 
0287     // horizontal midpoint
0288     pm.fill(Qt::transparent);
0289     pa.begin(&pm);
0290     pa.setPen(pen);
0291     pa.setRenderHint(QPainter::Antialiasing);
0292     pa.drawEllipse(1, 1, 4, 4);
0293     pa.drawEllipse(15, 15, 4, 4);
0294     pa.drawLine(3, 3, 10, 3);
0295     pa.drawLine(10, 3, 10, 17);
0296     pa.drawLine(10, 17, 17, 17);
0297     pa.end();
0298     ui.cbLineType->setItemIcon(4, pm);
0299 
0300     // vertical midpoint
0301     pm.fill(Qt::transparent);
0302     pa.begin(&pm);
0303     pa.setPen(pen);
0304     pa.setRenderHint(QPainter::Antialiasing);
0305     pa.drawEllipse(1, 1, 4, 4);
0306     pa.drawEllipse(15, 15, 4, 4);
0307     pa.drawLine(3, 3, 3, 10);
0308     pa.drawLine(3, 10, 17, 10);
0309     pa.drawLine(17, 10, 17, 17);
0310     pa.end();
0311     ui.cbLineType->setItemIcon(5, pm);
0312 
0313     // 2-segments
0314     pm.fill(Qt::transparent);
0315     pa.begin(&pm);
0316     pa.setPen(pen);
0317     pa.setRenderHint(QPainter::Antialiasing);
0318     pa.drawEllipse(1, 1, 4, 4);
0319     pa.drawEllipse(8, 8, 4, 4);
0320     pa.drawEllipse(15, 15, 4, 4);
0321     pa.drawLine(3, 3, 10, 10);
0322     pa.end();
0323     ui.cbLineType->setItemIcon(6, pm);
0324 
0325     // 3-segments
0326     pm.fill(Qt::transparent);
0327     pa.begin(&pm);
0328     pa.setPen(pen);
0329     pa.setRenderHint(QPainter::Antialiasing);
0330     pa.drawEllipse(1, 1, 4, 4);
0331     pa.drawEllipse(8, 8, 4, 4);
0332     pa.drawEllipse(15, 15, 4, 4);
0333     pa.drawLine(3, 3, 17, 17);
0334     pa.end();
0335     ui.cbLineType->setItemIcon(7, pm);
0336 
0337     // natural spline
0338     pm.fill(Qt::transparent);
0339     pa.begin(&pm);
0340     pa.setPen(pen);
0341     pa.setRenderHint(QPainter::Antialiasing);
0342     pa.drawEllipse(1, 1, 4, 4);
0343     pa.drawEllipse(15, 15, 4, 4);
0344     pa.rotate(45);
0345     pa.drawArc(2 * sqrt(2), -4, 17 * sqrt(2), 20, 30 * 16, 120 * 16);
0346 
0347     pa.end();
0348     ui.cbLineType->setItemIcon(8, pm);
0349     ui.cbLineType->setItemIcon(9, pm);
0350     ui.cbLineType->setItemIcon(10, pm);
0351     ui.cbLineType->setItemIcon(11, pm);
0352 
0353     m_initializing = false;
0354 
0355     // Values
0356     ui.cbValuesType->addItem(i18n("No Values"));
0357     ui.cbValuesType->addItem(QStringLiteral("x"));
0358     ui.cbValuesType->addItem(QStringLiteral("y"));
0359     ui.cbValuesType->addItem(QStringLiteral("x, y"));
0360     ui.cbValuesType->addItem(QStringLiteral("(x, y)"));
0361     ui.cbValuesType->addItem(i18n("Custom Column"));
0362 
0363     ui.cbValuesPosition->addItem(i18n("Above"));
0364     ui.cbValuesPosition->addItem(i18n("Below"));
0365     ui.cbValuesPosition->addItem(i18n("Left"));
0366     ui.cbValuesPosition->addItem(i18n("Right"));
0367 
0368     // Error Bars
0369     ui.cbXErrorType->addItem(i18n("No"), static_cast<int>(ErrorBar::Type::NoError));
0370     ui.cbXErrorType->addItem(i18n("Symmetric"), static_cast<int>(ErrorBar::Type::Symmetric));
0371     ui.cbXErrorType->addItem(i18n("Asymmetric"), static_cast<int>(ErrorBar::Type::Asymmetric));
0372 
0373     ui.cbYErrorType->addItem(i18n("No"), static_cast<int>(ErrorBar::Type::NoError));
0374     ui.cbYErrorType->addItem(i18n("Symmetric"), static_cast<int>(ErrorBar::Type::Symmetric));
0375     ui.cbYErrorType->addItem(i18n("Asymmetric"), static_cast<int>(ErrorBar::Type::Asymmetric));
0376 }
0377 
0378 QList<AspectType> XYCurveDock::defaultColumnTopLevelClasses() {
0379     return {AspectType::Folder,
0380             AspectType::Workbook,
0381             AspectType::Datapicker,
0382             AspectType::DatapickerCurve,
0383             AspectType::Spreadsheet,
0384             AspectType::LiveDataSource,
0385             AspectType::Column,
0386             AspectType::Worksheet,
0387             AspectType::CartesianPlot,
0388             AspectType::CantorWorksheet};
0389 }
0390 
0391 void XYCurveDock::setModel() {
0392     auto* model = aspectModel();
0393     model->enablePlottableColumnsOnly(true);
0394     model->enableShowPlotDesignation(true);
0395 
0396     QList<AspectType> list = defaultColumnTopLevelClasses();
0397     list.append(AspectType::XYFitCurve);
0398     list.append(AspectType::XYSmoothCurve);
0399 
0400     if (cbXColumn && cbYColumn) {
0401         cbXColumn->setTopLevelClasses(list);
0402         cbYColumn->setTopLevelClasses(list);
0403     }
0404     cbValuesColumn->setTopLevelClasses(list);
0405     cbXErrorMinusColumn->setTopLevelClasses(list);
0406     cbXErrorPlusColumn->setTopLevelClasses(list);
0407     cbYErrorMinusColumn->setTopLevelClasses(list);
0408     cbYErrorPlusColumn->setTopLevelClasses(list);
0409 
0410     if (m_curve->inherits(AspectType::XYAnalysisCurve))
0411         // the model is used in the combobox for curve data sources -> allow to also select analysis curves
0412         list = {AspectType::Column,
0413                 AspectType::XYCurve,
0414                 AspectType::XYFitCurve,
0415                 AspectType::XYIntegrationCurve,
0416                 AspectType::XYInterpolationCurve,
0417                 AspectType::XYSmoothCurve,
0418                 AspectType::XYFourierFilterCurve,
0419                 AspectType::XYFourierTransformCurve,
0420                 AspectType::XYConvolutionCurve,
0421                 AspectType::XYCorrelationCurve,
0422                 AspectType::XYDataReductionCurve};
0423     else
0424         list = {AspectType::Column};
0425 
0426     model->setSelectableAspects(list);
0427 
0428     if (cbXColumn && cbYColumn) {
0429         cbXColumn->setModel(model);
0430         cbYColumn->setModel(model);
0431     }
0432 
0433     cbXErrorMinusColumn->setModel(model);
0434     cbXErrorPlusColumn->setModel(model);
0435     cbYErrorMinusColumn->setModel(model);
0436     cbYErrorPlusColumn->setModel(model);
0437 
0438     // for value labels we need a dedicated model since we also want to allow
0439     // to select text columns and we don't want to call enablePlottableColumnsOnly().
0440     if (!m_valuesModel) {
0441         m_valuesModel = new AspectTreeModel(m_curve->project());
0442         m_valuesModel->setSelectableAspects(list);
0443     }
0444     cbValuesColumn->setModel(m_valuesModel);
0445 
0446     // this function is called after the dock widget is initialized and the curves are set.
0447     // so, we use this function to finalize the initialization even though it's not related
0448     // to the actual set of the model (could also be solved by a new class XYAnalysisiCurveDock).
0449 
0450     // hide property widgets that are not relevant for analysis curves
0451     bool visible = (m_curve->type() == AspectType::XYCurve);
0452     ui.lLineType->setVisible(visible);
0453     ui.cbLineType->setVisible(visible);
0454     ui.lLineSkipGaps->setVisible(visible);
0455     ui.chkLineSkipGaps->setVisible(visible);
0456     ui.lLineIncreasingXOnly->setVisible(visible);
0457     ui.chkLineIncreasingXOnly->setVisible(visible);
0458 
0459     // if it's not a xy-curve, it's an analysis curve and the line widget is always enables since we always draw the line
0460     if (!visible)
0461         lineWidget->setEnabled(true);
0462 
0463     // remove the tab "Error bars" for analysis curves
0464     if (!visible)
0465         ui.tabWidget->removeTab(5);
0466 }
0467 
0468 /*!
0469   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
0470 */
0471 void XYCurveDock::setCurves(QList<XYCurve*> list) {
0472     CONDITIONAL_LOCK_RETURN;
0473     m_curvesList = list;
0474     m_curve = list.first();
0475     setAspects(list);
0476     Q_ASSERT(m_curve);
0477     setModel();
0478     initGeneralTab();
0479     initTabs();
0480     setSymbols(list);
0481 }
0482 
0483 void XYCurveDock::setSymbols(QList<XYCurve*> curves) {
0484     // symbols
0485     QList<Symbol*> symbols;
0486     QList<Background*> backgrounds;
0487     QList<Line*> lines;
0488     QList<Line*> dropLines;
0489     QList<ErrorBarStyle*> errorBarStyles;
0490     for (auto* curve : curves) {
0491         symbols << curve->symbol();
0492         backgrounds << curve->background();
0493         lines << curve->line();
0494         dropLines << curve->dropLine();
0495         errorBarStyles << curve->errorBarStyle();
0496     }
0497 
0498     symbolWidget->setSymbols(symbols);
0499     backgroundWidget->setBackgrounds(backgrounds);
0500     lineWidget->setLines(lines);
0501     dropLineWidget->setLines(dropLines);
0502     errorBarStyleWidget->setErrorBarStyles(errorBarStyles);
0503 }
0504 
0505 void XYCurveDock::initGeneralTab() {
0506     // show the properties of the first curve
0507     cbXColumn->setColumn(m_curve->xColumn(), m_curve->xColumnPath());
0508     cbYColumn->setColumn(m_curve->yColumn(), m_curve->yColumnPath());
0509     uiGeneralTab.chkLegendVisible->setChecked(m_curve->legendVisible());
0510     uiGeneralTab.chkVisible->setChecked(m_curve->isVisible());
0511 
0512     updatePlotRangeList();
0513 
0514     // Slots
0515     connect(m_curve, &XYCurve::aspectDescriptionChanged, this, &XYCurveDock::curveDescriptionChanged);
0516     connect(m_curve, &XYCurve::xColumnChanged, this, &XYCurveDock::curveXColumnChanged);
0517     connect(m_curve, &XYCurve::yColumnChanged, this, &XYCurveDock::curveYColumnChanged);
0518     connect(m_curve, &WorksheetElement::plotRangeListChanged, this, &XYCurveDock::updatePlotRangeList);
0519 }
0520 
0521 void XYCurveDock::initTabs() {
0522     // if there are more than one curve in the list, disable the tab "general"
0523     if (m_curvesList.size() == 1) {
0524         cbValuesColumn->setColumn(m_curve->valuesColumn(), m_curve->valuesColumnPath());
0525         const auto* xErrorBar = m_curve->xErrorBar();
0526         const auto* yErrorBar = m_curve->yErrorBar();
0527         cbXErrorPlusColumn->setColumn(xErrorBar->plusColumn(), xErrorBar->plusColumnPath());
0528         cbXErrorMinusColumn->setColumn(xErrorBar->minusColumn(), xErrorBar->minusColumnPath());
0529         cbYErrorPlusColumn->setColumn(yErrorBar->plusColumn(), yErrorBar->plusColumnPath());
0530         cbYErrorMinusColumn->setColumn(yErrorBar->minusColumn(), yErrorBar->minusColumnPath());
0531     } else {
0532         cbValuesColumn->setCurrentModelIndex(QModelIndex());
0533         cbXErrorPlusColumn->setCurrentModelIndex(QModelIndex());
0534         cbXErrorMinusColumn->setCurrentModelIndex(QModelIndex());
0535         cbYErrorPlusColumn->setCurrentModelIndex(QModelIndex());
0536         cbYErrorMinusColumn->setCurrentModelIndex(QModelIndex());
0537     }
0538 
0539     // show the properties of the first curve
0540     load();
0541 
0542     // Slots
0543 
0544     // Line-Tab
0545     connect(m_curve, &XYCurve::lineTypeChanged, this, &XYCurveDock::curveLineTypeChanged);
0546     connect(m_curve, &XYCurve::lineSkipGapsChanged, this, &XYCurveDock::curveLineSkipGapsChanged);
0547     connect(m_curve, &XYCurve::lineIncreasingXOnlyChanged, this, &XYCurveDock::curveLineIncreasingXOnlyChanged);
0548     connect(m_curve, &XYCurve::lineInterpolationPointsCountChanged, this, &XYCurveDock::curveLineInterpolationPointsCountChanged);
0549 
0550     // Values-Tab
0551     connect(m_curve, &XYCurve::valuesTypeChanged, this, &XYCurveDock::curveValuesTypeChanged);
0552     connect(m_curve, &XYCurve::valuesColumnChanged, this, &XYCurveDock::curveValuesColumnChanged);
0553     connect(m_curve, &XYCurve::valuesPositionChanged, this, &XYCurveDock::curveValuesPositionChanged);
0554     connect(m_curve, &XYCurve::valuesDistanceChanged, this, &XYCurveDock::curveValuesDistanceChanged);
0555     connect(m_curve, &XYCurve::valuesOpacityChanged, this, &XYCurveDock::curveValuesOpacityChanged);
0556     connect(m_curve, &XYCurve::valuesRotationAngleChanged, this, &XYCurveDock::curveValuesRotationAngleChanged);
0557     connect(m_curve, &XYCurve::valuesNumericFormatChanged, this, &XYCurveDock::curveValuesNumericFormatChanged);
0558     connect(m_curve, &XYCurve::valuesPrecisionChanged, this, &XYCurveDock::curveValuesPrecisionChanged);
0559     connect(m_curve, &XYCurve::valuesDateTimeFormatChanged, this, &XYCurveDock::curveValuesDateTimeFormatChanged);
0560     connect(m_curve, &XYCurve::valuesPrefixChanged, this, &XYCurveDock::curveValuesPrefixChanged);
0561     connect(m_curve, &XYCurve::valuesSuffixChanged, this, &XYCurveDock::curveValuesSuffixChanged);
0562     connect(m_curve, &XYCurve::valuesFontChanged, this, &XYCurveDock::curveValuesFontChanged);
0563     connect(m_curve, &XYCurve::valuesColorChanged, this, &XYCurveDock::curveValuesColorChanged);
0564 
0565     //"Error bars"-Tab
0566     connect(m_curve->xErrorBar(), &ErrorBar::typeChanged, this, &XYCurveDock::curveXErrorTypeChanged);
0567     connect(m_curve->xErrorBar(), &ErrorBar::plusColumnChanged, this, &XYCurveDock::curveXErrorPlusColumnChanged);
0568     connect(m_curve->xErrorBar(), &ErrorBar::minusColumnChanged, this, &XYCurveDock::curveXErrorMinusColumnChanged);
0569     connect(m_curve->yErrorBar(), &ErrorBar::typeChanged, this, &XYCurveDock::curveYErrorTypeChanged);
0570     connect(m_curve->yErrorBar(), &ErrorBar::plusColumnChanged, this, &XYCurveDock::curveYErrorPlusColumnChanged);
0571     connect(m_curve->yErrorBar(), &ErrorBar::minusColumnChanged, this, &XYCurveDock::curveYErrorMinusColumnChanged);
0572 
0573     //"Margin Plots"-Tab
0574     connect(m_curve, &XYCurve::rugEnabledChanged, this, &XYCurveDock::curveRugEnabledChanged);
0575     connect(m_curve, &XYCurve::rugOrientationChanged, this, &XYCurveDock::curveRugOrientationChanged);
0576     connect(m_curve, &XYCurve::rugLengthChanged, this, &XYCurveDock::curveRugLengthChanged);
0577     connect(m_curve, &XYCurve::rugWidthChanged, this, &XYCurveDock::curveRugWidthChanged);
0578     connect(m_curve, &XYCurve::rugOffsetChanged, this, &XYCurveDock::curveRugOffsetChanged);
0579 }
0580 
0581 void XYCurveDock::updateLocale() {
0582     ui.sbValuesDistance->setLocale(QLocale());
0583     lineWidget->updateLocale();
0584     dropLineWidget->updateLocale();
0585     symbolWidget->updateLocale();
0586     errorBarStyleWidget->updateLocale();
0587 }
0588 
0589 //*************************************************************
0590 //********** SLOTs for changes triggered in XYCurveDock ********
0591 //*************************************************************
0592 void XYCurveDock::retranslateUi() {
0593     ui.lLineSkipGaps->setToolTip(i18n("If checked, connect neighbour points with lines even if there are gaps (invalid or masked values) between them"));
0594     ui.chkLineSkipGaps->setToolTip(i18n("If checked, connect neighbour points with lines even if there are gaps (invalid or masked values) between them"));
0595     ui.lLineIncreasingXOnly->setToolTip(i18n("If checked, connect data points only for strictly increasing values of X"));
0596     ui.chkLineIncreasingXOnly->setToolTip(i18n("If checked, connect data points only for strictly increasing values of X"));
0597     // TODO:
0598     //  uiGeneralTab.lName->setText(i18n("Name"));
0599     //  uiGeneralTab.lComment->setText(i18n("Comment"));
0600     //  uiGeneralTab.chkVisible->setText(i18n("Visible"));
0601     //  uiGeneralTab.lXColumn->setText(i18n("x-data"));
0602     //  uiGeneralTab.lYColumn->setText(i18n("y-data"));
0603 
0604     // TODO updatePenStyles, updateBrushStyles for all comboboxes
0605 }
0606 
0607 void XYCurveDock::xColumnChanged(const QModelIndex& index) {
0608     updateValuesWidgets();
0609 
0610     CONDITIONAL_LOCK_RETURN;
0611 
0612     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0613     AbstractColumn* column = nullptr;
0614     if (aspect) {
0615         column = dynamic_cast<AbstractColumn*>(aspect);
0616         Q_ASSERT(column);
0617     }
0618 
0619     for (auto* curve : m_curvesList)
0620         curve->setXColumn(column);
0621 }
0622 
0623 void XYCurveDock::yColumnChanged(const QModelIndex& index) {
0624     updateValuesWidgets();
0625 
0626     CONDITIONAL_LOCK_RETURN;
0627 
0628     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0629     AbstractColumn* column = nullptr;
0630     if (aspect) {
0631         column = dynamic_cast<AbstractColumn*>(aspect);
0632         Q_ASSERT(column);
0633     }
0634 
0635     for (auto* curve : m_curvesList)
0636         curve->setYColumn(column);
0637 }
0638 
0639 // "Line"-tab
0640 void XYCurveDock::lineTypeChanged(int index) {
0641     const auto lineType = XYCurve::LineType(index);
0642 
0643     if (lineType == XYCurve::LineType::NoLine) {
0644         ui.chkLineSkipGaps->setEnabled(false);
0645         lineWidget->setEnabled(false);
0646         ui.lLineInterpolationPointsCount->hide();
0647         ui.sbLineInterpolationPointsCount->hide();
0648     } else {
0649         ui.chkLineSkipGaps->setEnabled(true);
0650         lineWidget->setEnabled(true);
0651 
0652         if (lineType == XYCurve::LineType::SplineCubicNatural || lineType == XYCurve::LineType::SplineCubicPeriodic
0653             || lineType == XYCurve::LineType::SplineAkimaNatural || lineType == XYCurve::LineType::SplineAkimaPeriodic) {
0654             ui.lLineInterpolationPointsCount->show();
0655             ui.sbLineInterpolationPointsCount->show();
0656             ui.lLineSkipGaps->hide();
0657             ui.chkLineSkipGaps->hide();
0658         } else {
0659             ui.lLineInterpolationPointsCount->hide();
0660             ui.sbLineInterpolationPointsCount->hide();
0661             ui.lLineSkipGaps->show();
0662             ui.chkLineSkipGaps->show();
0663         }
0664     }
0665 
0666     CONDITIONAL_LOCK_RETURN;
0667 
0668     for (auto* curve : m_curvesList)
0669         curve->setLineType(lineType);
0670 }
0671 
0672 void XYCurveDock::lineSkipGapsChanged(bool skip) {
0673     CONDITIONAL_LOCK_RETURN;
0674 
0675     for (auto* curve : m_curvesList)
0676         curve->setLineSkipGaps(skip);
0677 }
0678 
0679 void XYCurveDock::lineIncreasingXOnlyChanged(bool incr) {
0680     CONDITIONAL_LOCK_RETURN;
0681 
0682     for (auto* curve : m_curvesList)
0683         curve->setLineIncreasingXOnly(incr);
0684 }
0685 
0686 void XYCurveDock::lineInterpolationPointsCountChanged(int count) {
0687     CONDITIONAL_LOCK_RETURN;
0688 
0689     for (auto* curve : m_curvesList)
0690         curve->setLineInterpolationPointsCount(count);
0691 }
0692 
0693 // Values-tab
0694 
0695 /*!
0696   called when the type of the values (none, x, y, (x,y) etc.) was changed.
0697 */
0698 void XYCurveDock::valuesTypeChanged(int index) {
0699     this->updateValuesWidgets();
0700 
0701     CONDITIONAL_LOCK_RETURN;
0702 
0703     const auto type = XYCurve::ValuesType(index);
0704     for (auto* curve : m_curvesList)
0705         curve->setValuesType(type);
0706 }
0707 
0708 /*!
0709   called when the custom column for the values was changed.
0710 */
0711 void XYCurveDock::valuesColumnChanged(const QModelIndex& index) {
0712     this->updateValuesWidgets();
0713 
0714     CONDITIONAL_LOCK_RETURN;
0715 
0716     auto* column = static_cast<Column*>(index.internalPointer());
0717     for (auto* curve : m_curvesList)
0718         curve->setValuesColumn(column);
0719 }
0720 
0721 /*!
0722   shows the formatting properties of the column \c column.
0723   Called, when a new column for the values was selected - either by changing the type of the values (none, x, y, etc.) or
0724   by selecting a new custom column for the values.
0725 */
0726 
0727 /*!
0728   depending on the currently selected values column type (column mode) updates the widgets for the values column format,
0729   shows/hides the allowed widgets, fills the corresponding combobox with the possible entries.
0730   Called when the values column was changed.
0731 */
0732 void XYCurveDock::updateValuesWidgets() {
0733     const auto type{XYCurve::ValuesType(ui.cbValuesType->currentIndex())};
0734     bool showValues{type != XYCurve::ValuesType::NoValues};
0735 
0736     ui.cbValuesPosition->setEnabled(showValues);
0737     ui.sbValuesDistance->setEnabled(showValues);
0738     ui.sbValuesRotation->setEnabled(showValues);
0739     ui.sbValuesOpacity->setEnabled(showValues);
0740     ui.kfrValuesFont->setEnabled(showValues);
0741     ui.kcbValuesColor->setEnabled(showValues);
0742 
0743     bool hasInteger = false;
0744     bool hasNumeric = false;
0745     bool hasDateTime = false;
0746 
0747     if (type == XYCurve::ValuesType::CustomColumn) {
0748         ui.lValuesColumn->show();
0749         cbValuesColumn->show();
0750 
0751         auto* column = static_cast<Column*>(cbValuesColumn->currentModelIndex().internalPointer());
0752         if (column) {
0753             if (column->columnMode() == AbstractColumn::ColumnMode::Double)
0754                 hasNumeric = true;
0755             else if (column->columnMode() == AbstractColumn::ColumnMode::Integer || column->columnMode() == AbstractColumn::ColumnMode::BigInt)
0756                 hasInteger = true;
0757             else if (column->columnMode() == AbstractColumn::ColumnMode::DateTime)
0758                 hasDateTime = true;
0759         }
0760     } else {
0761         ui.lValuesColumn->hide();
0762         cbValuesColumn->hide();
0763 
0764         const AbstractColumn* xColumn = nullptr;
0765         const AbstractColumn* yColumn = nullptr;
0766         switch (type) {
0767         case XYCurve::ValuesType::NoValues:
0768             break;
0769         case XYCurve::ValuesType::X:
0770             xColumn = m_curve->xColumn();
0771             break;
0772         case XYCurve::ValuesType::Y:
0773             yColumn = m_curve->yColumn();
0774             break;
0775         case XYCurve::ValuesType::XY:
0776         case XYCurve::ValuesType::XYBracketed:
0777             xColumn = m_curve->xColumn();
0778             yColumn = m_curve->yColumn();
0779             break;
0780         case XYCurve::ValuesType::CustomColumn:
0781             break;
0782         }
0783 
0784         hasInteger = (xColumn && (xColumn->columnMode() == AbstractColumn::ColumnMode::Integer || xColumn->columnMode() == AbstractColumn::ColumnMode::BigInt))
0785             || (yColumn && (yColumn->columnMode() == AbstractColumn::ColumnMode::Integer || yColumn->columnMode() == AbstractColumn::ColumnMode::BigInt));
0786 
0787         hasNumeric = (xColumn && xColumn->columnMode() == AbstractColumn::ColumnMode::Double)
0788             || (yColumn && yColumn->columnMode() == AbstractColumn::ColumnMode::Double);
0789 
0790         hasDateTime = (xColumn && xColumn->columnMode() == AbstractColumn::ColumnMode::DateTime)
0791             || (yColumn && yColumn->columnMode() == AbstractColumn::ColumnMode::DateTime);
0792     }
0793 
0794     // hide all the format related widgets first and
0795     // then show only what is required depending of the column mode(s)
0796     ui.lValuesFormat->hide();
0797     ui.lValuesNumericFormat->hide();
0798     ui.cbValuesNumericFormat->hide();
0799     ui.lValuesPrecision->hide();
0800     ui.sbValuesPrecision->hide();
0801     ui.lValuesDateTimeFormat->hide();
0802     ui.cbValuesDateTimeFormat->hide();
0803 
0804     if (hasNumeric || hasInteger) {
0805         ui.lValuesFormat->show();
0806         ui.lValuesNumericFormat->show();
0807         ui.cbValuesNumericFormat->show();
0808     }
0809 
0810     // precision is only available for Numeric
0811     if (hasNumeric) {
0812         ui.lValuesPrecision->show();
0813         ui.sbValuesPrecision->show();
0814     }
0815 
0816     if (hasDateTime) {
0817         ui.lValuesFormat->show();
0818         ui.lValuesDateTimeFormat->show();
0819         ui.cbValuesDateTimeFormat->show();
0820     }
0821 }
0822 
0823 void XYCurveDock::valuesPositionChanged(int index) {
0824     CONDITIONAL_LOCK_RETURN;
0825 
0826     for (auto* curve : m_curvesList)
0827         curve->setValuesPosition(XYCurve::ValuesPosition(index));
0828 }
0829 
0830 void XYCurveDock::valuesDistanceChanged(double value) {
0831     CONDITIONAL_RETURN_NO_LOCK;
0832 
0833     for (auto* curve : m_curvesList)
0834         curve->setValuesDistance(Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point));
0835 }
0836 
0837 void XYCurveDock::valuesRotationChanged(int value) {
0838     CONDITIONAL_LOCK_RETURN;
0839 
0840     for (auto* curve : m_curvesList)
0841         curve->setValuesRotationAngle(value);
0842 }
0843 
0844 void XYCurveDock::valuesOpacityChanged(int value) {
0845     CONDITIONAL_LOCK_RETURN;
0846 
0847     qreal opacity = (float)value / 100.;
0848     for (auto* curve : m_curvesList)
0849         curve->setValuesOpacity(opacity);
0850 }
0851 
0852 void XYCurveDock::valuesNumericFormatChanged(int index) {
0853     CONDITIONAL_LOCK_RETURN;
0854 
0855     char format = ui.cbValuesNumericFormat->itemData(index).toChar().toLatin1();
0856     for (auto* curve : m_curvesList)
0857         curve->setValuesNumericFormat(format);
0858 }
0859 
0860 void XYCurveDock::valuesDateTimeFormatChanged(const QString& format) {
0861     CONDITIONAL_LOCK_RETURN;
0862 
0863     for (auto* curve : m_curvesList)
0864         curve->setValuesDateTimeFormat(format);
0865 }
0866 
0867 void XYCurveDock::valuesPrecisionChanged(int precision) {
0868     CONDITIONAL_LOCK_RETURN;
0869 
0870     for (auto* curve : m_curvesList)
0871         curve->setValuesPrecision(precision);
0872 }
0873 
0874 void XYCurveDock::valuesPrefixChanged() {
0875     CONDITIONAL_LOCK_RETURN;
0876 
0877     QString prefix = ui.leValuesPrefix->text();
0878     for (auto* curve : m_curvesList)
0879         curve->setValuesPrefix(prefix);
0880 }
0881 
0882 void XYCurveDock::valuesSuffixChanged() {
0883     CONDITIONAL_LOCK_RETURN;
0884 
0885     QString suffix = ui.leValuesSuffix->text();
0886     for (auto* curve : m_curvesList)
0887         curve->setValuesSuffix(suffix);
0888 }
0889 
0890 void XYCurveDock::valuesFontChanged(const QFont& font) {
0891     CONDITIONAL_LOCK_RETURN;
0892 
0893     QFont valuesFont = font;
0894     valuesFont.setPixelSize(Worksheet::convertToSceneUnits(font.pointSizeF(), Worksheet::Unit::Point));
0895     for (auto* curve : m_curvesList)
0896         curve->setValuesFont(valuesFont);
0897 }
0898 
0899 void XYCurveDock::valuesColorChanged(const QColor& color) {
0900     CONDITIONAL_LOCK_RETURN;
0901 
0902     for (auto* curve : m_curvesList)
0903         curve->setValuesColor(color);
0904 }
0905 
0906 //"Error bars"-Tab
0907 void XYCurveDock::xErrorTypeChanged(int index) {
0908     if (index == 0) {
0909         // no error
0910         ui.lXErrorDataPlus->setVisible(false);
0911         cbXErrorPlusColumn->setVisible(false);
0912         ui.lXErrorDataMinus->setVisible(false);
0913         cbXErrorMinusColumn->setVisible(false);
0914     } else if (index == 1) {
0915         // symmetric error
0916         ui.lXErrorDataPlus->setVisible(true);
0917         cbXErrorPlusColumn->setVisible(true);
0918         ui.lXErrorDataMinus->setVisible(false);
0919         cbXErrorMinusColumn->setVisible(false);
0920         ui.lXErrorDataPlus->setText(i18n("Data, +-:"));
0921     } else if (index == 2) {
0922         // asymmetric error
0923         ui.lXErrorDataPlus->setVisible(true);
0924         cbXErrorPlusColumn->setVisible(true);
0925         ui.lXErrorDataMinus->setVisible(true);
0926         cbXErrorMinusColumn->setVisible(true);
0927         ui.lXErrorDataPlus->setText(i18n("Data, +:"));
0928     }
0929 
0930     bool b = (index != 0 || ui.cbYErrorType->currentIndex() != 0);
0931     ui.lErrorFormat->setVisible(b);
0932     errorBarStyleWidget->setVisible(b);
0933 
0934     CONDITIONAL_LOCK_RETURN;
0935 
0936     for (auto* curve : m_curvesList)
0937         curve->xErrorBar()->setType(ErrorBar::Type(index));
0938 }
0939 
0940 void XYCurveDock::xErrorPlusColumnChanged(const QModelIndex& index) {
0941     CONDITIONAL_LOCK_RETURN;
0942 
0943     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0944     auto* column = dynamic_cast<AbstractColumn*>(aspect);
0945     Q_ASSERT(column);
0946 
0947     for (auto* curve : m_curvesList)
0948         curve->xErrorBar()->setPlusColumn(column);
0949 }
0950 
0951 void XYCurveDock::xErrorMinusColumnChanged(const QModelIndex& index) {
0952     CONDITIONAL_LOCK_RETURN;
0953 
0954     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0955     auto* column = dynamic_cast<AbstractColumn*>(aspect);
0956     Q_ASSERT(column);
0957 
0958     for (auto* curve : m_curvesList)
0959         curve->xErrorBar()->setMinusColumn(column);
0960 }
0961 
0962 void XYCurveDock::yErrorTypeChanged(int index) {
0963     if (index == 0) {
0964         // no error
0965         ui.lYErrorDataPlus->setVisible(false);
0966         cbYErrorPlusColumn->setVisible(false);
0967         ui.lYErrorDataMinus->setVisible(false);
0968         cbYErrorMinusColumn->setVisible(false);
0969     } else if (index == 1) {
0970         // symmetric error
0971         ui.lYErrorDataPlus->setVisible(true);
0972         cbYErrorPlusColumn->setVisible(true);
0973         ui.lYErrorDataMinus->setVisible(false);
0974         cbYErrorMinusColumn->setVisible(false);
0975         ui.lYErrorDataPlus->setText(i18n("Data, +-:"));
0976     } else if (index == 2) {
0977         // asymmetric error
0978         ui.lYErrorDataPlus->setVisible(true);
0979         cbYErrorPlusColumn->setVisible(true);
0980         ui.lYErrorDataMinus->setVisible(true);
0981         cbYErrorMinusColumn->setVisible(true);
0982         ui.lYErrorDataPlus->setText(i18n("Data, +:"));
0983     }
0984 
0985     bool b = (index != 0 || ui.cbXErrorType->currentIndex() != 0);
0986     ui.lErrorFormat->setVisible(b);
0987     errorBarStyleWidget->setVisible(b);
0988 
0989     CONDITIONAL_LOCK_RETURN;
0990 
0991     for (auto* curve : m_curvesList)
0992         curve->yErrorBar()->setType(ErrorBar::Type(index));
0993 }
0994 
0995 void XYCurveDock::yErrorPlusColumnChanged(const QModelIndex& index) {
0996     CONDITIONAL_LOCK_RETURN;
0997 
0998     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0999     auto* column = dynamic_cast<AbstractColumn*>(aspect);
1000     Q_ASSERT(column);
1001 
1002     for (auto* curve : m_curvesList)
1003         curve->yErrorBar()->setPlusColumn(column);
1004 }
1005 
1006 void XYCurveDock::yErrorMinusColumnChanged(const QModelIndex& index) {
1007     CONDITIONAL_LOCK_RETURN;
1008 
1009     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
1010     auto* column = dynamic_cast<AbstractColumn*>(aspect);
1011     Q_ASSERT(column);
1012 
1013     for (auto* curve : m_curvesList)
1014         curve->yErrorBar()->setMinusColumn(column);
1015 }
1016 
1017 //"Margin Plots"-Tab
1018 void XYCurveDock::rugEnabledChanged(bool state) {
1019     CONDITIONAL_LOCK_RETURN;
1020 
1021     for (auto* curve : qAsConst(m_curvesList))
1022         curve->setRugEnabled(state);
1023 }
1024 
1025 void XYCurveDock::rugOrientationChanged(int index) {
1026     CONDITIONAL_LOCK_RETURN;
1027 
1028     auto orientation = static_cast<WorksheetElement::Orientation>(index);
1029     for (auto* curve : qAsConst(m_curvesList))
1030         curve->setRugOrientation(orientation);
1031 }
1032 
1033 void XYCurveDock::rugLengthChanged(double value) {
1034     CONDITIONAL_RETURN_NO_LOCK;
1035 
1036     const double length = Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point);
1037     for (auto* curve : qAsConst(m_curvesList))
1038         curve->setRugLength(length);
1039 }
1040 
1041 void XYCurveDock::rugWidthChanged(double value) {
1042     CONDITIONAL_RETURN_NO_LOCK;
1043 
1044     const double width = Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point);
1045     for (auto* curve : qAsConst(m_curvesList))
1046         curve->setRugWidth(width);
1047 }
1048 
1049 void XYCurveDock::rugOffsetChanged(double value) {
1050     CONDITIONAL_RETURN_NO_LOCK;
1051 
1052     const double offset = Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point);
1053     for (auto* curve : qAsConst(m_curvesList))
1054         curve->setRugOffset(offset);
1055 }
1056 
1057 //*************************************************************
1058 //*********** SLOTs for changes triggered in XYCurve **********
1059 //*************************************************************
1060 // General-Tab
1061 void XYCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) {
1062     if (m_curve != aspect)
1063         return;
1064 
1065     CONDITIONAL_LOCK_RETURN;
1066     if (aspect->name() != uiGeneralTab.leName->text())
1067         uiGeneralTab.leName->setText(aspect->name());
1068     else if (aspect->comment() != uiGeneralTab.teComment->text())
1069         uiGeneralTab.teComment->setText(aspect->comment());
1070 }
1071 
1072 void XYCurveDock::curveXColumnChanged(const AbstractColumn* column) {
1073     updateValuesWidgets();
1074     CONDITIONAL_LOCK_RETURN;
1075     cbXColumn->setColumn(column, m_curve->xColumnPath());
1076 }
1077 
1078 void XYCurveDock::curveYColumnChanged(const AbstractColumn* column) {
1079     updateValuesWidgets();
1080     CONDITIONAL_LOCK_RETURN;
1081     cbYColumn->setColumn(column, m_curve->yColumnPath());
1082 }
1083 
1084 // Line-Tab
1085 void XYCurveDock::curveLineTypeChanged(XYCurve::LineType type) {
1086     CONDITIONAL_LOCK_RETURN;
1087     ui.cbLineType->setCurrentIndex((int)type);
1088 }
1089 void XYCurveDock::curveLineSkipGapsChanged(bool skip) {
1090     CONDITIONAL_LOCK_RETURN;
1091     ui.chkLineSkipGaps->setChecked(skip);
1092 }
1093 void XYCurveDock::curveLineIncreasingXOnlyChanged(bool incr) {
1094     CONDITIONAL_LOCK_RETURN;
1095     ui.chkLineIncreasingXOnly->setChecked(incr);
1096 }
1097 void XYCurveDock::curveLineInterpolationPointsCountChanged(int count) {
1098     CONDITIONAL_LOCK_RETURN;
1099     ui.sbLineInterpolationPointsCount->setValue(count);
1100 }
1101 
1102 // Values-Tab
1103 void XYCurveDock::curveValuesTypeChanged(XYCurve::ValuesType type) {
1104     CONDITIONAL_LOCK_RETURN;
1105     ui.cbValuesType->setCurrentIndex((int)type);
1106 }
1107 void XYCurveDock::curveValuesColumnChanged(const AbstractColumn* column) {
1108     CONDITIONAL_LOCK_RETURN;
1109     cbValuesColumn->setColumn(column, m_curve->valuesColumnPath());
1110 }
1111 void XYCurveDock::curveValuesPositionChanged(XYCurve::ValuesPosition position) {
1112     CONDITIONAL_LOCK_RETURN;
1113     ui.cbValuesPosition->setCurrentIndex((int)position);
1114 }
1115 void XYCurveDock::curveValuesDistanceChanged(qreal distance) {
1116     CONDITIONAL_LOCK_RETURN;
1117     ui.sbValuesDistance->setValue(Worksheet::convertFromSceneUnits(distance, Worksheet::Unit::Point));
1118 }
1119 void XYCurveDock::curveValuesRotationAngleChanged(qreal angle) {
1120     CONDITIONAL_LOCK_RETURN;
1121     ui.sbValuesRotation->setValue(angle);
1122 }
1123 void XYCurveDock::curveValuesNumericFormatChanged(char format) {
1124     CONDITIONAL_LOCK_RETURN;
1125     ui.cbValuesNumericFormat->setCurrentIndex(ui.cbValuesNumericFormat->findData(format));
1126 }
1127 void XYCurveDock::curveValuesPrecisionChanged(int precision) {
1128     CONDITIONAL_LOCK_RETURN;
1129     ui.sbValuesPrecision->setValue(precision);
1130 }
1131 void XYCurveDock::curveValuesDateTimeFormatChanged(const QString& format) {
1132     CONDITIONAL_LOCK_RETURN;
1133     ui.cbValuesDateTimeFormat->setCurrentText(format);
1134 }
1135 void XYCurveDock::curveValuesOpacityChanged(qreal opacity) {
1136     CONDITIONAL_LOCK_RETURN;
1137     ui.sbValuesOpacity->setValue(round(opacity * 100.0));
1138 }
1139 void XYCurveDock::curveValuesPrefixChanged(const QString& prefix) {
1140     CONDITIONAL_LOCK_RETURN;
1141     ui.leValuesPrefix->setText(prefix);
1142 }
1143 void XYCurveDock::curveValuesSuffixChanged(const QString& suffix) {
1144     CONDITIONAL_LOCK_RETURN;
1145     ui.leValuesSuffix->setText(suffix);
1146 }
1147 void XYCurveDock::curveValuesFontChanged(QFont font) {
1148     CONDITIONAL_LOCK_RETURN;
1149     font.setPointSizeF(round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Unit::Point)));
1150     ui.kfrValuesFont->setFont(font);
1151 }
1152 void XYCurveDock::curveValuesColorChanged(QColor color) {
1153     CONDITIONAL_LOCK_RETURN;
1154     ui.kcbValuesColor->setColor(color);
1155 }
1156 
1157 //"Error bars"-Tab
1158 void XYCurveDock::curveXErrorTypeChanged(ErrorBar::Type type) {
1159     CONDITIONAL_LOCK_RETURN;
1160     ui.cbXErrorType->setCurrentIndex((int)type);
1161 }
1162 void XYCurveDock::curveXErrorPlusColumnChanged(const AbstractColumn* column) {
1163     CONDITIONAL_LOCK_RETURN;
1164     cbXErrorPlusColumn->setColumn(column, m_curve->xErrorBar()->plusColumnPath());
1165 }
1166 void XYCurveDock::curveXErrorMinusColumnChanged(const AbstractColumn* column) {
1167     CONDITIONAL_LOCK_RETURN;
1168     cbXErrorMinusColumn->setColumn(column, m_curve->xErrorBar()->minusColumnPath());
1169 }
1170 void XYCurveDock::curveYErrorTypeChanged(ErrorBar::Type type) {
1171     CONDITIONAL_LOCK_RETURN;
1172     ui.cbYErrorType->setCurrentIndex((int)type);
1173 }
1174 void XYCurveDock::curveYErrorPlusColumnChanged(const AbstractColumn* column) {
1175     CONDITIONAL_LOCK_RETURN;
1176     cbYErrorPlusColumn->setColumn(column, m_curve->yErrorBar()->plusColumnPath());
1177 }
1178 void XYCurveDock::curveYErrorMinusColumnChanged(const AbstractColumn* column) {
1179     CONDITIONAL_LOCK_RETURN;
1180     cbYErrorMinusColumn->setColumn(column, m_curve->yErrorBar()->minusColumnPath());
1181 }
1182 
1183 //"Margin Plot"-Tab
1184 void XYCurveDock::curveRugEnabledChanged(bool status) {
1185     CONDITIONAL_LOCK_RETURN;
1186     ui.chkRugEnabled->setChecked(status);
1187 }
1188 void XYCurveDock::curveRugOrientationChanged(WorksheetElement::Orientation orientation) {
1189     CONDITIONAL_LOCK_RETURN;
1190     ui.cbRugOrientation->setCurrentIndex(static_cast<int>(orientation));
1191 }
1192 void XYCurveDock::curveRugLengthChanged(double value) {
1193     CONDITIONAL_LOCK_RETURN;
1194     ui.sbRugLength->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Unit::Point));
1195 }
1196 void XYCurveDock::curveRugWidthChanged(double value) {
1197     CONDITIONAL_LOCK_RETURN;
1198     ui.sbRugWidth->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Unit::Point));
1199 }
1200 void XYCurveDock::curveRugOffsetChanged(double value) {
1201     CONDITIONAL_LOCK_RETURN;
1202     ui.sbRugOffset->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Unit::Point));
1203 }
1204 
1205 //*************************************************************
1206 //************************* Settings **************************
1207 //*************************************************************
1208 void XYCurveDock::load() {
1209     // General
1210     // This data is read in XYCurveDock::setCurves().
1211 
1212     // Line
1213     bool xyCurve = (m_curve->type() == AspectType::XYCurve);
1214     if (xyCurve) { // options available for XYCurve only and not for analysis curves
1215         ui.cbLineType->setCurrentIndex((int)m_curve->lineType());
1216         ui.chkLineSkipGaps->setChecked(m_curve->lineSkipGaps());
1217         ui.sbLineInterpolationPointsCount->setValue(m_curve->lineInterpolationPointsCount());
1218     }
1219 
1220     // Values
1221     ui.cbValuesType->setCurrentIndex((int)m_curve->valuesType());
1222     ui.cbValuesPosition->setCurrentIndex((int)m_curve->valuesPosition());
1223     ui.sbValuesDistance->setValue(Worksheet::convertFromSceneUnits(m_curve->valuesDistance(), Worksheet::Unit::Point));
1224     ui.sbValuesRotation->setValue(m_curve->valuesRotationAngle());
1225     ui.sbValuesOpacity->setValue(round(m_curve->valuesOpacity() * 100.0));
1226     ui.sbValuesPrecision->setValue(m_curve->valuesPrecision());
1227     ui.cbValuesNumericFormat->setCurrentIndex(ui.cbValuesNumericFormat->findData(m_curve->valuesNumericFormat()));
1228     ui.cbValuesDateTimeFormat->setCurrentText(m_curve->valuesDateTimeFormat());
1229     ui.leValuesPrefix->setText(m_curve->valuesPrefix());
1230     ui.leValuesSuffix->setText(m_curve->valuesSuffix());
1231     QFont valuesFont = m_curve->valuesFont();
1232     valuesFont.setPointSizeF(round(Worksheet::convertFromSceneUnits(valuesFont.pixelSize(), Worksheet::Unit::Point)));
1233     ui.kfrValuesFont->setFont(valuesFont);
1234     ui.kcbValuesColor->setColor(m_curve->valuesColor());
1235     this->updateValuesWidgets();
1236 
1237     // Error bars
1238     if (xyCurve) {
1239         int index = ui.cbXErrorType->findData(static_cast<int>(m_curve->xErrorBar()->type()));
1240         ui.cbXErrorType->setCurrentIndex(index);
1241         index = ui.cbYErrorType->findData(static_cast<int>(m_curve->yErrorBar()->type()));
1242         ui.cbYErrorType->setCurrentIndex((int)m_curve->yErrorBar()->type());
1243     }
1244 
1245     // Margin plots
1246     ui.chkRugEnabled->setChecked(m_curve->rugEnabled());
1247     ui.cbRugOrientation->setCurrentIndex(static_cast<int>(m_curve->rugOrientation()));
1248     ui.sbRugWidth->setValue(Worksheet::convertFromSceneUnits(m_curve->rugWidth(), Worksheet::Unit::Point));
1249     ui.sbRugLength->setValue(Worksheet::convertFromSceneUnits(m_curve->rugLength(), Worksheet::Unit::Point));
1250     ui.sbRugOffset->setValue(Worksheet::convertFromSceneUnits(m_curve->rugOffset(), Worksheet::Unit::Point));
1251 }
1252 
1253 void XYCurveDock::loadConfigFromTemplate(KConfig& config) {
1254     auto name = TemplateHandler::templateName(config);
1255     int size = m_curvesList.size();
1256     if (size > 1)
1257         m_curve->beginMacro(i18n("%1 xy-curves: template \"%2\" loaded", size, name));
1258     else
1259         m_curve->beginMacro(i18n("%1: template \"%2\" loaded", m_curve->name(), name));
1260 
1261     this->loadConfig(config);
1262 
1263     m_curve->endMacro();
1264 }
1265 
1266 void XYCurveDock::loadConfig(KConfig& config) {
1267     KConfigGroup group = config.group(QStringLiteral("XYCurve"));
1268 
1269     // General
1270     // we don't load/save the settings in the general-tab, since they are not style related.
1271     // It doesn't make sense to load/save them in the template.
1272     // This data is read in XYCurveDock::setCurves().
1273 
1274     // Line
1275     bool xyCurve = (m_curve->type() == AspectType::XYCurve);
1276     if (xyCurve) {
1277         ui.cbLineType->setCurrentIndex(group.readEntry(QStringLiteral("LineType"), (int)m_curve->lineType()));
1278         ui.chkLineSkipGaps->setChecked(group.readEntry(QStringLiteral("LineSkipGaps"), m_curve->lineSkipGaps()));
1279         ui.sbLineInterpolationPointsCount->setValue(group.readEntry(QStringLiteral("LineInterpolationPointsCount"), m_curve->lineInterpolationPointsCount()));
1280     }
1281     lineWidget->loadConfig(group);
1282     dropLineWidget->loadConfig(group);
1283 
1284     // Symbols
1285     symbolWidget->loadConfig(group);
1286 
1287     // Values
1288     ui.cbValuesType->setCurrentIndex(group.readEntry(QStringLiteral("ValuesType"), (int)m_curve->valuesType()));
1289     ui.cbValuesPosition->setCurrentIndex(group.readEntry(QStringLiteral("ValuesPosition"), (int)m_curve->valuesPosition()));
1290     ui.sbValuesDistance->setValue(
1291         Worksheet::convertFromSceneUnits(group.readEntry(QStringLiteral("ValuesDistance"), m_curve->valuesDistance()), Worksheet::Unit::Point));
1292     ui.sbValuesRotation->setValue(group.readEntry(QStringLiteral("ValuesRotation"), m_curve->valuesRotationAngle()));
1293     ui.sbValuesOpacity->setValue(round(group.readEntry(QStringLiteral("ValuesOpacity"), m_curve->valuesOpacity()) * 100.0));
1294     ui.leValuesPrefix->setText(group.readEntry(QStringLiteral("ValuesPrefix"), m_curve->valuesPrefix()));
1295     ui.leValuesSuffix->setText(group.readEntry(QStringLiteral("ValuesSuffix"), m_curve->valuesSuffix()));
1296     QFont valuesFont = m_curve->valuesFont();
1297     valuesFont.setPointSizeF(round(Worksheet::convertFromSceneUnits(valuesFont.pixelSize(), Worksheet::Unit::Point)));
1298     ui.kfrValuesFont->setFont(group.readEntry(QStringLiteral("ValuesFont"), valuesFont));
1299     ui.kcbValuesColor->setColor(group.readEntry(QStringLiteral("ValuesColor"), m_curve->valuesColor()));
1300 
1301     // Filling
1302     backgroundWidget->loadConfig(group);
1303 
1304     // Error bars
1305     if (xyCurve) {
1306         ui.cbXErrorType->setCurrentIndex(group.readEntry(QStringLiteral("XErrorType"), (int)m_curve->xErrorBar()->type()));
1307         ui.cbYErrorType->setCurrentIndex(group.readEntry(QStringLiteral("YErrorType"), (int)m_curve->yErrorBar()->type()));
1308         errorBarStyleWidget->loadConfig(group);
1309     }
1310 }
1311 
1312 void XYCurveDock::saveConfigAsTemplate(KConfig& config) {
1313     KConfigGroup group = config.group(QStringLiteral("XYCurve"));
1314 
1315     // General
1316     // we don't load/save the settings in the general-tab, since they are not style related.
1317     // It doesn't make sense to load/save them in the template.
1318 
1319     bool xyCurve = (m_curve->type() == AspectType::XYCurve);
1320     if (xyCurve) {
1321         group.writeEntry(QStringLiteral("LineType"), ui.cbLineType->currentIndex());
1322         group.writeEntry(QStringLiteral("LineSkipGaps"), ui.chkLineSkipGaps->isChecked());
1323         group.writeEntry(QStringLiteral("LineInterpolationPointsCount"), ui.sbLineInterpolationPointsCount->value());
1324     }
1325 
1326     lineWidget->saveConfig(group);
1327     dropLineWidget->saveConfig(group);
1328 
1329     // Symbols
1330     symbolWidget->saveConfig(group);
1331 
1332     // Values
1333     group.writeEntry(QStringLiteral("ValuesType"), ui.cbValuesType->currentIndex());
1334     group.writeEntry(QStringLiteral("ValuesPosition"), ui.cbValuesPosition->currentIndex());
1335     group.writeEntry(QStringLiteral("ValuesDistance"), Worksheet::convertToSceneUnits(ui.sbValuesDistance->value(), Worksheet::Unit::Point));
1336     group.writeEntry(QStringLiteral("ValuesRotation"), ui.sbValuesRotation->value());
1337     group.writeEntry(QStringLiteral("ValuesOpacity"), ui.sbValuesOpacity->value() / 100.0);
1338     group.writeEntry(QStringLiteral("valuesNumericFormat"), ui.cbValuesNumericFormat->currentText());
1339     group.writeEntry(QStringLiteral("valuesPrecision"), ui.sbValuesPrecision->value());
1340     group.writeEntry(QStringLiteral("valuesDateTimeFormat"), ui.cbValuesDateTimeFormat->currentText());
1341     group.writeEntry(QStringLiteral("ValuesPrefix"), ui.leValuesPrefix->text());
1342     group.writeEntry(QStringLiteral("ValuesSuffix"), ui.leValuesSuffix->text());
1343     group.writeEntry(QStringLiteral("ValuesFont"), ui.kfrValuesFont->font());
1344     group.writeEntry(QStringLiteral("ValuesColor"), ui.kcbValuesColor->color());
1345 
1346     // Filling
1347     backgroundWidget->saveConfig(group);
1348 
1349     // Error bars
1350     if (xyCurve) {
1351         group.writeEntry(QStringLiteral("XErrorType"), ui.cbXErrorType->currentIndex());
1352         group.writeEntry(QStringLiteral("YErrorType"), ui.cbYErrorType->currentIndex());
1353         errorBarStyleWidget->saveConfig(group);
1354     }
1355 
1356     config.sync();
1357 }