diff --git a/src/backend/core/Project.h b/src/backend/core/Project.h --- a/src/backend/core/Project.h +++ b/src/backend/core/Project.h @@ -34,6 +34,7 @@ #include "backend/lib/macros.h" class QString; +class ColumnObserver; class Project : public Folder { Q_OBJECT @@ -82,6 +83,7 @@ public slots: void descriptionChanged(const AbstractAspect*); + void aspectAddedSlot(const AbstractAspect*); signals: void requestSaveState(QXmlStreamWriter*) const; diff --git a/src/backend/core/Project.cpp b/src/backend/core/Project.cpp --- a/src/backend/core/Project.cpp +++ b/src/backend/core/Project.cpp @@ -121,6 +121,7 @@ d->changed = false; connect(this, &Project::aspectDescriptionChanged,this, &Project::descriptionChanged); + connect(this, &Project::aspectAdded,this, &Project::aspectAddedSlot); } Project::~Project() { @@ -192,21 +193,123 @@ d->changed = value; } -bool Project ::hasChanged() const { +bool Project::hasChanged() const { return d->changed ; } +/*! + * \brief Project::descriptionChanged + * When the column is created, it gets a random name and is eventually not connected to any curve. + * When changing the name it can match a curve and should than be connected to the curve. + * \param column + */ void Project::descriptionChanged(const AbstractAspect* aspect) { if (isLoading()) return; - if (this != aspect) + if (this != aspect) { + const AbstractColumn* column = dynamic_cast(aspect); + if (!column) + return; + + QVector curves = children(AbstractAspect::ChildIndexFlag::Recursive); + QString columnPath = column->path(); + + // setXColumnPath must not be set, because if curve->column matches column, there already exist a + // signal/slot connection between the curve and the column to update this. If they are not same, + // xColumnPath is set in setXColumn. Same for the yColumn. + for (auto* curve : curves) { + auto* analysisCurve = dynamic_cast(curve); + if (analysisCurve) { + if (analysisCurve->xDataColumnPath() == columnPath) + analysisCurve->setXDataColumn(column); + if (analysisCurve->yDataColumnPath() == columnPath) + analysisCurve->setYDataColumn(column); + if (analysisCurve->y2DataColumnPath() == columnPath) + analysisCurve->setY2DataColumn(column); + + auto* fitCurve = dynamic_cast(curve); + if (fitCurve) { + if (fitCurve->xErrorColumnPath() == columnPath) + fitCurve->setXErrorColumn(column); + if (fitCurve->yErrorColumnPath() == columnPath) + fitCurve->setYErrorColumn(column); + } + } else { + if (curve->xColumnPath() == columnPath) + curve->setXColumn(column); + if (curve->yColumnPath() == columnPath) + curve->setYColumn(column); + if (curve->valuesColumnPath() == columnPath) + curve->setValuesColumn(column); + if (curve->xErrorPlusColumnPath() == columnPath) + curve->setXErrorPlusColumn(column); + if (curve->xErrorMinusColumnPath() == columnPath) + curve->setXErrorMinusColumn(column); + if (curve->yErrorPlusColumnPath() == columnPath) + curve->setYErrorPlusColumn(column); + if (curve->yErrorMinusColumnPath() == columnPath) + curve->setYErrorMinusColumn(column); + } + + } return; + } d->changed = true; emit changed(); } +/*! + * \brief Project::aspectAddedSlot + * When adding new columns, these should be connected to the corresponding curves + * \param aspect + */ +void Project::aspectAddedSlot(const AbstractAspect* aspect) { + const AbstractColumn* column = dynamic_cast(aspect); + if (!column) + return; + + QVector curves = children(AbstractAspect::ChildIndexFlag::Recursive); + QString columnPath = column->path(); + + for (auto* curve : curves) { + auto* analysisCurve = dynamic_cast(curve); + if (analysisCurve) { + if (analysisCurve->xDataColumnPath() == columnPath) + analysisCurve->setXDataColumn(column); + if (analysisCurve->yDataColumnPath() == columnPath) + analysisCurve->setYDataColumn(column); + if (analysisCurve->y2DataColumnPath() == columnPath) + analysisCurve->setY2DataColumn(column); + + auto* fitCurve = dynamic_cast(curve); + if (fitCurve) { + if (fitCurve->xErrorColumnPath() == columnPath) + fitCurve->setXErrorColumn(column); + if (fitCurve->yErrorColumnPath() == columnPath) + fitCurve->setYErrorColumn(column); + } + } else { + if (curve->xColumnPath() == columnPath) + curve->setXColumn(column); + if (curve->yColumnPath() == columnPath) + curve->setYColumn(column); + if (curve->valuesColumnPath() == columnPath) + curve->setValuesColumn(column); + if (curve->xErrorPlusColumnPath() == columnPath) + curve->setXErrorPlusColumn(column); + if (curve->xErrorMinusColumnPath() == columnPath) + curve->setXErrorMinusColumn(column); + if (curve->yErrorPlusColumnPath() == columnPath) + curve->setYErrorPlusColumn(column); + if (curve->yErrorMinusColumnPath() == columnPath) + curve->setYErrorMinusColumn(column); + } + } + +} + void Project::navigateTo(const QString& path) { emit requestNavigateTo(path); } @@ -382,6 +485,8 @@ QVector columns = children(AbstractAspect::Recursive); //xy-curves + // cannot be removed by the column observer, because it does not react + // on curve changes QVector curves = children(AbstractAspect::Recursive); for (auto* curve : curves) { if (!curve) continue; diff --git a/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.h b/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.h --- a/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.h +++ b/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.h @@ -54,9 +54,9 @@ POINTER_D_ACCESSOR_DECL(const AbstractColumn, xDataColumn, XDataColumn) POINTER_D_ACCESSOR_DECL(const AbstractColumn, yDataColumn, YDataColumn) POINTER_D_ACCESSOR_DECL(const AbstractColumn, y2DataColumn, Y2DataColumn) // optional - const QString& xDataColumnPath() const; - const QString& yDataColumnPath() const; - const QString& y2DataColumnPath() const; + CLASS_D_ACCESSOR_DECL(QString, xDataColumnPath, XDataColumnPath) + CLASS_D_ACCESSOR_DECL(QString, yDataColumnPath, YDataColumnPath) + CLASS_D_ACCESSOR_DECL(QString, y2DataColumnPath, Y2DataColumnPath) typedef XYAnalysisCurvePrivate Private; @@ -69,6 +69,13 @@ public slots: void handleSourceDataChanged(); +private slots: + void xDataColumnAboutToBeRemoved(const AbstractAspect*); + void yDataColumnAboutToBeRemoved(const AbstractAspect*); + void y2DataColumnAboutToBeRemoved(const AbstractAspect*); + void xDataColumnNameChanged(); + void yDataColumnNameChanged(); + void y2DataColumnNameChanged(); signals: void sourceDataChanged(); //emitted when the source data used in the analysis curves was changed to enable the recalculation in the dock widgets diff --git a/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.cpp b/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.cpp --- a/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYAnalysisCurve.cpp @@ -76,9 +76,9 @@ BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, const AbstractColumn*, xDataColumn, xDataColumn) BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, const AbstractColumn*, yDataColumn, yDataColumn) BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, const AbstractColumn*, y2DataColumn, y2DataColumn) -const QString& XYAnalysisCurve::xDataColumnPath() const { Q_D(const XYAnalysisCurve); return d->xDataColumnPath; } -const QString& XYAnalysisCurve::yDataColumnPath() const { Q_D(const XYAnalysisCurve); return d->yDataColumnPath; } -const QString& XYAnalysisCurve::y2DataColumnPath() const { Q_D(const XYAnalysisCurve); return d->y2DataColumnPath; } +CLASS_SHARED_D_READER_IMPL(XYAnalysisCurve, QString, xDataColumnPath, xDataColumnPath) +CLASS_SHARED_D_READER_IMPL(XYAnalysisCurve, QString, yDataColumnPath, yDataColumnPath) +CLASS_SHARED_D_READER_IMPL(XYAnalysisCurve, QString, y2DataColumnPath, y2DataColumnPath) //############################################################################## //################# setter methods and undo commands ########################## @@ -115,10 +115,14 @@ DEBUG("XYAnalysisCurve::setXDataColumn()"); Q_D(XYAnalysisCurve); if (column != d->xDataColumn) { + setXDataColumnPath(column->path()); exec(new XYAnalysisCurveSetXDataColumnCmd(d, column, ki18n("%1: assign x-data"))); handleSourceDataChanged(); if (column) { + connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, + this, &XYAnalysisCurve::xDataColumnAboutToBeRemoved); connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); + connect(column, &AbstractAspect::aspectDescriptionChanged, this, &XYAnalysisCurve::xDataColumnNameChanged); //TODO disconnect on undo } } @@ -129,10 +133,14 @@ DEBUG("XYAnalysisCurve::setYDataColumn()"); Q_D(XYAnalysisCurve); if (column != d->yDataColumn) { + setYDataColumnPath(column->path()); exec(new XYAnalysisCurveSetYDataColumnCmd(d, column, ki18n("%1: assign y-data"))); handleSourceDataChanged(); if (column) { + connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, + this, &XYAnalysisCurve::yDataColumnAboutToBeRemoved); connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); + connect(column, &AbstractAspect::aspectDescriptionChanged, this, &XYAnalysisCurve::yDataColumnNameChanged); //TODO disconnect on undo } } @@ -143,15 +151,34 @@ DEBUG("XYAnalysisCurve::setY2DataColumn()"); Q_D(XYAnalysisCurve); if (column != d->y2DataColumn) { + setY2DataColumnPath(column->path()); exec(new XYAnalysisCurveSetY2DataColumnCmd(d, column, ki18n("%1: assign second y-data"))); handleSourceDataChanged(); if (column) { + connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, + this, &XYAnalysisCurve::y2DataColumnAboutToBeRemoved); connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); + connect(column, &AbstractAspect::aspectDescriptionChanged, this, &XYAnalysisCurve::y2DataColumnNameChanged); //TODO disconnect on undo } } } +void XYAnalysisCurve::setXDataColumnPath(const QString& path) { + Q_D(XYAnalysisCurve); + d->xDataColumnPath = path; +} + +void XYAnalysisCurve::setYDataColumnPath(const QString& path) { + Q_D(XYAnalysisCurve); + d->yDataColumnPath = path; +} + +void XYAnalysisCurve::setY2DataColumnPath(const QString& path) { + Q_D(XYAnalysisCurve); + d->y2DataColumnPath = path; +} + //############################################################################## //################################# SLOTS #################################### //############################################################################## @@ -161,6 +188,45 @@ emit sourceDataChanged(); } +void XYAnalysisCurve::xDataColumnAboutToBeRemoved(const AbstractAspect* aspect) { + Q_D(XYAnalysisCurve); + if (aspect == d->xDataColumn) { + d->xDataColumn = nullptr; + d->retransform(); + } +} + +void XYAnalysisCurve::yDataColumnAboutToBeRemoved(const AbstractAspect* aspect) { + Q_D(XYAnalysisCurve); + if (aspect == d->yDataColumn) { + d->yDataColumn = nullptr; + d->retransform(); + } +} + +void XYAnalysisCurve::y2DataColumnAboutToBeRemoved(const AbstractAspect* aspect) { + Q_D(XYAnalysisCurve); + if (aspect == d->y2DataColumn) { + d->y2DataColumn = nullptr; + d->retransform(); + } +} + +void XYAnalysisCurve::xDataColumnNameChanged() { + Q_D(XYAnalysisCurve); + setXDataColumnPath(d->xDataColumn->path()); +} + +void XYAnalysisCurve::yDataColumnNameChanged() { + Q_D(XYAnalysisCurve); + setYDataColumnPath(d->yDataColumn->path()); +} + +void XYAnalysisCurve::y2DataColumnNameChanged() { + Q_D(XYAnalysisCurve); + setYDataColumnPath(d->y2DataColumn->path()); +} + //############################################################################## //######################### Private implementation ############################# //############################################################################## diff --git a/src/backend/worksheet/plots/cartesian/XYAnalysisCurvePrivate.h b/src/backend/worksheet/plots/cartesian/XYAnalysisCurvePrivate.h --- a/src/backend/worksheet/plots/cartesian/XYAnalysisCurvePrivate.h +++ b/src/backend/worksheet/plots/cartesian/XYAnalysisCurvePrivate.h @@ -33,6 +33,7 @@ class XYAnalysisCurve; class Column; +class AbstractColumn; class XYAnalysisCurvePrivate : public XYCurvePrivate { public: diff --git a/src/backend/worksheet/plots/cartesian/XYCurve.h b/src/backend/worksheet/plots/cartesian/XYCurve.h --- a/src/backend/worksheet/plots/cartesian/XYCurve.h +++ b/src/backend/worksheet/plots/cartesian/XYCurve.h @@ -159,7 +159,8 @@ void xErrorMinusColumnAboutToBeRemoved(const AbstractAspect*); void yErrorPlusColumnAboutToBeRemoved(const AbstractAspect*); void yErrorMinusColumnAboutToBeRemoved(const AbstractAspect*); - + void xColumnNameChanged(); + void yColumnNameChanged(); //SLOTs for changes triggered via QActions in the context menu void visibilityChanged(); void navigateTo(); diff --git a/src/backend/worksheet/plots/cartesian/XYCurve.cpp b/src/backend/worksheet/plots/cartesian/XYCurve.cpp --- a/src/backend/worksheet/plots/cartesian/XYCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYCurve.cpp @@ -329,6 +329,7 @@ void XYCurve::setXColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->xColumn) { + setXColumnPath(column->path()); exec(new XYCurveSetXColumnCmd(d, column, ki18n("%1: x-data source changed"))); //emit xDataChanged() in order to notify the plot about the changes @@ -338,7 +339,7 @@ connect(column, &AbstractColumn::dataChanged, this, [=](){ d->recalcLogicalPoints(); }); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &XYCurve::xColumnAboutToBeRemoved); - + connect(column, &AbstractAspect::aspectDescriptionChanged, this, &XYCurve::xColumnNameChanged); //after the curve was updated, emit the signal to update the plot ranges connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SIGNAL(xDataChanged())); @@ -351,6 +352,9 @@ void XYCurve::setYColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->yColumn) { + setYColumnPath(column->path()); + // disconnect old column + disconnect(d->yColumn, &AbstractAspect::aspectDescriptionChanged, this, &XYCurve::yColumnNameChanged); exec(new XYCurveSetYColumnCmd(d, column, ki18n("%1: y-data source changed"))); //emit yDataChanged() in order to notify the plot about the changes @@ -360,6 +364,7 @@ connect(column, &AbstractColumn::dataChanged, this, [=](){ d->recalcLogicalPoints(); }); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &XYCurve::yColumnAboutToBeRemoved); + connect(column, &AbstractAspect::aspectDescriptionChanged, this, &XYCurve::yColumnNameChanged); //after the curve was updated, emit the signal to update the plot ranges connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SIGNAL(yDataChanged())); @@ -830,6 +835,16 @@ } } +void XYCurve::xColumnNameChanged() { + Q_D(XYCurve); + setXColumnPath(d->xColumn->path()); +} + +void XYCurve::yColumnNameChanged() { + Q_D(XYCurve); + setYColumnPath(d->yColumn->path()); +} + //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## diff --git a/src/commonfrontend/widgets/TreeViewComboBox.h b/src/commonfrontend/widgets/TreeViewComboBox.h --- a/src/commonfrontend/widgets/TreeViewComboBox.h +++ b/src/commonfrontend/widgets/TreeViewComboBox.h @@ -53,11 +53,19 @@ void showPopup() override; void hidePopup() override; + void setInvalid(bool invalid, QString tooltip = QString()); + + void useCurrentIndexText(const bool set); + + QString currentText() const; + void setText(QString text); private: QTreeView* m_treeView; QGroupBox* m_groupBox; QLineEdit* m_lineEdit; + QString m_lineEditText{""}; + bool m_useCurrentIndexText{true}; QList m_topLevelClasses; QList m_selectableClasses; @@ -69,6 +77,8 @@ bool isTopLevel(const AbstractAspect*) const; bool isHidden(const AbstractAspect*) const; + void paintEvent(QPaintEvent *) override; + private slots: void treeViewIndexActivated(const QModelIndex&); void filterChanged(const QString&); diff --git a/src/commonfrontend/widgets/TreeViewComboBox.cpp b/src/commonfrontend/widgets/TreeViewComboBox.cpp --- a/src/commonfrontend/widgets/TreeViewComboBox.cpp +++ b/src/commonfrontend/widgets/TreeViewComboBox.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -76,6 +77,7 @@ addItem(QString()); setCurrentIndex(0); + setEditText(m_lineEditText); // signal activated() is platform dependent connect(m_treeView, &QTreeView::pressed, this, &TreeViewComboBox::treeViewIndexActivated); @@ -103,6 +105,8 @@ //Expand the complete tree in order to see everything in the first popup. m_treeView->expandAll(); + + setEditText(m_lineEditText); } /*! @@ -132,17 +136,79 @@ QModelIndex root = m_treeView->model()->index(0,0); showTopLevelOnly(root); - - m_lineEdit->setText(QString()); m_groupBox->show(); m_groupBox->resize(this->width(), 250); m_groupBox->move(mapToGlobal( this->rect().topLeft() )); + + setEditText(m_lineEditText); +} + +/*! + \reimp + TODO: why do I have to reimplement paintEvent. It should work + also without +*/ +void TreeViewComboBox::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + opt.currentText = currentText(); // TODO: why it's not working when letting this away? + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + // draw the icon and text + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } void TreeViewComboBox::hidePopup() { m_groupBox->hide(); } + +void TreeViewComboBox::useCurrentIndexText(const bool set) { + m_useCurrentIndexText = set; +} + +/*! + \property QComboBox::currentText + \brief the current text + If the combo box is editable, the current text is the value displayed + by the line edit. Otherwise, it is the value of the current item or + an empty string if the combo box is empty or no current item is set. + The setter setCurrentText() simply calls setEditText() if the combo box is editable. + Otherwise, if there is a matching text in the list, currentIndex is set to the + corresponding index. + If m_useCurrentIndexText is false, the Text set with setText is used. The intention of displaying + this text is to show a text in the case of removed element. + \sa editable, setEditText() +*/ +QString TreeViewComboBox::currentText() const { + if (lineEdit()) + return lineEdit()->text(); + else if (currentModelIndex().isValid() && m_useCurrentIndexText) + return itemText(currentIndex()); + else if (!m_useCurrentIndexText) + return m_lineEditText; + else + return QString(); +} + +void TreeViewComboBox::setText(QString text) { + m_lineEditText = text; +} + +void TreeViewComboBox::setInvalid(bool invalid, QString tooltip) { + if (invalid) { + setStyleSheet("background: red;"); + setToolTip(tooltip); + return; + } + + setToolTip(""); + setStyleSheet(""); +} + /*! Hides the non-toplevel items of the model used in the tree view. */ diff --git a/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp b/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYConvolutionCurveDock.cpp @@ -157,6 +157,10 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + checkCurveAvailability(m_curve, cbY2DataColumn, "y2DataColumn"); + //show the properties of the first curve m_convolutionCurve = dynamic_cast(m_curve); @@ -179,6 +183,10 @@ this->autoRangeChanged(); y2DataColumnChanged(cbY2DataColumn->currentModelIndex()); + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + checkCurveAvailability(m_curve, cbY2DataColumn, "y2DataColumn"); + // settings uiGeneralTab.cbDirection->setCurrentIndex(m_convolutionData.direction); uiGeneralTab.cbType->setCurrentIndex(m_convolutionData.type); @@ -338,6 +346,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYConvolutionCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -350,6 +361,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYConvolutionCurveDock::y2DataColumnChanged(const QModelIndex& index) { @@ -362,6 +376,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setY2DataColumn(column); + + cbY2DataColumn->useCurrentIndexText(true); + cbY2DataColumn->setInvalid(false); } void XYConvolutionCurveDock::samplingIntervalChanged() { diff --git a/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYCorrelationCurveDock.cpp @@ -146,6 +146,10 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + checkCurveAvailability(m_curve, cbY2DataColumn, "y2DataColumn"); + //show the properties of the first curve m_correlationCurve = dynamic_cast(m_curve); @@ -166,6 +170,10 @@ this->autoRangeChanged(); y2DataColumnChanged(cbY2DataColumn->currentModelIndex()); + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + checkCurveAvailability(m_curve, cbY2DataColumn, "y2DataColumn"); + // settings uiGeneralTab.cbType->setCurrentIndex(m_correlationData.type); //m_correlationData.method not used @@ -314,6 +322,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYCorrelationCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -326,6 +337,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYCorrelationCurveDock::y2DataColumnChanged(const QModelIndex& index) { @@ -338,6 +352,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setY2DataColumn(column); + + cbY2DataColumn->useCurrentIndexText(true); + cbY2DataColumn->setInvalid(false); } void XYCorrelationCurveDock::samplingIntervalChanged() { diff --git a/src/kdefrontend/dockwidgets/XYCurveDock.h b/src/kdefrontend/dockwidgets/XYCurveDock.h --- a/src/kdefrontend/dockwidgets/XYCurveDock.h +++ b/src/kdefrontend/dockwidgets/XYCurveDock.h @@ -50,6 +50,7 @@ void setCurves(QList); virtual void setupGeneral(); + void checkCurveAvailability(XYCurve*, TreeViewComboBox*, QString); private: virtual void initGeneralTab(); diff --git a/src/kdefrontend/dockwidgets/XYCurveDock.cpp b/src/kdefrontend/dockwidgets/XYCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYCurveDock.cpp @@ -30,6 +30,8 @@ #include "XYCurveDock.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/Worksheet.h" +#include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h" +#include "backend/worksheet/plots/cartesian/XYFitCurve.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" @@ -551,6 +553,14 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXColumn, "xColumn"); + checkCurveAvailability(m_curve, cbYColumn, "yColumn"); + checkCurveAvailability(m_curve, cbValuesColumn, "valuesColumn"); + checkCurveAvailability(m_curve, cbXErrorPlusColumn, "xErrorPlusColumn"); + checkCurveAvailability(m_curve, cbXErrorMinusColumn, "xErrorMinusColumn"); + checkCurveAvailability(m_curve, cbYErrorPlusColumn, "yErrorPlusColumn"); + checkCurveAvailability(m_curve, cbYErrorMinusColumn, "yErrorMinusColumn"); + //show the properties of the first curve uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); @@ -715,6 +725,93 @@ } } +void XYCurveDock::checkCurveAvailability(XYCurve* curve, TreeViewComboBox* cb, QString type) { + QString path; + QString designation; + AbstractColumn* column = nullptr; + bool found = false; + + if (!cb) + return;// normally it should be called + + if (type == "xColumn") { + path = curve->xColumnPath(); + column = const_cast(curve->xColumn()); + found = true; + } else if (type == "yColumn") { + path = curve->yColumnPath(); + column = const_cast(curve->yColumn()); + found = true; + } else if (type == "valuesColumn") { + path = curve->valuesColumnPath(); + column = const_cast(curve->valuesColumn()); + found = true; + } else if (type == "xErrorPlusColumn") { + path = curve->xErrorPlusColumnPath(); + column = const_cast(curve->xErrorPlusColumn()); + found = true; + } else if (type == "xErrorMinusColumn") { + path = curve->xErrorMinusColumnPath(); + column = const_cast(curve->xErrorMinusColumn()); + found = true; + } else if (type == "yErrorPlusColumn") { + path = curve->yErrorPlusColumnPath(); + column = const_cast(curve->yErrorPlusColumn()); + found = true; + } else if (type == "yErrorMinusColumn") { + path = curve->yErrorMinusColumnPath(); + column = const_cast(curve->yErrorMinusColumn()); + found = true; + } + + auto* analysisCurve = dynamic_cast(curve); + if (analysisCurve) { + + if (type == "xDataColumn") { + path = analysisCurve->xDataColumnPath(); + column = const_cast(analysisCurve->xDataColumn()); + found = true; + } else if (type == "yDataColumn") { + path = analysisCurve->yDataColumnPath(); + column = const_cast(analysisCurve->yDataColumn()); + found = true; + } else if (type == "y2DataColumn") { + path = analysisCurve->y2DataColumnPath(); + column = const_cast(analysisCurve->y2DataColumn()); + found = true; + } + + auto* fitCurve = dynamic_cast(curve); + if (fitCurve) { + if (type == "xErrorColumn") { + path = fitCurve->xErrorColumnPath(); + column = const_cast(fitCurve->xErrorColumn()); + found = true; + } else if (type == "yErrorColumn") { + path = fitCurve->yErrorColumnPath(); + column = const_cast(fitCurve->yErrorColumn()); + found = true; + } + } + } + + if (!found) + return; + + if (path.isEmpty()) + return; // don't make the comboboxes red when initially created curves + + if (column){ + // current index text should be used + cb->useCurrentIndexText(true); + cb->setInvalid(false); + } else { + cb->useCurrentIndexText(false); + cb->setInvalid(true, i18n("The column \"%1\"\nis not available anymore. It will be automatically used once it is created again.", path)); + } + cb->setText(path.split("/").last()); +} + /*! shows the formatting properties of the column \c column. Called, when a new column for the values was selected - either by changing the type of the values (none, x, y, etc.) or @@ -1742,12 +1839,16 @@ void XYCurveDock::curveXColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbXColumn, column); + cbXColumn->useCurrentIndexText(true); + cbXColumn->setInvalid(false); m_initializing = false; } void XYCurveDock::curveYColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbYColumn, column); + cbYColumn->useCurrentIndexText(true); + cbYColumn->setInvalid(false); m_initializing = false; } diff --git a/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp b/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp @@ -140,6 +140,9 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + //show the properties of the first curve m_dataReductionCurve = dynamic_cast(m_curve); @@ -155,6 +158,9 @@ // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + uiGeneralTab.cbType->setCurrentIndex(m_dataReductionData.type); this->typeChanged(); uiGeneralTab.chkAuto->setChecked(m_dataReductionData.autoTolerance); @@ -300,6 +306,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + updateTolerance(); updateTolerance2(); } @@ -314,6 +323,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + updateTolerance(); updateTolerance2(); } diff --git a/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp @@ -134,6 +134,9 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + //show the properties of the first curve m_differentiationCurve = dynamic_cast(m_curve); @@ -149,6 +152,9 @@ // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + uiGeneralTab.cbDerivOrder->setCurrentIndex(m_differentiationData.derivOrder); this->derivOrderChanged(); uiGeneralTab.sbAccOrder->setValue(m_differentiationData.accOrder); @@ -283,6 +289,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYDifferentiationCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -294,6 +303,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } /*! diff --git a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp @@ -237,6 +237,11 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + checkCurveAvailability(m_curve, cbXErrorColumn, "xErrorColumn"); + checkCurveAvailability(m_curve, cbYErrorColumn, "yErrorColumn"); + uiGeneralTab.cbDataSourceType->setCurrentIndex(m_fitCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_fitCurve->dataSourceCurve()); @@ -409,6 +414,9 @@ // set model dependent start values from new data XYFitCurve::initStartValues(m_fitData, m_curve); + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYFitCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -423,6 +431,9 @@ // set model dependent start values from new data XYFitCurve::initStartValues(m_fitData, m_curve); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYFitCurveDock::xErrorColumnChanged(const QModelIndex& index) { @@ -434,6 +445,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setXErrorColumn(column); + + cbXErrorColumn->useCurrentIndexText(true); + cbXErrorColumn->setInvalid(false); } void XYFitCurveDock::yErrorColumnChanged(const QModelIndex& index) { @@ -445,6 +459,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYErrorColumn(column); + + cbYErrorColumn->useCurrentIndexText(true); + cbYErrorColumn->setInvalid(false); } ///////////////////////// fold/unfold options ////////////////////////////////////////////////// diff --git a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp @@ -142,6 +142,9 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + //show the properties of the first curve m_filterCurve = dynamic_cast(m_curve); @@ -301,6 +304,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYFourierFilterCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -312,6 +318,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYFourierFilterCurveDock::autoRangeChanged() { diff --git a/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp @@ -126,6 +126,9 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + //show the properties of the first curve m_transformCurve = dynamic_cast(m_curve); @@ -226,6 +229,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYFourierTransformCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -237,6 +243,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYFourierTransformCurveDock::autoRangeChanged() { diff --git a/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp @@ -135,6 +135,9 @@ uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); + //show the properties of the first curve m_integrationCurve = dynamic_cast(m_curve); @@ -288,6 +291,9 @@ // disable integration methods that need more data points this->updateSettings(column); } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } /*! @@ -307,11 +313,16 @@ if (m_initializing) return; + cbYDataColumn->hidePopup(); + auto* aspect = static_cast(index.internalPointer()); auto* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYIntegrationCurveDock::autoRangeChanged() { diff --git a/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp @@ -156,6 +156,8 @@ uiGeneralTab.leName->setText(QString()); uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); //show the properties of the first curve m_interpolationCurve = dynamic_cast(m_curve); @@ -319,6 +321,9 @@ for (XYCurve* curve: m_curvesList) dynamic_cast(curve)->setXDataColumn(column); + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYInterpolationCurveDock::updateSettings(const AbstractColumn* column) { @@ -411,6 +416,9 @@ for (XYCurve* curve: m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYInterpolationCurveDock::autoRangeChanged() { diff --git a/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp b/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp --- a/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp @@ -147,6 +147,8 @@ uiGeneralTab.leName->setText(QString()); uiGeneralTab.leComment->setText(QString()); } + checkCurveAvailability(m_curve, cbXDataColumn, "xDataColumn"); + checkCurveAvailability(m_curve, cbYDataColumn, "yDataColumn"); //show the properties of the first curve m_smoothCurve = dynamic_cast(m_curve); @@ -310,6 +312,9 @@ uiGeneralTab.sbPoints->setMaximum((int)n); } + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } void XYSmoothCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -321,6 +326,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYSmoothCurveDock::autoRangeChanged() {