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 @@ -82,6 +82,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,127 @@ d->changed = value; } -bool Project ::hasChanged() const { +bool Project::hasChanged() const { return d->changed ; } +/*! + * \brief Project::descriptionChanged + * \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; + + // 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. + 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) { + curve->setUndoAware(false); + 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); + } + curve->setUndoAware(true); + + } 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) { + curve->setUndoAware(false); + 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); + } + curve->setUndoAware(true); + } + +} + void Project::navigateTo(const QString& path) { emit requestNavigateTo(path); } @@ -382,6 +489,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 @@ -159,6 +159,11 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + checkColumnAvailability(cbY2DataColumn, analysisCurve->y2DataColumn(), analysisCurve->y2DataColumnPath()); + //show the properties of the first curve m_convolutionCurve = dynamic_cast(m_curve); @@ -326,6 +331,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYConvolutionCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -338,6 +346,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYConvolutionCurveDock::y2DataColumnChanged(const QModelIndex& index) { @@ -350,6 +361,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setY2DataColumn(column); + + cbY2DataColumn->useCurrentIndexText(true); + cbY2DataColumn->setInvalid(false); } void XYConvolutionCurveDock::samplingIntervalChanged() { @@ -526,6 +540,10 @@ if (m_convolutionCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectY != nullptr); + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_convolutionCurve->dataSourceCurve() != nullptr); } 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 @@ -148,6 +148,11 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + checkColumnAvailability(cbY2DataColumn, analysisCurve->y2DataColumn(), analysisCurve->y2DataColumnPath()); + //show the properties of the first curve m_correlationCurve = dynamic_cast(m_curve); @@ -302,6 +307,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYCorrelationCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -314,6 +322,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYCorrelationCurveDock::y2DataColumnChanged(const QModelIndex& index) { @@ -326,6 +337,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setY2DataColumn(column); + + cbY2DataColumn->useCurrentIndexText(true); + cbY2DataColumn->setInvalid(false); } void XYCorrelationCurveDock::samplingIntervalChanged() { @@ -415,6 +429,14 @@ AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY2 = static_cast(cbY2DataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectY != nullptr && aspectY2 != nullptr); + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } + if (aspectY2) { + cbY2DataColumn->useCurrentIndexText(true); + cbY2DataColumn->setInvalid(false); + } } else { hasSourceData = (m_correlationCurve->dataSourceCurve() != nullptr); } 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 @@ -52,6 +52,7 @@ void setCurves(QList); virtual void setupGeneral(); + void checkColumnAvailability(TreeViewComboBox*, const AbstractColumn *column, const QString columnPath); 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" @@ -559,8 +561,13 @@ uiGeneralTab.leComment->setText(QString()); } - uiGeneralTab.leName->setStyleSheet(""); - uiGeneralTab.leName->setToolTip(""); + checkColumnAvailability(cbXColumn, m_curve->xColumn(), m_curve->xColumnPath()); + checkColumnAvailability(cbYColumn, m_curve->yColumn(), m_curve->yColumnPath()); + checkColumnAvailability(cbValuesColumn, m_curve->valuesColumn(), m_curve->valuesColumnPath()); + checkColumnAvailability(cbXErrorPlusColumn, m_curve->xErrorPlusColumn(), m_curve->xErrorPlusColumnPath()); + checkColumnAvailability(cbXErrorMinusColumn, m_curve->xErrorMinusColumn(), m_curve->xErrorMinusColumnPath()); + checkColumnAvailability(cbYErrorPlusColumn, m_curve->yErrorPlusColumn(), m_curve->yErrorPlusColumnPath()); + checkColumnAvailability(cbYErrorMinusColumn, m_curve->yErrorMinusColumn(), m_curve->yErrorMinusColumnPath()); //show the properties of the first curve uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); @@ -726,6 +733,25 @@ } } +void XYCurveDock::checkColumnAvailability(TreeViewComboBox* cb, const AbstractColumn* column, const QString columnPath) { + + if (!cb) + return;// normally it shouldn't be called + + if (columnPath.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.", columnPath)); + } + cb->setText(columnPath.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 @@ -1738,12 +1764,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 @@ -142,6 +142,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_dataReductionCurve = dynamic_cast(m_curve); @@ -288,6 +292,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + updateTolerance(); updateTolerance2(); } @@ -302,6 +309,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + updateTolerance(); updateTolerance2(); } @@ -589,6 +599,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_dataReductionCurve->dataSourceCurve() != nullptr); } 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 @@ -136,6 +136,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_differentiationCurve = dynamic_cast(m_curve); @@ -271,6 +275,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYDifferentiationCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -282,6 +289,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } /*! @@ -479,6 +489,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_differentiationCurve->dataSourceCurve() != nullptr); } 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 @@ -239,6 +239,13 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + auto* fitCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXErrorColumn, fitCurve->xErrorColumn(), fitCurve->xErrorColumnPath()); + checkColumnAvailability(cbYErrorColumn, fitCurve->yErrorColumn(), fitCurve->yErrorColumnPath()); + uiGeneralTab.cbDataSourceType->setCurrentIndex(m_fitCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_fitCurve->dataSourceCurve()); @@ -397,6 +404,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) { @@ -411,6 +421,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) { @@ -422,6 +435,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setXErrorColumn(column); + + cbXErrorColumn->useCurrentIndexText(true); + cbXErrorColumn->setInvalid(false); } void XYFitCurveDock::yErrorColumnChanged(const QModelIndex& index) { @@ -433,6 +449,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYErrorColumn(column); + + cbYErrorColumn->useCurrentIndexText(true); + cbYErrorColumn->setInvalid(false); } ///////////////////////// fold/unfold options ////////////////////////////////////////////////// @@ -991,6 +1010,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_fitCurve->dataSourceCurve() != nullptr); } 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 @@ -144,6 +144,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_filterCurve = dynamic_cast(m_curve); @@ -289,6 +293,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYFourierFilterCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -300,6 +307,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYFourierFilterCurveDock::autoRangeChanged() { @@ -590,6 +600,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_filterCurve->dataSourceCurve() != nullptr); } 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 @@ -128,6 +128,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_transformCurve = dynamic_cast(m_curve); @@ -214,6 +218,9 @@ uiGeneralTab.sbMax->setValue(column->maximum()); } } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYFourierTransformCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -225,6 +232,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYFourierTransformCurveDock::autoRangeChanged() { @@ -324,6 +334,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); bool data = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } uiGeneralTab.pbRecalculate->setEnabled(data); } 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 @@ -137,6 +137,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_integrationCurve = dynamic_cast(m_curve); @@ -277,6 +281,9 @@ // disable integration methods that need more data points this->updateSettings(column); } + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } /*! @@ -296,11 +303,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() { @@ -394,6 +406,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_integrationCurve->dataSourceCurve() != nullptr); } 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 @@ -159,6 +159,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_interpolationCurve = dynamic_cast(m_curve); Q_ASSERT(m_interpolationCurve); @@ -307,6 +311,9 @@ for (XYCurve* curve: m_curvesList) dynamic_cast(curve)->setXDataColumn(column); + + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); } void XYInterpolationCurveDock::updateSettings(const AbstractColumn* column) { @@ -399,6 +406,9 @@ for (XYCurve* curve: m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYInterpolationCurveDock::autoRangeChanged() { @@ -623,6 +633,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_interpolationCurve->dataSourceCurve() != nullptr); } 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 @@ -150,6 +150,10 @@ uiGeneralTab.leComment->setText(QString()); } + auto* analysisCurve = dynamic_cast(m_curve); + checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); + checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); + //show the properties of the first curve m_smoothCurve = dynamic_cast(m_curve); @@ -298,6 +302,9 @@ uiGeneralTab.sbPoints->setMaximum((int)n); } + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } void XYSmoothCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -309,6 +316,9 @@ for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); + + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); } void XYSmoothCurveDock::autoRangeChanged() { @@ -486,6 +496,14 @@ AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != nullptr && aspectY != nullptr); + if (aspectX) { + cbXDataColumn->useCurrentIndexText(true); + cbXDataColumn->setInvalid(false); + } + if (aspectY) { + cbYDataColumn->useCurrentIndexText(true); + cbYDataColumn->setInvalid(false); + } } else { hasSourceData = (m_smoothCurve->dataSourceCurve() != nullptr); }