File indexing completed on 2025-01-26 03:34:10

0001 /*
0002     File                 : ErrorBar.cpp
0003     Project              : LabPlot
0004     Description          : ErrorBar
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2024 Alexander Semke <alexander.semke@web.de>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 /*!
0011   \class ErrorBar
0012   \brief This class contains the background properties of worksheet elements like worksheet background,
0013   plot background, the area filling in ErrorBar, ErrorBar, etc.
0014 
0015   \ingroup worksheet
0016 */
0017 
0018 #include "ErrorBar.h"
0019 #include "ErrorBarPrivate.h"
0020 #include "backend/core/AbstractColumn.h"
0021 #include "backend/core/Project.h"
0022 #include "backend/lib/XmlStreamReader.h"
0023 #include "backend/lib/commandtemplates.h"
0024 #include "backend/lib/macrosCurve.h"
0025 
0026 #include <KConfigGroup>
0027 #include <KLocalizedString>
0028 
0029 CURVE_COLUMN_CONNECT(ErrorBar, Plus, plus, update)
0030 CURVE_COLUMN_CONNECT(ErrorBar, Minus, minus, update)
0031 
0032 ErrorBar::ErrorBar(const QString& name)
0033     : AbstractAspect(name, AspectType::AbstractAspect)
0034     , d_ptr(new ErrorBarPrivate(this)) {
0035 }
0036 
0037 ErrorBar::~ErrorBar() {
0038     delete d_ptr;
0039 }
0040 
0041 void ErrorBar::setPrefix(const QString& prefix) {
0042     Q_D(ErrorBar);
0043     d->prefix = prefix;
0044 }
0045 
0046 const QString& ErrorBar::prefix() const {
0047     Q_D(const ErrorBar);
0048     return d->prefix;
0049 }
0050 
0051 void ErrorBar::init(const KConfigGroup& group) {
0052     Q_D(ErrorBar);
0053     d->type = (Type)group.readEntry(d->prefix + QStringLiteral("ErrorType"), static_cast<int>(Type::NoError));
0054 }
0055 
0056 void ErrorBar::update() {
0057     Q_D(ErrorBar);
0058     d->update();
0059 }
0060 
0061 // ##############################################################################
0062 // ##########################  getter methods  ##################################
0063 // ##############################################################################
0064 BASIC_SHARED_D_READER_IMPL(ErrorBar, ErrorBar::Type, type, type)
0065 BASIC_SHARED_D_READER_IMPL(ErrorBar, const AbstractColumn*, plusColumn, plusColumn)
0066 BASIC_SHARED_D_READER_IMPL(ErrorBar, const AbstractColumn*, minusColumn, minusColumn)
0067 BASIC_SHARED_D_READER_IMPL(ErrorBar, QString, plusColumnPath, plusColumnPath)
0068 BASIC_SHARED_D_READER_IMPL(ErrorBar, QString, minusColumnPath, minusColumnPath)
0069 
0070 // ##############################################################################
0071 // #################  setter methods and undo commands ##########################
0072 // ##############################################################################
0073 STD_SETTER_CMD_IMPL_F_S(ErrorBar, SetType, ErrorBar::Type, type, update)
0074 void ErrorBar::setType(Type type) {
0075     Q_D(ErrorBar);
0076     if (type != d->type)
0077         exec(new ErrorBarSetTypeCmd(d, type, ki18n("%1: error type changed")));
0078 }
0079 
0080 CURVE_COLUMN_SETTER_CMD_IMPL_F_S(ErrorBar, Plus, plus, update)
0081 void ErrorBar::setPlusColumn(const AbstractColumn* column) {
0082     Q_D(ErrorBar);
0083     if (column != d->plusColumn)
0084         exec(new ErrorBarSetPlusColumnCmd(d, column, ki18n("%1: set error column")));
0085 }
0086 
0087 void ErrorBar::setPlusColumnPath(const QString& path) {
0088     Q_D(ErrorBar);
0089     d->plusColumnPath = path;
0090 }
0091 
0092 CURVE_COLUMN_SETTER_CMD_IMPL_F_S(ErrorBar, Minus, minus, update)
0093 void ErrorBar::setMinusColumn(const AbstractColumn* column) {
0094     Q_D(ErrorBar);
0095     if (column != d->minusColumn)
0096         exec(new ErrorBarSetMinusColumnCmd(d, column, ki18n("%1: set error column")));
0097 }
0098 
0099 void ErrorBar::setMinusColumnPath(const QString& path) {
0100     Q_D(ErrorBar);
0101     d->minusColumnPath = path;
0102 }
0103 
0104 void ErrorBar::plusColumnAboutToBeRemoved(const AbstractAspect* aspect) {
0105     Q_D(ErrorBar);
0106     if (aspect == d->plusColumn) {
0107         d->plusColumn = nullptr;
0108         d->update();
0109     }
0110 }
0111 
0112 void ErrorBar::minusColumnAboutToBeRemoved(const AbstractAspect* aspect) {
0113     Q_D(ErrorBar);
0114     if (aspect == d->minusColumn) {
0115         d->minusColumn = nullptr;
0116         d->update();
0117     }
0118 }
0119 
0120 // ##############################################################################
0121 // ####################### Private implementation ###############################
0122 // ##############################################################################
0123 ErrorBarPrivate::ErrorBarPrivate(ErrorBar* owner)
0124     : q(owner) {
0125 }
0126 
0127 QString ErrorBarPrivate::name() const {
0128     return q->parentAspect()->name();
0129 }
0130 
0131 void ErrorBarPrivate::update() {
0132     Q_EMIT q->updateRequested();
0133 }
0134 
0135 // ##############################################################################
0136 // ##################  Serialization/Deserialization  ###########################
0137 // ##############################################################################
0138 //! Save as XML
0139 void ErrorBar::save(QXmlStreamWriter* writer) const {
0140     Q_D(const ErrorBar);
0141 
0142     if (!d->prefix.isEmpty()) {
0143         // for names in the XML file, the first letter is lower case but the camel case still remains.
0144         QString newPrefix = d->prefix;
0145         newPrefix.replace(0, 1, d->prefix.at(0).toLower());
0146 
0147         writer->writeAttribute(newPrefix + QStringLiteral("ErrorType"), QString::number(static_cast<int>(d->type)));
0148 
0149         if (d->plusColumn)
0150             writer->writeAttribute(newPrefix + QStringLiteral("ErrorPlusColumn"), d->plusColumn->path());
0151         else
0152             writer->writeAttribute(newPrefix + QStringLiteral("ErrorPlusColumn"), QString());
0153 
0154         if (d->minusColumn)
0155             writer->writeAttribute(newPrefix + QStringLiteral("ErrorMinusColumn"), d->minusColumn->path());
0156         else
0157             writer->writeAttribute(newPrefix + QStringLiteral("ErrorMinusColumn"), QString());
0158     } else {
0159         writer->writeAttribute(QStringLiteral("errorType"), QString::number(static_cast<int>(d->type)));
0160         WRITE_COLUMN(d->plusColumn, errorPlusColumn);
0161         WRITE_COLUMN(d->minusColumn, errorMinusColumn);
0162     }
0163 }
0164 
0165 //! Load from XML
0166 bool ErrorBar::load(XmlStreamReader* reader, bool preview) {
0167     if (preview)
0168         return true;
0169 
0170     Q_D(ErrorBar);
0171     QString str;
0172     auto attribs = reader->attributes();
0173     int type = 0;
0174 
0175     if (!d->prefix.isEmpty()) {
0176         QString newPrefix = d->prefix;
0177         newPrefix.replace(0, 1, d->prefix.at(0).toLower());
0178 
0179         type = attribs.value(newPrefix + QStringLiteral("ErrorType")).toInt();
0180         d->plusColumnPath = attribs.value(newPrefix + QStringLiteral("ErrorPlusColumn")).toString();
0181         d->minusColumnPath = attribs.value(newPrefix + QStringLiteral("ErrorMinusColumn")).toString();
0182     } else {
0183         type = attribs.value(QStringLiteral("errorType")).toInt();
0184         d->plusColumnPath = attribs.value(QStringLiteral("errorPlusColumn")).toString();
0185         d->minusColumnPath = attribs.value(QStringLiteral("errorMinusColumn")).toString();
0186     }
0187 
0188     // prior to XML version 11, a different order of enum values for the error type was used in Histogram
0189     // (old "{ NoError, Poisson, CustomSymmetric, CustomAsymmetric }" instead of
0190     // the new "{ NoError, Symmetric, Asymmetric, Poisson }")
0191     // and we need to map from the old to the new values
0192     if (Project::xmlVersion() < 11 && parentAspect()->type() == AspectType::Histogram) {
0193         if (type == 0)
0194             d->type = ErrorBar::Type::NoError;
0195         else if (type == 1)
0196             d->type = ErrorBar::Type::Poisson;
0197         else if (type == 2)
0198             d->type = ErrorBar::Type::Symmetric;
0199         else if (type == 3)
0200             d->type = ErrorBar::Type::Asymmetric;
0201     } else
0202         d->type = static_cast<Type>(type);
0203 
0204     return true;
0205 }