File indexing completed on 2024-05-12 15:26:41

0001 /***************************************************************************
0002     File                 : DatapickerCurve.cpp
0003     Project              : LabPlot
0004     Description          : container for Curve-Point and Datasheet/Spreadsheet
0005                            of datapicker
0006     --------------------------------------------------------------------
0007     Copyright            : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com)
0008     Copyright            : (C) 2015-2019 Alexander Semke (alexander.semke@web.de)
0009  ***************************************************************************/
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "DatapickerCurve.h"
0030 #include "backend/datapicker/DatapickerCurvePrivate.h"
0031 #include "backend/datapicker/Datapicker.h"
0032 #include "backend/datapicker/DatapickerPoint.h"
0033 #include "backend/lib/commandtemplates.h"
0034 #include "backend/lib/XmlStreamReader.h"
0035 #include "backend/spreadsheet/Spreadsheet.h"
0036 #include "backend/worksheet/Worksheet.h"
0037 
0038 #include <QIcon>
0039 #include <QVector3D>
0040 
0041 #include <KConfig>
0042 #include <KLocalizedString>
0043 #include <KConfigGroup>
0044 
0045 /**
0046  * \class DatapickerCurve
0047  * \brief Top-level container for Curve-Point and Datasheet/Spreadsheet of datapicker.
0048  * \ingroup backend
0049  */
0050 
0051 DatapickerCurve::DatapickerCurve(const QString &name)
0052     : AbstractAspect(name, AspectType::DatapickerCurve), d_ptr(new DatapickerCurvePrivate(this)) {
0053 
0054     init();
0055 }
0056 
0057 DatapickerCurve::DatapickerCurve(const QString &name, DatapickerCurvePrivate *dd)
0058     : AbstractAspect(name, AspectType::DatapickerCurve), d_ptr(dd) {
0059 
0060     init();
0061 }
0062 
0063 DatapickerCurve::~DatapickerCurve() {
0064     delete d_ptr;
0065 }
0066 
0067 void DatapickerCurve::init() {
0068     Q_D(DatapickerCurve);
0069 
0070     KConfig config;
0071     KConfigGroup group;
0072     group = config.group("DatapickerCurve");
0073     d->curveErrorTypes.x = (ErrorType) group.readEntry("CurveErrorType_X", static_cast<int>(ErrorType::NoError));
0074     d->curveErrorTypes.y = (ErrorType) group.readEntry("CurveErrorType_Y", static_cast<int>(ErrorType::NoError));
0075 
0076     // point properties
0077     d->pointStyle = (Symbol::Style)group.readEntry("PointStyle", static_cast<int>(Symbol::Style::Cross));
0078     d->pointSize = group.readEntry("Size", Worksheet::convertToSceneUnits(7, Worksheet::Unit::Point));
0079     d->pointRotationAngle = group.readEntry("Rotation", 0.0);
0080     d->pointOpacity = group.readEntry("Opacity", 1.0);
0081     d->pointBrush.setStyle( (Qt::BrushStyle)group.readEntry("FillingStyle", (int)Qt::NoBrush) );
0082     d->pointBrush.setColor( group.readEntry("FillingColor", QColor(Qt::black)) );
0083     d->pointPen.setStyle( (Qt::PenStyle)group.readEntry("BorderStyle", (int)Qt::SolidLine) );
0084     d->pointPen.setColor( group.readEntry("BorderColor", QColor(Qt::red)) );
0085     d->pointPen.setWidthF( group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1, Worksheet::Unit::Point)) );
0086     d->pointErrorBarSize = group.readEntry("ErrorBarSize", Worksheet::convertToSceneUnits(8, Worksheet::Unit::Point));
0087     d->pointErrorBarBrush.setStyle( (Qt::BrushStyle)group.readEntry("ErrorBarFillingStyle", (int)Qt::NoBrush) );
0088     d->pointErrorBarBrush.setColor( group.readEntry("ErrorBarFillingColor", QColor(Qt::black)) );
0089     d->pointErrorBarPen.setStyle( (Qt::PenStyle)group.readEntry("ErrorBarBorderStyle", (int)Qt::SolidLine) );
0090     d->pointErrorBarPen.setColor( group.readEntry("ErrorBarBorderColor", QColor(Qt::black)) );
0091     d->pointErrorBarPen.setWidthF( group.readEntry("ErrorBarBorderWidth", Worksheet::convertToSceneUnits(1, Worksheet::Unit::Point)) );
0092     d->pointVisibility = group.readEntry("PointVisibility", true);
0093 }
0094 
0095 /*!
0096     Returns an icon to be used in the project explorer.
0097 */
0098 QIcon DatapickerCurve::icon() const {
0099     return QIcon::fromTheme("labplot-xy-curve");
0100 }
0101 
0102 Column* DatapickerCurve::appendColumn(const QString& name) {
0103     Column* col = new Column(i18n("Column"), AbstractColumn::ColumnMode::Numeric);
0104     col->insertRows(0, m_datasheet->rowCount());
0105     col->setName(name);
0106     m_datasheet->addChild(col);
0107 
0108     return col;
0109 }
0110 
0111 //##############################################################################
0112 //##########################  getter methods  ##################################
0113 //##############################################################################
0114 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, DatapickerCurve::Errors, curveErrorTypes, curveErrorTypes)
0115 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, Symbol::Style, pointStyle, pointStyle)
0116 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointOpacity, pointOpacity)
0117 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointRotationAngle, pointRotationAngle)
0118 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointSize, pointSize)
0119 CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QBrush, pointBrush, pointBrush)
0120 CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QPen, pointPen, pointPen)
0121 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointErrorBarSize, pointErrorBarSize)
0122 CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QBrush, pointErrorBarBrush, pointErrorBarBrush)
0123 CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QPen, pointErrorBarPen, pointErrorBarPen)
0124 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, bool, pointVisibility, pointVisibility)
0125 
0126 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, posXColumn, posXColumn)
0127 QString& DatapickerCurve::posXColumnPath() const {
0128     return d_ptr->posXColumnPath;
0129 }
0130 
0131 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, posYColumn, posYColumn)
0132 QString& DatapickerCurve::posYColumnPath() const {
0133     return d_ptr->posYColumnPath;
0134 }
0135 
0136 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, posZColumn, posZColumn)
0137 QString& DatapickerCurve::posZColumnPath() const {
0138     return d_ptr->posZColumnPath;
0139 }
0140 
0141 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, plusDeltaXColumn, plusDeltaXColumn)
0142 QString& DatapickerCurve::plusDeltaXColumnPath() const {
0143     return d_ptr->plusDeltaXColumnPath;
0144 }
0145 
0146 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, minusDeltaXColumn, minusDeltaXColumn)
0147 QString& DatapickerCurve::minusDeltaXColumnPath() const {
0148     return d_ptr->minusDeltaXColumnPath;
0149 }
0150 
0151 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, plusDeltaYColumn, plusDeltaYColumn)
0152 QString& DatapickerCurve::plusDeltaYColumnPath() const {
0153     return d_ptr->plusDeltaYColumnPath;
0154 }
0155 
0156 BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, minusDeltaYColumn, minusDeltaYColumn)
0157 QString& DatapickerCurve::minusDeltaYColumnPath() const {
0158     return d_ptr->minusDeltaYColumnPath;
0159 }
0160 
0161 //##############################################################################
0162 //#########################  setter methods  ###################################
0163 //##############################################################################
0164 void DatapickerCurve::addDatasheet(DatapickerImage::GraphType type) {
0165     Q_D(DatapickerCurve);
0166 
0167     m_datasheet = new Spreadsheet(i18n("Data"));
0168     addChild(m_datasheet);
0169     QString xLabel('x');
0170     QString yLabel('y');
0171 
0172     if (type == DatapickerImage::GraphType::PolarInDegree) {
0173         xLabel = QLatin1String("r");
0174         yLabel = QLatin1String("y(deg)");
0175     } else if (type == DatapickerImage::GraphType::PolarInRadians) {
0176         xLabel = QLatin1String("r");
0177         yLabel = QLatin1String("y(rad)");
0178     } else if (type == DatapickerImage::GraphType::LogarithmicX) {
0179         xLabel = QLatin1String("log(x)");
0180         yLabel = QLatin1String("y");
0181     } else if (type == DatapickerImage::GraphType::LogarithmicY) {
0182         xLabel = QLatin1String("x");
0183         yLabel = QLatin1String("log(y)");
0184     }
0185 
0186     if (type == DatapickerImage::GraphType::Ternary)
0187         d->posZColumn = appendColumn(i18n("c"));
0188 
0189     d->posXColumn = m_datasheet->column(0);
0190     d->posXColumn->setName(xLabel);
0191 
0192     d->posYColumn = m_datasheet->column(1);
0193     d->posYColumn->setName(yLabel);
0194 }
0195 
0196 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetCurveErrorTypes, DatapickerCurve::Errors, curveErrorTypes)
0197 void DatapickerCurve::setCurveErrorTypes(const DatapickerCurve::Errors errors) {
0198     Q_D(DatapickerCurve);
0199     if (d->curveErrorTypes.x != errors.x || d->curveErrorTypes.y != errors.y) {
0200         beginMacro(i18n("%1: set xy-error type", name()));
0201         exec(new DatapickerCurveSetCurveErrorTypesCmd(d, errors, ki18n("%1: set xy-error type")));
0202 
0203         if ( errors.x != ErrorType::NoError && !d->plusDeltaXColumn )
0204             setPlusDeltaXColumn(appendColumn(QLatin1String("+delta_x")));
0205         else if ( d->plusDeltaXColumn && errors.x ==ErrorType:: NoError ) {
0206             d->plusDeltaXColumn->remove();
0207             d->plusDeltaXColumn = nullptr;
0208         }
0209 
0210         if ( errors.x == ErrorType::AsymmetricError && !d->minusDeltaXColumn )
0211             setMinusDeltaXColumn(appendColumn(QLatin1String("-delta_x")));
0212         else if ( d->minusDeltaXColumn && errors.x != ErrorType::AsymmetricError ) {
0213             d->minusDeltaXColumn->remove();
0214             d->minusDeltaXColumn = nullptr;
0215         }
0216 
0217         if ( errors.y != ErrorType::NoError && !d->plusDeltaYColumn )
0218             setPlusDeltaYColumn(appendColumn(QLatin1String("+delta_y")));
0219         else if ( d->plusDeltaYColumn && errors.y == ErrorType::NoError ) {
0220             d->plusDeltaYColumn->remove();
0221             d->plusDeltaYColumn = nullptr;
0222         }
0223 
0224         if ( errors.y == ErrorType::AsymmetricError && !d->minusDeltaYColumn )
0225             setMinusDeltaYColumn(appendColumn(QLatin1String("-delta_y")));
0226         else if ( d->minusDeltaYColumn && errors.y != ErrorType::AsymmetricError ) {
0227             d->minusDeltaYColumn->remove();
0228             d->minusDeltaYColumn = nullptr;
0229         }
0230 
0231         endMacro();
0232     }
0233 }
0234 
0235 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPosXColumn, AbstractColumn*, posXColumn)
0236 void DatapickerCurve::setPosXColumn(AbstractColumn* column) {
0237     Q_D(DatapickerCurve);
0238     if (d->posXColumn != column)
0239         exec(new DatapickerCurveSetPosXColumnCmd(d, column, ki18n("%1: set position X column")));
0240 }
0241 
0242 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPosYColumn, AbstractColumn*, posYColumn)
0243 void DatapickerCurve::setPosYColumn(AbstractColumn* column) {
0244     Q_D(DatapickerCurve);
0245     if (d->posYColumn != column)
0246         exec(new DatapickerCurveSetPosYColumnCmd(d, column, ki18n("%1: set position Y column")));
0247 }
0248 
0249 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPosZColumn, AbstractColumn*, posZColumn)
0250 void DatapickerCurve::setPosZColumn(AbstractColumn* column) {
0251     Q_D(DatapickerCurve);
0252     if (d->posZColumn != column)
0253         exec(new DatapickerCurveSetPosZColumnCmd(d, column, ki18n("%1: set position Z column")));
0254 }
0255 
0256 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPlusDeltaXColumn, AbstractColumn*, plusDeltaXColumn)
0257 void DatapickerCurve::setPlusDeltaXColumn(AbstractColumn* column) {
0258     Q_D(DatapickerCurve);
0259     if (d->plusDeltaXColumn != column)
0260         exec(new DatapickerCurveSetPlusDeltaXColumnCmd(d, column, ki18n("%1: set +delta_X column")));
0261 }
0262 
0263 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetMinusDeltaXColumn, AbstractColumn*, minusDeltaXColumn)
0264 void DatapickerCurve::setMinusDeltaXColumn(AbstractColumn* column) {
0265     Q_D(DatapickerCurve);
0266     if (d->minusDeltaXColumn != column)
0267         exec(new DatapickerCurveSetMinusDeltaXColumnCmd(d, column, ki18n("%1: set -delta_X column")));
0268 }
0269 
0270 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPlusDeltaYColumn, AbstractColumn*, plusDeltaYColumn)
0271 void DatapickerCurve::setPlusDeltaYColumn(AbstractColumn* column) {
0272     Q_D(DatapickerCurve);
0273     if (d->plusDeltaYColumn != column)
0274         exec(new DatapickerCurveSetPlusDeltaYColumnCmd(d, column, ki18n("%1: set +delta_Y column")));
0275 }
0276 
0277 STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetMinusDeltaYColumn, AbstractColumn*, minusDeltaYColumn)
0278 void DatapickerCurve::setMinusDeltaYColumn(AbstractColumn* column) {
0279     Q_D(DatapickerCurve);
0280     if (d->minusDeltaYColumn != column)
0281         exec(new DatapickerCurveSetMinusDeltaYColumnCmd(d, column, ki18n("%1: set -delta_Y column")));
0282 }
0283 
0284 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointStyle, Symbol::Style, pointStyle, retransform)
0285 void DatapickerCurve::setPointStyle(Symbol::Style newStyle) {
0286     Q_D(DatapickerCurve);
0287     if (newStyle != d->pointStyle)
0288         exec(new DatapickerCurveSetPointStyleCmd(d, newStyle, ki18n("%1: set point's style")));
0289 }
0290 
0291 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointSize, qreal, pointSize, retransform)
0292 void DatapickerCurve::setPointSize(qreal value) {
0293     Q_D(DatapickerCurve);
0294     if (!qFuzzyCompare(1 + value, 1 + d->pointSize))
0295         exec(new DatapickerCurveSetPointSizeCmd(d, value, ki18n("%1: set point's size")));
0296 }
0297 
0298 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointRotationAngle, qreal, pointRotationAngle, retransform)
0299 void DatapickerCurve::setPointRotationAngle(qreal angle) {
0300     Q_D(DatapickerCurve);
0301     if (!qFuzzyCompare(1 + angle, 1 + d->pointRotationAngle))
0302         exec(new DatapickerCurveSetPointRotationAngleCmd(d, angle, ki18n("%1: rotate point")));
0303 }
0304 
0305 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointBrush, QBrush, pointBrush, retransform)
0306 void DatapickerCurve::setPointBrush(const QBrush& newBrush) {
0307     Q_D(DatapickerCurve);
0308     if (newBrush != d->pointBrush)
0309         exec(new DatapickerCurveSetPointBrushCmd(d, newBrush, ki18n("%1: set point's filling")));
0310 }
0311 
0312 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointPen, QPen, pointPen, retransform)
0313 void DatapickerCurve::setPointPen(const QPen &newPen) {
0314     Q_D(DatapickerCurve);
0315     if (newPen != d->pointPen)
0316         exec(new DatapickerCurveSetPointPenCmd(d, newPen, ki18n("%1: set outline style")));
0317 }
0318 
0319 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointOpacity, qreal, pointOpacity, retransform)
0320 void DatapickerCurve::setPointOpacity(qreal newOpacity) {
0321     Q_D(DatapickerCurve);
0322     if (newOpacity != d->pointOpacity)
0323         exec(new DatapickerCurveSetPointOpacityCmd(d, newOpacity, ki18n("%1: set point's opacity")));
0324 }
0325 
0326 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointErrorBarSize, qreal, pointErrorBarSize, retransform)
0327 void DatapickerCurve::setPointErrorBarSize(qreal size) {
0328     Q_D(DatapickerCurve);
0329     if (size != d->pointErrorBarSize)
0330         exec(new DatapickerCurveSetPointErrorBarSizeCmd(d, size, ki18n("%1: set error bar size")));
0331 }
0332 
0333 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointErrorBarBrush, QBrush, pointErrorBarBrush, retransform)
0334 void DatapickerCurve::setPointErrorBarBrush(const QBrush &brush) {
0335     Q_D(DatapickerCurve);
0336     if (brush != d->pointErrorBarBrush)
0337         exec(new DatapickerCurveSetPointErrorBarBrushCmd(d, brush, ki18n("%1: set error bar filling")));
0338 }
0339 
0340 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointErrorBarPen, QPen, pointErrorBarPen, retransform)
0341 void DatapickerCurve::setPointErrorBarPen(const QPen &pen) {
0342     Q_D(DatapickerCurve);
0343     if (pen != d->pointErrorBarPen)
0344         exec(new DatapickerCurveSetPointErrorBarPenCmd(d, pen, ki18n("%1: set error bar outline style")));
0345 }
0346 
0347 STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointVisibility, bool, pointVisibility, retransform)
0348 void DatapickerCurve::setPointVisibility(bool on) {
0349     Q_D(DatapickerCurve);
0350     if (on != d->pointVisibility)
0351         exec(new DatapickerCurveSetPointVisibilityCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
0352 }
0353 
0354 void DatapickerCurve::setPrinting(bool on) {
0355     for (auto* point : children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden))
0356         point->setPrinting(on);
0357 }
0358 
0359 /*!
0360     Selects or deselects the Datapicker/Curve in the project explorer.
0361     This function is called in \c DatapickerImageView.
0362 */
0363 void DatapickerCurve::setSelectedInView(bool b) {
0364     if (b)
0365         emit childAspectSelectedInView(this);
0366     else
0367         emit childAspectDeselectedInView(this);
0368 }
0369 //##############################################################################
0370 //######  SLOTs for changes triggered via QActions in the context menu  ########
0371 //##############################################################################
0372 void DatapickerCurve::updatePoints() {
0373     for (auto* point : children<DatapickerPoint>(ChildIndexFlag::IncludeHidden))
0374         updatePoint(point);
0375 }
0376 
0377 /*!
0378     Update datasheet for corresponding curve-point,
0379     it is called every time whenever there is any change in position
0380     of curve-point or its error-bar so keep it undo unaware
0381     no need to create extra entry in undo stack
0382 */
0383 void DatapickerCurve::updatePoint(const DatapickerPoint* point) {
0384     Q_D(DatapickerCurve);
0385 
0386     //TODO: this check shouldn't be required.
0387     //redesign the retransform()-call in load() to avoid it.
0388     if (!parentAspect())
0389         return;
0390 
0391     auto* datapicker = static_cast<Datapicker*>(parentAspect());
0392     int row = indexOfChild<DatapickerPoint>(point, ChildIndexFlag::IncludeHidden);
0393     QVector3D data = datapicker->mapSceneToLogical(point->position());
0394 
0395     if (d->posXColumn)
0396         d->posXColumn->setValueAt(row, data.x());
0397 
0398     if (d->posYColumn)
0399         d->posYColumn->setValueAt(row, data.y());
0400 
0401     if (d->posZColumn)
0402         d->posZColumn->setValueAt(row, data.y());
0403 
0404     if (d->plusDeltaXColumn) {
0405         data = datapicker->mapSceneLengthToLogical(QPointF(point->plusDeltaXPos().x(), 0));
0406         d->plusDeltaXColumn->setValueAt(row, qAbs(data.x()));
0407     }
0408 
0409     if (d->minusDeltaXColumn) {
0410         data = datapicker->mapSceneLengthToLogical(QPointF(point->minusDeltaXPos().x(), 0));
0411         d->minusDeltaXColumn->setValueAt(row, qAbs(data.x()));
0412     }
0413 
0414     if (d->plusDeltaYColumn) {
0415         data = datapicker->mapSceneLengthToLogical(QPointF(0, point->plusDeltaYPos().y()));
0416         d->plusDeltaYColumn->setValueAt(row, qAbs(data.y()));
0417     }
0418 
0419     if (d->minusDeltaYColumn) {
0420         data = datapicker->mapSceneLengthToLogical(QPointF(0, point->minusDeltaYPos().y()));
0421         d->minusDeltaYColumn->setValueAt(row, qAbs(data.y()));
0422     }
0423 }
0424 
0425 //##############################################################################
0426 //####################### Private implementation ###############################
0427 //##############################################################################
0428 DatapickerCurvePrivate::DatapickerCurvePrivate(DatapickerCurve *curve) : q(curve) {
0429 }
0430 
0431 QString DatapickerCurvePrivate::name() const {
0432     return q->name();
0433 }
0434 
0435 void DatapickerCurvePrivate::retransform() {
0436     auto points = q->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden);
0437     for (auto* point : points)
0438         point->retransform();
0439 }
0440 
0441 //##############################################################################
0442 //##################  Serialization/Deserialization  ###########################
0443 //##############################################################################
0444 //! Save as XML
0445 void DatapickerCurve::save(QXmlStreamWriter* writer) const {
0446     Q_D(const DatapickerCurve);
0447 
0448     writer->writeStartElement("datapickerCurve");
0449     writeBasicAttributes(writer);
0450     writeCommentElement(writer);
0451 
0452     //general
0453     writer->writeStartElement("general");
0454     WRITE_COLUMN(d->posXColumn, posXColumn);
0455     WRITE_COLUMN(d->posYColumn, posYColumn);
0456     WRITE_COLUMN(d->posZColumn, posZColumn);
0457     WRITE_COLUMN(d->plusDeltaXColumn, plusDeltaXColumn);
0458     WRITE_COLUMN(d->minusDeltaXColumn, minusDeltaXColumn);
0459     WRITE_COLUMN(d->plusDeltaYColumn, plusDeltaYColumn);
0460     WRITE_COLUMN(d->minusDeltaYColumn, minusDeltaYColumn);
0461     writer->writeAttribute( "curveErrorType_X", QString::number(static_cast<int>(d->curveErrorTypes.x)) );
0462     writer->writeAttribute( "curveErrorType_Y", QString::number(static_cast<int>(d->curveErrorTypes.y)) );
0463     writer->writeEndElement();
0464 
0465     //symbol properties
0466     writer->writeStartElement("symbolProperties");
0467     writer->writeAttribute( "pointRotationAngle", QString::number(d->pointRotationAngle) );
0468     writer->writeAttribute( "pointOpacity", QString::number(d->pointOpacity) );
0469     writer->writeAttribute( "pointSize", QString::number(d->pointSize) );
0470     writer->writeAttribute( "pointStyle", QString::number(static_cast<int>(d->pointStyle)) );
0471     writer->writeAttribute( "pointVisibility", QString::number(d->pointVisibility) );
0472     WRITE_QBRUSH(d->pointBrush);
0473     WRITE_QPEN(d->pointPen);
0474     writer->writeEndElement();
0475 
0476     //error bar properties
0477     writer->writeStartElement("errorBarProperties");
0478     writer->writeAttribute( "pointErrorBarSize", QString::number(d->pointErrorBarSize) );
0479     WRITE_QBRUSH(d->pointErrorBarBrush);
0480     WRITE_QPEN(d->pointErrorBarPen);
0481     writer->writeEndElement();
0482 
0483     //serialize all children
0484     for (auto* child : children<AbstractAspect>(ChildIndexFlag::IncludeHidden))
0485         child->save(writer);
0486 
0487     writer->writeEndElement(); // close section
0488 }
0489 
0490 //! Load from XML
0491 bool DatapickerCurve::load(XmlStreamReader* reader, bool preview) {
0492     Q_D(DatapickerCurve);
0493 
0494     if (!readBasicAttributes(reader))
0495         return false;
0496 
0497     KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
0498     QXmlStreamAttributes attribs;
0499     QString str;
0500 
0501     while (!reader->atEnd()) {
0502         reader->readNext();
0503         if (reader->isEndElement() && reader->name() == "datapickerCurve")
0504             break;
0505 
0506         if (!reader->isStartElement())
0507             continue;
0508 
0509         if (reader->name() == "comment") {
0510             if (!readCommentElement(reader)) return false;
0511         } else if (!preview && reader->name() == "general") {
0512             attribs = reader->attributes();
0513 
0514             READ_INT_VALUE("curveErrorType_X", curveErrorTypes.x, ErrorType);
0515             READ_INT_VALUE("curveErrorType_Y", curveErrorTypes.y, ErrorType);
0516 
0517             READ_COLUMN(posXColumn);
0518             READ_COLUMN(posYColumn);
0519             READ_COLUMN(posZColumn);
0520             READ_COLUMN(plusDeltaXColumn);
0521             READ_COLUMN(minusDeltaXColumn);
0522             READ_COLUMN(plusDeltaYColumn);
0523             READ_COLUMN(minusDeltaYColumn);
0524         } else if (!preview && reader->name() == "symbolProperties") {
0525             attribs = reader->attributes();
0526 
0527             READ_DOUBLE_VALUE("pointRotationAngle", pointRotationAngle);
0528             READ_DOUBLE_VALUE("pointOpacity", pointOpacity);
0529             READ_DOUBLE_VALUE("pointSize", pointSize);
0530             READ_INT_VALUE("pointStyle", pointStyle, Symbol::Style);
0531             READ_INT_VALUE("pointVisibility", pointVisibility, bool);
0532 
0533             READ_QBRUSH(d->pointBrush);
0534             READ_QPEN(d->pointPen);
0535         } else if (!preview && reader->name() == "errorBarProperties") {
0536             attribs = reader->attributes();
0537 
0538             READ_DOUBLE_VALUE("pointErrorBarSize", pointErrorBarSize);
0539             READ_QBRUSH(d->pointErrorBarBrush);
0540             READ_QPEN(d->pointErrorBarPen);
0541         } else if (reader->name() == "datapickerPoint") {
0542             auto* curvePoint = new DatapickerPoint(QString());
0543             curvePoint->setHidden(true);
0544             if (!curvePoint->load(reader, preview)) {
0545                 delete curvePoint;
0546                 return false;
0547             } else {
0548                 addChild(curvePoint);
0549                 curvePoint->initErrorBar(curveErrorTypes());
0550             }
0551         } else if (reader->name() == "spreadsheet") {
0552             Spreadsheet* datasheet = new Spreadsheet("spreadsheet", true);
0553             if (!datasheet->load(reader, preview)) {
0554                 delete datasheet;
0555                 return false;
0556             } else {
0557                 addChild(datasheet);
0558                 m_datasheet = datasheet;
0559             }
0560         } else { // unknown element
0561             reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
0562             if (!reader->skipToEndElement()) return false;
0563         }
0564     }
0565 
0566     d->retransform();
0567     return true;
0568 }