"Fossies" - the Fresh Open Source Software Archive

Member "labplot-2.8.2/src/backend/worksheet/plots/cartesian/XYCurve.cpp" (24 Feb 2021, 124184 Bytes) of package /linux/privat/labplot-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "XYCurve.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8.1_vs_2.8.2.

    1 /***************************************************************************
    2     File                 : XYCurve.cpp
    3     Project              : LabPlot
    4     Description          : A xy-curve
    5     --------------------------------------------------------------------
    6     Copyright            : (C) 2010-2020 Alexander Semke (alexander.semke@web.de)
    7     Copyright            : (C) 2013-2020 Stefan Gerlach (stefan.gerlach@uni.kn)
    8 
    9  ***************************************************************************/
   10 
   11 /***************************************************************************
   12  *                                                                         *
   13  *  This program is free software; you can redistribute it and/or modify   *
   14  *  it under the terms of the GNU General Public License as published by   *
   15  *  the Free Software Foundation; either version 2 of the License, or      *
   16  *  (at your option) any later version.                                    *
   17  *                                                                         *
   18  *  This program is distributed in the hope that it will be useful,        *
   19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
   20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
   21  *  GNU General Public License for more details.                           *
   22  *                                                                         *
   23  *   You should have received a copy of the GNU General Public License     *
   24  *   along with this program; if not, write to the Free Software           *
   25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
   26  *   Boston, MA  02110-1301  USA                                           *
   27  *                                                                         *
   28  ***************************************************************************/
   29 
   30 /*!
   31   \class XYCurve
   32   \brief A 2D-curve, provides an interface for editing many properties of the curve.
   33 
   34   \ingroup worksheet
   35 */
   36 
   37 #include "XYCurve.h"
   38 #include "XYCurvePrivate.h"
   39 #include "backend/core/column/Column.h"
   40 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h"
   41 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
   42 #include "backend/lib/commandtemplates.h"
   43 #include "backend/core/Project.h"
   44 #include "backend/spreadsheet/Spreadsheet.h"
   45 #include "backend/worksheet/Worksheet.h"
   46 #include "backend/lib/XmlStreamReader.h"
   47 #include "backend/lib/macros.h"
   48 #include "backend/lib/trace.h"
   49 #include "backend/gsl/errors.h"
   50 #include "tools/ImageTools.h"
   51 
   52 #include <QPainter>
   53 #include <QGraphicsSceneContextMenuEvent>
   54 #include <QMenu>
   55 #include <QDesktopWidget>
   56 
   57 #include <KConfig>
   58 #include <KLocalizedString>
   59 
   60 extern "C" {
   61 #include <gsl/gsl_math.h>
   62 #include <gsl/gsl_spline.h>
   63 #include <gsl/gsl_errno.h>
   64 }
   65 
   66 XYCurve::XYCurve(const QString &name, AspectType type)
   67     : WorksheetElement(name, type), d_ptr(new XYCurvePrivate(this)) {
   68 
   69     init();
   70 }
   71 
   72 XYCurve::XYCurve(const QString& name, XYCurvePrivate* dd, AspectType type)
   73     : WorksheetElement(name, type), d_ptr(dd) {
   74 
   75     init();
   76 }
   77 
   78 //no need to delete the d-pointer here - it inherits from QGraphicsItem
   79 //and is deleted during the cleanup in QGraphicsScene
   80 XYCurve::~XYCurve() = default;
   81 
   82 void XYCurve::finalizeAdd() {
   83     Q_D(XYCurve);
   84     d->plot = static_cast<const CartesianPlot*>(parentAspect());
   85     d->cSystem = static_cast<const CartesianCoordinateSystem*>(d->plot->coordinateSystem());
   86 }
   87 
   88 void XYCurve::init() {
   89     Q_D(XYCurve);
   90 
   91     KConfig config;
   92     KConfigGroup group = config.group("XYCurve");
   93 
   94     d->lineType = (LineType) group.readEntry("LineType", static_cast<int>(LineType::Line));
   95     d->lineIncreasingXOnly = group.readEntry("LineIncreasingXOnly", false);
   96     d->lineSkipGaps = group.readEntry("SkipLineGaps", false);
   97     d->lineInterpolationPointsCount = group.readEntry("LineInterpolationPointsCount", 1);
   98     d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int)Qt::SolidLine) );
   99     d->linePen.setColor( group.readEntry("LineColor", QColor(Qt::black)) );
  100     d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)) );
  101     d->lineOpacity = group.readEntry("LineOpacity", 1.0);
  102 
  103     d->dropLineType = (DropLineType) group.readEntry("DropLineType", static_cast<int>(LineType::NoLine));
  104     d->dropLinePen.setStyle( (Qt::PenStyle) group.readEntry("DropLineStyle", (int)Qt::SolidLine) );
  105     d->dropLinePen.setColor( group.readEntry("DropLineColor", QColor(Qt::black)));
  106     d->dropLinePen.setWidthF( group.readEntry("DropLineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)) );
  107     d->dropLineOpacity = group.readEntry("DropLineOpacity", 1.0);
  108 
  109     d->symbolsStyle = (Symbol::Style)group.readEntry("SymbolStyle", static_cast<int>(Symbol::Style::NoSymbols));
  110     d->symbolsSize = group.readEntry("SymbolSize", Worksheet::convertToSceneUnits(5, Worksheet::Unit::Point));
  111     d->symbolsRotationAngle = group.readEntry("SymbolRotation", 0.0);
  112     d->symbolsOpacity = group.readEntry("SymbolOpacity", 1.0);
  113     d->symbolsBrush.setStyle( (Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern) );
  114     d->symbolsBrush.setColor( group.readEntry("SymbolFillingColor", QColor(Qt::black)) );
  115     d->symbolsPen.setStyle( (Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine) );
  116     d->symbolsPen.setColor( group.readEntry("SymbolBorderColor", QColor(Qt::black)) );
  117     d->symbolsPen.setWidthF( group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Unit::Point)) );
  118 
  119     d->valuesType = (ValuesType) group.readEntry("ValuesType", static_cast<int>(ValuesType::NoValues));
  120     d->valuesPosition = (ValuesPosition) group.readEntry("ValuesPosition", static_cast<int>(ValuesPosition::Above));
  121     d->valuesDistance = group.readEntry("ValuesDistance", Worksheet::convertToSceneUnits(5, Worksheet::Unit::Point));
  122     d->valuesRotationAngle = group.readEntry("ValuesRotation", 0.0);
  123     d->valuesOpacity = group.readEntry("ValuesOpacity", 1.0);
  124     d->valuesNumericFormat = group.readEntry("ValuesNumericFormat", "f").at(0).toLatin1();
  125     d->valuesPrecision = group.readEntry("ValuesNumericFormat", 2);
  126     d->valuesDateTimeFormat = group.readEntry("ValuesDateTimeFormat", "yyyy-MM-dd");
  127     d->valuesPrefix = group.readEntry("ValuesPrefix", "");
  128     d->valuesSuffix = group.readEntry("ValuesSuffix", "");
  129     d->valuesFont = group.readEntry("ValuesFont", QFont());
  130     d->valuesFont.setPixelSize( Worksheet::convertToSceneUnits(8, Worksheet::Unit::Point) );
  131     d->valuesColor = group.readEntry("ValuesColor", QColor(Qt::black));
  132 
  133     d->fillingPosition = (FillingPosition) group.readEntry("FillingPosition", static_cast<int>(FillingPosition::NoFilling));
  134     d->fillingType = (PlotArea::BackgroundType) group.readEntry("FillingType", static_cast<int>(PlotArea::BackgroundType::Color));
  135     d->fillingColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("FillingColorStyle", static_cast<int>(PlotArea::BackgroundColorStyle::SingleColor));
  136     d->fillingImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("FillingImageStyle", static_cast<int>(PlotArea::BackgroundImageStyle::Scaled));
  137     d->fillingBrushStyle = (Qt::BrushStyle) group.readEntry("FillingBrushStyle", static_cast<int>(Qt::SolidPattern));
  138     d->fillingFileName = group.readEntry("FillingFileName", QString());
  139     d->fillingFirstColor = group.readEntry("FillingFirstColor", QColor(Qt::white));
  140     d->fillingSecondColor = group.readEntry("FillingSecondColor", QColor(Qt::black));
  141     d->fillingOpacity = group.readEntry("FillingOpacity", 1.0);
  142 
  143     d->xErrorType = (ErrorType) group.readEntry("XErrorType", static_cast<int>(ErrorType::NoError));
  144     d->yErrorType = (ErrorType) group.readEntry("YErrorType", static_cast<int>(ErrorType::NoError));
  145     d->errorBarsType = (ErrorBarsType) group.readEntry("ErrorBarsType", static_cast<int>(ErrorBarsType::Simple));
  146     d->errorBarsCapSize = group.readEntry( "ErrorBarsCapSize", Worksheet::convertToSceneUnits(10, Worksheet::Unit::Point) );
  147     d->errorBarsPen.setStyle( (Qt::PenStyle)group.readEntry("ErrorBarsStyle", (int)Qt::SolidLine) );
  148     d->errorBarsPen.setColor( group.readEntry("ErrorBarsColor", QColor(Qt::black)) );
  149     d->errorBarsPen.setWidthF( group.readEntry("ErrorBarsWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)) );
  150     d->errorBarsOpacity = group.readEntry("ErrorBarsOpacity", 1.0);
  151 }
  152 
  153 void XYCurve::initActions() {
  154     visibilityAction = new QAction(QIcon::fromTheme("view-visible"), i18n("Visible"), this);
  155     visibilityAction->setCheckable(true);
  156     connect(visibilityAction, SIGNAL(triggered(bool)), this, SLOT(visibilityChanged()));
  157 
  158     navigateToAction = new QAction(QIcon::fromTheme("go-next-view"), QString(), this);
  159     connect(navigateToAction, SIGNAL(triggered(bool)), this, SLOT(navigateTo()));
  160 
  161     m_menusInitialized = true;
  162 }
  163 
  164 QMenu* XYCurve::createContextMenu() {
  165     if (!m_menusInitialized)
  166         initActions();
  167 
  168     QMenu* menu = WorksheetElement::createContextMenu();
  169     QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action"
  170     visibilityAction->setChecked(isVisible());
  171     menu->insertAction(firstAction, visibilityAction);
  172 
  173     //"data analysis" menu
  174     auto* plot = static_cast<CartesianPlot*>(parentAspect());
  175     menu->insertMenu(visibilityAction, plot->analysisMenu());
  176     menu->insertSeparator(visibilityAction);
  177 
  178     //"Navigate to spreadsheet"-action, show only if x- or y-columns have data from a spreadsheet
  179     AbstractAspect* parentSpreadsheet = nullptr;
  180     if (xColumn() && dynamic_cast<Spreadsheet*>(xColumn()->parentAspect()) )
  181         parentSpreadsheet = xColumn()->parentAspect();
  182     else if (yColumn() && dynamic_cast<Spreadsheet*>(yColumn()->parentAspect()) )
  183         parentSpreadsheet = yColumn()->parentAspect();
  184 
  185     if (parentSpreadsheet) {
  186         navigateToAction->setText(i18n("Navigate to \"%1\"", parentSpreadsheet->name()));
  187         navigateToAction->setData(parentSpreadsheet->path());
  188         menu->insertAction(visibilityAction, navigateToAction);
  189         menu->insertSeparator(visibilityAction);
  190     }
  191 
  192     //if the context menu is called on an item that is not selected yet, select it
  193     if (!graphicsItem()->isSelected())
  194         graphicsItem()->setSelected(true);
  195 
  196     return menu;
  197 }
  198 
  199 /*!
  200     Returns an icon to be used in the project explorer.
  201 */
  202 QIcon XYCurve::icon() const {
  203     return QIcon::fromTheme("labplot-xy-curve");
  204 }
  205 
  206 QGraphicsItem* XYCurve::graphicsItem() const {
  207     return d_ptr;
  208 }
  209 
  210 STD_SWAP_METHOD_SETTER_CMD_IMPL(XYCurve, SetVisible, bool, swapVisible)
  211 void XYCurve::setVisible(bool on) {
  212     Q_D(XYCurve);
  213     exec(new XYCurveSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
  214 }
  215 
  216 bool XYCurve::isVisible() const {
  217     Q_D(const XYCurve);
  218     return d->isVisible();
  219 }
  220 
  221 void XYCurve::setPrinting(bool on) {
  222     Q_D(XYCurve);
  223     d->setPrinting(on);
  224 }
  225 
  226 /*!
  227  * \brief XYCurve::activateCurve
  228  * Checks if the mousepos distance to the curve is less than @p maxDist
  229  * \p mouseScenePos
  230  * \p maxDist Maximum distance the point lies away from the curve
  231  * \return Returns true if the distance is smaller than maxDist.
  232  */
  233 bool XYCurve::activateCurve(QPointF mouseScenePos, double maxDist) {
  234     Q_D(XYCurve);
  235     return d->activateCurve(mouseScenePos, maxDist);
  236 }
  237 
  238 /*!
  239  * \brief XYCurve::setHover
  240  * Will be called in CartesianPlot::hoverMoveEvent()
  241  * See d->setHover(on) for more documentation
  242  * \p on
  243  */
  244 void XYCurve::setHover(bool on) {
  245     Q_D(XYCurve);
  246     d->setHover(on);
  247 }
  248 
  249 //##############################################################################
  250 //##########################  getter methods  ##################################
  251 //##############################################################################
  252 
  253 //data source
  254 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xColumn, xColumn)
  255 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yColumn, yColumn)
  256 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xColumnPath, xColumnPath)
  257 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yColumnPath, yColumnPath)
  258 
  259 //line
  260 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::LineType, lineType, lineType)
  261 BASIC_SHARED_D_READER_IMPL(XYCurve, bool, lineSkipGaps, lineSkipGaps)
  262 BASIC_SHARED_D_READER_IMPL(XYCurve, bool, lineIncreasingXOnly, lineIncreasingXOnly)
  263 BASIC_SHARED_D_READER_IMPL(XYCurve, int, lineInterpolationPointsCount, lineInterpolationPointsCount)
  264 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, linePen, linePen)
  265 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, lineOpacity, lineOpacity)
  266 
  267 //droplines
  268 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::DropLineType, dropLineType, dropLineType)
  269 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, dropLinePen, dropLinePen)
  270 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, dropLineOpacity, dropLineOpacity)
  271 
  272 //symbols
  273 BASIC_SHARED_D_READER_IMPL(XYCurve, Symbol::Style, symbolsStyle, symbolsStyle)
  274 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsOpacity, symbolsOpacity)
  275 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsRotationAngle, symbolsRotationAngle)
  276 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsSize, symbolsSize)
  277 CLASS_SHARED_D_READER_IMPL(XYCurve, QBrush, symbolsBrush, symbolsBrush)
  278 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, symbolsPen, symbolsPen)
  279 
  280 //values
  281 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ValuesType, valuesType, valuesType)
  282 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn *, valuesColumn, valuesColumn)
  283 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesColumnPath, valuesColumnPath)
  284 
  285 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ValuesPosition, valuesPosition, valuesPosition)
  286 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesDistance, valuesDistance)
  287 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesRotationAngle, valuesRotationAngle)
  288 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesOpacity, valuesOpacity)
  289 CLASS_SHARED_D_READER_IMPL(XYCurve, char, valuesNumericFormat, valuesNumericFormat)
  290 BASIC_SHARED_D_READER_IMPL(XYCurve, int, valuesPrecision, valuesPrecision)
  291 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesDateTimeFormat, valuesDateTimeFormat)
  292 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesPrefix, valuesPrefix)
  293 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesSuffix, valuesSuffix)
  294 CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, valuesColor, valuesColor)
  295 CLASS_SHARED_D_READER_IMPL(XYCurve, QFont, valuesFont, valuesFont)
  296 
  297 //filling
  298 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::FillingPosition, fillingPosition, fillingPosition)
  299 BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundType, fillingType, fillingType)
  300 BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundColorStyle, fillingColorStyle, fillingColorStyle)
  301 BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundImageStyle, fillingImageStyle, fillingImageStyle)
  302 CLASS_SHARED_D_READER_IMPL(XYCurve, Qt::BrushStyle, fillingBrushStyle, fillingBrushStyle)
  303 CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, fillingFirstColor, fillingFirstColor)
  304 CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, fillingSecondColor, fillingSecondColor)
  305 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, fillingFileName, fillingFileName)
  306 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, fillingOpacity, fillingOpacity)
  307 
  308 //error bars
  309 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorType, xErrorType, xErrorType)
  310 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xErrorPlusColumn, xErrorPlusColumn)
  311 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xErrorMinusColumn, xErrorMinusColumn)
  312 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorType, yErrorType, yErrorType)
  313 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yErrorPlusColumn, yErrorPlusColumn)
  314 BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yErrorMinusColumn, yErrorMinusColumn)
  315 
  316 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xErrorPlusColumnPath, xErrorPlusColumnPath)
  317 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xErrorMinusColumnPath, xErrorMinusColumnPath)
  318 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yErrorPlusColumnPath, yErrorPlusColumnPath)
  319 CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yErrorMinusColumnPath, yErrorMinusColumnPath)
  320 
  321 BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorBarsType, errorBarsType, errorBarsType)
  322 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, errorBarsCapSize, errorBarsCapSize)
  323 CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, errorBarsPen, errorBarsPen)
  324 BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, errorBarsOpacity, errorBarsOpacity)
  325 
  326 /*!
  327  * return \c true if the data in the source columns (x, y) used in the analysis curves, \c false otherwise
  328  */
  329 bool XYCurve::isSourceDataChangedSinceLastRecalc() const {
  330     Q_D(const XYCurve);
  331     return d->sourceDataChangedSinceLastRecalc;
  332 }
  333 
  334 //##############################################################################
  335 //#################  setter methods and undo commands ##########################
  336 //##############################################################################
  337 
  338 // 1) add XYCurveSetXColumnCmd as friend class to XYCurve
  339 // 2) add XYCURVE_COLUMN_CONNECT(x) as private method to XYCurve
  340 // 3) define all missing slots
  341 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(X, x, recalcLogicalPoints)
  342 void XYCurve::setXColumn(const AbstractColumn* column) {
  343     Q_D(XYCurve);
  344     if (column != d->xColumn)
  345         exec(new XYCurveSetXColumnCmd(d, column, ki18n("%1: x-data source changed")));
  346 }
  347 
  348 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(Y, y, recalcLogicalPoints)
  349 void XYCurve::setYColumn(const AbstractColumn* column) {
  350     Q_D(XYCurve);
  351     if (column != d->yColumn)
  352         exec(new XYCurveSetYColumnCmd(d, column, ki18n("%1: y-data source changed")));
  353 }
  354 
  355 void XYCurve::setXColumnPath(const QString& path) {
  356     Q_D(XYCurve);
  357     d->xColumnPath = path;
  358 }
  359 
  360 void XYCurve::setYColumnPath(const QString& path) {
  361     Q_D(XYCurve);
  362     d->yColumnPath = path;
  363 }
  364 
  365 //Line
  366 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineType, XYCurve::LineType, lineType, updateLines)
  367 void XYCurve::setLineType(LineType type) {
  368     Q_D(XYCurve);
  369     if (type != d->lineType)
  370         exec(new XYCurveSetLineTypeCmd(d, type, ki18n("%1: line type changed")));
  371 }
  372 
  373 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineSkipGaps, bool, lineSkipGaps, updateLines)
  374 void XYCurve::setLineSkipGaps(bool skip) {
  375     Q_D(XYCurve);
  376     if (skip != d->lineSkipGaps)
  377         exec(new XYCurveSetLineSkipGapsCmd(d, skip, ki18n("%1: set skip line gaps")));
  378 }
  379 
  380 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineIncreasingXOnly, bool, lineIncreasingXOnly, updateLines)
  381 void XYCurve::setLineIncreasingXOnly(bool incr) {
  382     Q_D(XYCurve);
  383     if (incr != d->lineIncreasingXOnly)
  384         exec(new XYCurveSetLineIncreasingXOnlyCmd(d, incr, ki18n("%1: set increasing X")));
  385 }
  386 
  387 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineInterpolationPointsCount, int, lineInterpolationPointsCount, updateLines)
  388 void XYCurve::setLineInterpolationPointsCount(int count) {
  389     Q_D(XYCurve);
  390     if (count != d->lineInterpolationPointsCount)
  391         exec(new XYCurveSetLineInterpolationPointsCountCmd(d, count, ki18n("%1: set the number of interpolation points")));
  392 }
  393 
  394 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect)
  395 void XYCurve::setLinePen(const QPen &pen) {
  396     Q_D(XYCurve);
  397     if (pen != d->linePen)
  398         exec(new XYCurveSetLinePenCmd(d, pen, ki18n("%1: set line style")));
  399 }
  400 
  401 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineOpacity, qreal, lineOpacity, updatePixmap);
  402 void XYCurve::setLineOpacity(qreal opacity) {
  403     Q_D(XYCurve);
  404     if (opacity != d->lineOpacity)
  405         exec(new XYCurveSetLineOpacityCmd(d, opacity, ki18n("%1: set line opacity")));
  406 }
  407 
  408 //Drop lines
  409 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLineType, XYCurve::DropLineType, dropLineType, updateDropLines)
  410 void XYCurve::setDropLineType(DropLineType type) {
  411     Q_D(XYCurve);
  412     if (type != d->dropLineType)
  413         exec(new XYCurveSetDropLineTypeCmd(d, type, ki18n("%1: drop line type changed")));
  414 }
  415 
  416 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLinePen, QPen, dropLinePen, recalcShapeAndBoundingRect)
  417 void XYCurve::setDropLinePen(const QPen &pen) {
  418     Q_D(XYCurve);
  419     if (pen != d->dropLinePen)
  420         exec(new XYCurveSetDropLinePenCmd(d, pen, ki18n("%1: set drop line style")));
  421 }
  422 
  423 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLineOpacity, qreal, dropLineOpacity, updatePixmap)
  424 void XYCurve::setDropLineOpacity(qreal opacity) {
  425     Q_D(XYCurve);
  426     if (opacity != d->dropLineOpacity)
  427         exec(new XYCurveSetDropLineOpacityCmd(d, opacity, ki18n("%1: set drop line opacity")));
  428 }
  429 
  430 // Symbols-Tab
  431 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsStyle, Symbol::Style, symbolsStyle, retransform)
  432 void XYCurve::setSymbolsStyle(Symbol::Style style) {
  433     Q_D(XYCurve);
  434     if (style != d->symbolsStyle)
  435         exec(new XYCurveSetSymbolsStyleCmd(d, style, ki18n("%1: set symbol style")));
  436 }
  437 
  438 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsSize, qreal, symbolsSize, updateSymbols)
  439 void XYCurve::setSymbolsSize(qreal size) {
  440     Q_D(XYCurve);
  441     if (!qFuzzyCompare(1 + size, 1 + d->symbolsSize))
  442         exec(new XYCurveSetSymbolsSizeCmd(d, size, ki18n("%1: set symbol size")));
  443 }
  444 
  445 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsRotationAngle, qreal, symbolsRotationAngle, updateSymbols)
  446 void XYCurve::setSymbolsRotationAngle(qreal angle) {
  447     Q_D(XYCurve);
  448     if (!qFuzzyCompare(1 + angle, 1 + d->symbolsRotationAngle))
  449         exec(new XYCurveSetSymbolsRotationAngleCmd(d, angle, ki18n("%1: rotate symbols")));
  450 }
  451 
  452 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsBrush, QBrush, symbolsBrush, updatePixmap)
  453 void XYCurve::setSymbolsBrush(const QBrush &brush) {
  454     Q_D(XYCurve);
  455     if (brush != d->symbolsBrush)
  456         exec(new XYCurveSetSymbolsBrushCmd(d, brush, ki18n("%1: set symbol filling")));
  457 }
  458 
  459 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsPen, QPen, symbolsPen, updateSymbols)
  460 void XYCurve::setSymbolsPen(const QPen &pen) {
  461     Q_D(XYCurve);
  462     if (pen != d->symbolsPen)
  463         exec(new XYCurveSetSymbolsPenCmd(d, pen, ki18n("%1: set symbol outline style")));
  464 }
  465 
  466 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsOpacity, qreal, symbolsOpacity, updatePixmap)
  467 void XYCurve::setSymbolsOpacity(qreal opacity) {
  468     Q_D(XYCurve);
  469     if (opacity != d->symbolsOpacity)
  470         exec(new XYCurveSetSymbolsOpacityCmd(d, opacity, ki18n("%1: set symbols opacity")));
  471 }
  472 
  473 //Values-Tab
  474 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesType, XYCurve::ValuesType, valuesType, updateValues)
  475 void XYCurve::setValuesType(XYCurve::ValuesType type) {
  476     Q_D(XYCurve);
  477     if (type != d->valuesType)
  478         exec(new XYCurveSetValuesTypeCmd(d, type, ki18n("%1: set values type")));
  479 }
  480 
  481 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(Values, values, updateValues)
  482 void XYCurve::setValuesColumn(const AbstractColumn* column) {
  483     Q_D(XYCurve);
  484     if (column != d->valuesColumn) {
  485         exec(new XYCurveSetValuesColumnCmd(d, column, ki18n("%1: set values column")));
  486         if (column)
  487             connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateValues()));
  488     }
  489 }
  490 
  491 void XYCurve::setValuesColumnPath(const QString& path) {
  492     Q_D(XYCurve);
  493     d->valuesColumnPath = path;
  494 }
  495 
  496 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPosition, XYCurve::ValuesPosition, valuesPosition, updateValues)
  497 void XYCurve::setValuesPosition(ValuesPosition position) {
  498     Q_D(XYCurve);
  499     if (position != d->valuesPosition)
  500         exec(new XYCurveSetValuesPositionCmd(d, position, ki18n("%1: set values position")));
  501 }
  502 
  503 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesDistance, qreal, valuesDistance, updateValues)
  504 void XYCurve::setValuesDistance(qreal distance) {
  505     Q_D(XYCurve);
  506     if (distance != d->valuesDistance)
  507         exec(new XYCurveSetValuesDistanceCmd(d, distance, ki18n("%1: set values distance")));
  508 }
  509 
  510 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesRotationAngle, qreal, valuesRotationAngle, updateValues)
  511 void XYCurve::setValuesRotationAngle(qreal angle) {
  512     Q_D(XYCurve);
  513     if (!qFuzzyCompare(1 + angle, 1 + d->valuesRotationAngle))
  514         exec(new XYCurveSetValuesRotationAngleCmd(d, angle, ki18n("%1: rotate values")));
  515 }
  516 
  517 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesOpacity, qreal, valuesOpacity, updatePixmap)
  518 void XYCurve::setValuesOpacity(qreal opacity) {
  519     Q_D(XYCurve);
  520     if (opacity != d->valuesOpacity)
  521         exec(new XYCurveSetValuesOpacityCmd(d, opacity, ki18n("%1: set values opacity")));
  522 }
  523 
  524 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesNumericFormat, char, valuesNumericFormat, updateValues)
  525 void XYCurve::setValuesNumericFormat(char format) {
  526     Q_D(XYCurve);
  527     if (format != d->valuesNumericFormat)
  528         exec(new XYCurveSetValuesNumericFormatCmd(d, format, ki18n("%1: set values numeric format")));
  529 }
  530 
  531 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPrecision, int, valuesPrecision, updateValues)
  532 void XYCurve::setValuesPrecision(int precision) {
  533     Q_D(XYCurve);
  534     if (precision != d->valuesPrecision)
  535         exec(new XYCurveSetValuesPrecisionCmd(d, precision, ki18n("%1: set values precision")));
  536 }
  537 
  538 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesDateTimeFormat, QString, valuesDateTimeFormat, updateValues)
  539 void XYCurve::setValuesDateTimeFormat(const QString& format) {
  540     Q_D(XYCurve);
  541     if (format != d->valuesDateTimeFormat)
  542         exec(new XYCurveSetValuesDateTimeFormatCmd(d, format, ki18n("%1: set values datetime format")));
  543 }
  544 
  545 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPrefix, QString, valuesPrefix, updateValues)
  546 void XYCurve::setValuesPrefix(const QString& prefix) {
  547     Q_D(XYCurve);
  548     if (prefix != d->valuesPrefix)
  549         exec(new XYCurveSetValuesPrefixCmd(d, prefix, ki18n("%1: set values prefix")));
  550 }
  551 
  552 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesSuffix, QString, valuesSuffix, updateValues)
  553 void XYCurve::setValuesSuffix(const QString& suffix) {
  554     Q_D(XYCurve);
  555     if (suffix != d->valuesSuffix)
  556         exec(new XYCurveSetValuesSuffixCmd(d, suffix, ki18n("%1: set values suffix")));
  557 }
  558 
  559 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesFont, QFont, valuesFont, updateValues)
  560 void XYCurve::setValuesFont(const QFont& font) {
  561     Q_D(XYCurve);
  562     if (font != d->valuesFont)
  563         exec(new XYCurveSetValuesFontCmd(d, font, ki18n("%1: set values font")));
  564 }
  565 
  566 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesColor, QColor, valuesColor, updatePixmap)
  567 void XYCurve::setValuesColor(const QColor& color) {
  568     Q_D(XYCurve);
  569     if (color != d->valuesColor)
  570         exec(new XYCurveSetValuesColorCmd(d, color, ki18n("%1: set values color")));
  571 }
  572 
  573 //Filling
  574 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingPosition, XYCurve::FillingPosition, fillingPosition, updateFilling)
  575 void XYCurve::setFillingPosition(FillingPosition position) {
  576     Q_D(XYCurve);
  577     if (position != d->fillingPosition)
  578         exec(new XYCurveSetFillingPositionCmd(d, position, ki18n("%1: filling position changed")));
  579 }
  580 
  581 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingType, PlotArea::BackgroundType, fillingType, updatePixmap)
  582 void XYCurve::setFillingType(PlotArea::BackgroundType type) {
  583     Q_D(XYCurve);
  584     if (type != d->fillingType)
  585         exec(new XYCurveSetFillingTypeCmd(d, type, ki18n("%1: filling type changed")));
  586 }
  587 
  588 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingColorStyle, PlotArea::BackgroundColorStyle, fillingColorStyle, updatePixmap)
  589 void XYCurve::setFillingColorStyle(PlotArea::BackgroundColorStyle style) {
  590     Q_D(XYCurve);
  591     if (style != d->fillingColorStyle)
  592         exec(new XYCurveSetFillingColorStyleCmd(d, style, ki18n("%1: filling color style changed")));
  593 }
  594 
  595 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingImageStyle, PlotArea::BackgroundImageStyle, fillingImageStyle, updatePixmap)
  596 void XYCurve::setFillingImageStyle(PlotArea::BackgroundImageStyle style) {
  597     Q_D(XYCurve);
  598     if (style != d->fillingImageStyle)
  599         exec(new XYCurveSetFillingImageStyleCmd(d, style, ki18n("%1: filling image style changed")));
  600 }
  601 
  602 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingBrushStyle, Qt::BrushStyle, fillingBrushStyle, updatePixmap)
  603 void XYCurve::setFillingBrushStyle(Qt::BrushStyle style) {
  604     Q_D(XYCurve);
  605     if (style != d->fillingBrushStyle)
  606         exec(new XYCurveSetFillingBrushStyleCmd(d, style, ki18n("%1: filling brush style changed")));
  607 }
  608 
  609 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingFirstColor, QColor, fillingFirstColor, updatePixmap)
  610 void XYCurve::setFillingFirstColor(const QColor& color) {
  611     Q_D(XYCurve);
  612     if (color != d->fillingFirstColor)
  613         exec(new XYCurveSetFillingFirstColorCmd(d, color, ki18n("%1: set filling first color")));
  614 }
  615 
  616 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingSecondColor, QColor, fillingSecondColor, updatePixmap)
  617 void XYCurve::setFillingSecondColor(const QColor& color) {
  618     Q_D(XYCurve);
  619     if (color != d->fillingSecondColor)
  620         exec(new XYCurveSetFillingSecondColorCmd(d, color, ki18n("%1: set filling second color")));
  621 }
  622 
  623 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingFileName, QString, fillingFileName, updatePixmap)
  624 void XYCurve::setFillingFileName(const QString& fileName) {
  625     Q_D(XYCurve);
  626     if (fileName != d->fillingFileName)
  627         exec(new XYCurveSetFillingFileNameCmd(d, fileName, ki18n("%1: set filling image")));
  628 }
  629 
  630 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingOpacity, qreal, fillingOpacity, updatePixmap)
  631 void XYCurve::setFillingOpacity(qreal opacity) {
  632     Q_D(XYCurve);
  633     if (opacity != d->fillingOpacity)
  634         exec(new XYCurveSetFillingOpacityCmd(d, opacity, ki18n("%1: set filling opacity")));
  635 }
  636 
  637 //Error bars
  638 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetXErrorType, XYCurve::ErrorType, xErrorType, updateErrorBars)
  639 void XYCurve::setXErrorType(ErrorType type) {
  640     Q_D(XYCurve);
  641     if (type != d->xErrorType)
  642         exec(new XYCurveSetXErrorTypeCmd(d, type, ki18n("%1: x-error type changed")));
  643 }
  644 
  645 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(XErrorPlus, xErrorPlus, updateErrorBars)
  646 void XYCurve::setXErrorPlusColumn(const AbstractColumn* column) {
  647     Q_D(XYCurve);
  648     if (column != d->xErrorPlusColumn) {
  649         exec(new XYCurveSetXErrorPlusColumnCmd(d, column, ki18n("%1: set x-error column")));
  650         if (column) {
  651             connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
  652             //in the macro we connect to recalcLogicalPoints which is not needed for error columns
  653             disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
  654         }
  655     }
  656 }
  657 
  658 void XYCurve::setXErrorPlusColumnPath(const QString& path) {
  659     Q_D(XYCurve);
  660     d->xErrorPlusColumnPath = path;
  661 }
  662 
  663 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(XErrorMinus, xErrorMinus, updateErrorBars)
  664 void XYCurve::setXErrorMinusColumn(const AbstractColumn* column) {
  665     Q_D(XYCurve);
  666     if (column != d->xErrorMinusColumn) {
  667         exec(new XYCurveSetXErrorMinusColumnCmd(d, column, ki18n("%1: set x-error column")));
  668         if (column) {
  669             connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
  670             //in the macro we connect to recalcLogicalPoints which is not needed for error columns
  671             disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
  672         }
  673     }
  674 }
  675 
  676 void XYCurve::setXErrorMinusColumnPath(const QString& path) {
  677     Q_D(XYCurve);
  678     d->xErrorMinusColumnPath = path;
  679 }
  680 
  681 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetYErrorType, XYCurve::ErrorType, yErrorType, updateErrorBars)
  682 void XYCurve::setYErrorType(ErrorType type) {
  683     Q_D(XYCurve);
  684     if (type != d->yErrorType)
  685         exec(new XYCurveSetYErrorTypeCmd(d, type, ki18n("%1: y-error type changed")));
  686 }
  687 
  688 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(YErrorPlus, yErrorPlus, updateErrorBars)
  689 void XYCurve::setYErrorPlusColumn(const AbstractColumn* column) {
  690     Q_D(XYCurve);
  691     if (column != d->yErrorPlusColumn) {
  692         exec(new XYCurveSetYErrorPlusColumnCmd(d, column, ki18n("%1: set y-error column")));
  693         if (column) {
  694             connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
  695             //in the macro we connect to recalcLogicalPoints which is not needed for error columns
  696             disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
  697         }
  698     }
  699 }
  700 
  701 void XYCurve::setYErrorPlusColumnPath(const QString& path) {
  702     Q_D(XYCurve);
  703     d->yErrorPlusColumnPath = path;
  704 }
  705 
  706 XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(YErrorMinus, yErrorMinus, updateErrorBars)
  707 void XYCurve::setYErrorMinusColumn(const AbstractColumn* column) {
  708     Q_D(XYCurve);
  709     if (column != d->yErrorMinusColumn) {
  710         exec(new XYCurveSetYErrorMinusColumnCmd(d, column, ki18n("%1: set y-error column")));
  711         if (column) {
  712             connect(column, &AbstractColumn::dataChanged, this, &XYCurve::updateErrorBars);
  713             //in the macro we connect to recalcLogicalPoints which is not needed for error columns
  714             disconnect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints);
  715         }
  716     }
  717 }
  718 
  719 void XYCurve::setYErrorMinusColumnPath(const QString& path) {
  720     Q_D(XYCurve);
  721     d->yErrorMinusColumnPath = path;
  722 }
  723 
  724 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsCapSize, qreal, errorBarsCapSize, updateErrorBars)
  725 void XYCurve::setErrorBarsCapSize(qreal size) {
  726     Q_D(XYCurve);
  727     if (size != d->errorBarsCapSize)
  728         exec(new XYCurveSetErrorBarsCapSizeCmd(d, size, ki18n("%1: set error bar cap size")));
  729 }
  730 
  731 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsType, XYCurve::ErrorBarsType, errorBarsType, updateErrorBars)
  732 void XYCurve::setErrorBarsType(ErrorBarsType type) {
  733     Q_D(XYCurve);
  734     if (type != d->errorBarsType)
  735         exec(new XYCurveSetErrorBarsTypeCmd(d, type, ki18n("%1: error bar type changed")));
  736 }
  737 
  738 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsPen, QPen, errorBarsPen, recalcShapeAndBoundingRect)
  739 void XYCurve::setErrorBarsPen(const QPen& pen) {
  740     Q_D(XYCurve);
  741     if (pen != d->errorBarsPen)
  742         exec(new XYCurveSetErrorBarsPenCmd(d, pen, ki18n("%1: set error bar style")));
  743 }
  744 
  745 STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsOpacity, qreal, errorBarsOpacity, updatePixmap)
  746 void XYCurve::setErrorBarsOpacity(qreal opacity) {
  747     Q_D(XYCurve);
  748     if (opacity != d->errorBarsOpacity)
  749         exec(new XYCurveSetErrorBarsOpacityCmd(d, opacity, ki18n("%1: set error bar opacity")));
  750 }
  751 
  752 void XYCurve::suppressRetransform(bool b) {
  753     Q_D(XYCurve);
  754     d->suppressRetransform(b);
  755 }
  756 
  757 //##############################################################################
  758 //#################################  SLOTS  ####################################
  759 //##############################################################################
  760 void XYCurve::retransform() {
  761     Q_D(XYCurve);
  762     d->retransform();
  763 }
  764 
  765 void XYCurve::recalcLogicalPoints() {
  766     Q_D(XYCurve);
  767     d->recalcLogicalPoints();
  768 }
  769 
  770 void XYCurve::updateValues() {
  771     Q_D(XYCurve);
  772     d->updateValues();
  773 }
  774 
  775 void XYCurve::updateErrorBars() {
  776     Q_D(XYCurve);
  777     d->updateErrorBars();
  778 }
  779 
  780 //TODO
  781 void XYCurve::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
  782     Q_UNUSED(pageResize);
  783     Q_D(const XYCurve);
  784 
  785     setSymbolsSize(d->symbolsSize * horizontalRatio);
  786 
  787     QPen pen = d->symbolsPen;
  788     pen.setWidthF(pen.widthF() * (horizontalRatio + verticalRatio) / 2.);
  789     setSymbolsPen(pen);
  790 
  791     pen = d->linePen;
  792     pen.setWidthF(pen.widthF() * (horizontalRatio + verticalRatio) / 2.);
  793     setLinePen(pen);
  794 
  795     //setValuesDistance(d->distance*);
  796     QFont font = d->valuesFont;
  797     font.setPointSizeF(font.pointSizeF()*horizontalRatio);
  798     setValuesFont(font);
  799 }
  800 
  801 void XYCurve::xColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  802     Q_D(XYCurve);
  803     if (aspect == d->xColumn) {
  804         disconnect(aspect, nullptr, this, nullptr);
  805         d->xColumn = nullptr;
  806         d->retransform();
  807     }
  808 }
  809 
  810 void XYCurve::yColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  811     Q_D(XYCurve);
  812     if (aspect == d->yColumn) {
  813         disconnect(aspect, nullptr, this, nullptr);
  814         d->yColumn = nullptr;
  815         d->retransform();
  816     }
  817 }
  818 
  819 void XYCurve::valuesColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  820     Q_D(XYCurve);
  821     if (aspect == d->valuesColumn) {
  822         disconnect(aspect, nullptr, this, nullptr);
  823         d->valuesColumn = nullptr;
  824         d->updateValues();
  825     }
  826 }
  827 
  828 void XYCurve::xErrorPlusColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  829     Q_D(XYCurve);
  830     if (aspect == d->xErrorPlusColumn) {
  831         disconnect(aspect, nullptr, this, nullptr);
  832         d->xErrorPlusColumn = nullptr;
  833         d->updateErrorBars();
  834     }
  835 }
  836 
  837 void XYCurve::xErrorMinusColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  838     Q_D(XYCurve);
  839     if (aspect == d->xErrorMinusColumn) {
  840         disconnect(aspect, nullptr, this, nullptr);
  841         d->xErrorMinusColumn = nullptr;
  842         d->updateErrorBars();
  843     }
  844 }
  845 
  846 void XYCurve::yErrorPlusColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  847     Q_D(XYCurve);
  848     if (aspect == d->yErrorPlusColumn) {
  849         disconnect(aspect, nullptr, this, nullptr);
  850         d->yErrorPlusColumn = nullptr;
  851         d->updateErrorBars();
  852     }
  853 }
  854 
  855 void XYCurve::yErrorMinusColumnAboutToBeRemoved(const AbstractAspect* aspect) {
  856     Q_D(XYCurve);
  857     if (aspect == d->yErrorMinusColumn) {
  858         disconnect(aspect, nullptr, this, nullptr);
  859         d->yErrorMinusColumn = nullptr;
  860         d->updateErrorBars();
  861     }
  862 }
  863 
  864 void XYCurve::xColumnNameChanged() {
  865     Q_D(XYCurve);
  866     setXColumnPath(d->xColumn->path());
  867 }
  868 
  869 void XYCurve::yColumnNameChanged() {
  870     Q_D(XYCurve);
  871     setYColumnPath(d->yColumn->path());
  872 }
  873 
  874 void XYCurve::xErrorPlusColumnNameChanged() {
  875     Q_D(XYCurve);
  876     setXErrorPlusColumnPath(d->xErrorPlusColumn->path());
  877 }
  878 
  879 void XYCurve::xErrorMinusColumnNameChanged() {
  880     Q_D(XYCurve);
  881     setXErrorMinusColumnPath(d->xErrorMinusColumn->path());
  882 }
  883 
  884 void XYCurve::yErrorPlusColumnNameChanged() {
  885     Q_D(XYCurve);
  886     setYErrorPlusColumnPath(d->yErrorPlusColumn->path());
  887 }
  888 
  889 void XYCurve::yErrorMinusColumnNameChanged() {
  890     Q_D(XYCurve);
  891     setYErrorMinusColumnPath(d->yErrorMinusColumn->path());
  892 }
  893 
  894 void XYCurve::valuesColumnNameChanged() {
  895     Q_D(XYCurve);
  896     setValuesColumnPath(d->valuesColumn->path());
  897 }
  898 
  899 //##############################################################################
  900 //######  SLOTs for changes triggered via QActions in the context menu  ########
  901 //##############################################################################
  902 void XYCurve::visibilityChanged() {
  903     Q_D(const XYCurve);
  904     this->setVisible(!d->isVisible());
  905 }
  906 
  907 void XYCurve::navigateTo() {
  908     project()->navigateTo(navigateToAction->data().toString());
  909 }
  910 
  911 //##############################################################################
  912 //######################### Private implementation #############################
  913 //##############################################################################
  914 XYCurvePrivate::XYCurvePrivate(XYCurve *owner) : q(owner) {
  915     setFlag(QGraphicsItem::ItemIsSelectable, true);
  916     setAcceptHoverEvents(false);
  917 }
  918 
  919 QString XYCurvePrivate::name() const {
  920     return q->name();
  921 }
  922 
  923 QRectF XYCurvePrivate::boundingRect() const {
  924     return boundingRectangle;
  925 }
  926 
  927 /*!
  928   Returns the shape of the XYCurve as a QPainterPath in local coordinates
  929 */
  930 QPainterPath XYCurvePrivate::shape() const {
  931     return curveShape;
  932 }
  933 
  934 void XYCurvePrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
  935     if (q->activateCurve(event->pos())) {
  936         q->createContextMenu()->exec(event->screenPos());
  937         return;
  938     }
  939     QGraphicsItem::contextMenuEvent(event);
  940 }
  941 
  942 bool XYCurvePrivate::swapVisible(bool on) {
  943     bool oldValue = isVisible();
  944 
  945     //When making a graphics item invisible, it gets deselected in the scene.
  946     //In this case we don't want to deselect the item in the project explorer.
  947     //We need to supress the deselection in the view.
  948     auto* worksheet = static_cast<Worksheet*>(q->parent(AspectType::Worksheet));
  949     worksheet->suppressSelectionChangedEvent(true);
  950     setVisible(on);
  951     worksheet->suppressSelectionChangedEvent(false);
  952 
  953     emit q->visibilityChanged(on);
  954     retransform();
  955     return oldValue;
  956 }
  957 
  958 /*!
  959   called when the size of the plot or its data ranges (manual changes, zooming, etc.) were changed.
  960   recalculates the position of the scene points to be drawn.
  961   triggers the update of lines, drop lines, symbols etc.
  962 */
  963 void XYCurvePrivate::retransform() {
  964     if (!isVisible())
  965         return;
  966 
  967     DEBUG("\n" << Q_FUNC_INFO << ", name = " << STDSTRING(name()) << ", m_suppressRetransform = " << m_suppressRetransform);
  968     if (m_suppressRetransform || !plot)
  969         return;
  970 
  971     {
  972 #ifdef PERFTRACE_CURVES
  973     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform()");
  974 #endif
  975 
  976     m_scenePoints.clear();
  977 
  978     if (!xColumn || !yColumn) {
  979         DEBUG(Q_FUNC_INFO << ", xColumn or yColumn not available");
  980         linePath = QPainterPath();
  981         dropLinePath = QPainterPath();
  982         symbolsPath = QPainterPath();
  983         valuesPath = QPainterPath();
  984         errorBarsPath = QPainterPath();
  985         curveShape = QPainterPath();
  986         m_lines.clear();
  987         m_valuePoints.clear();
  988         m_valueStrings.clear();
  989         m_fillPolygons.clear();
  990         recalcShapeAndBoundingRect();
  991         return;
  992     }
  993 
  994     if (!plot->isPanningActive())
  995         WAIT_CURSOR;
  996 
  997     //calculate the scene coordinates
  998     // This condition cannot be used, because m_logicalPoints is also used in updateErrorBars(), updateDropLines() and in updateFilling()
  999     // TODO: check updateErrorBars() and updateDropLines() and if they aren't available don't calculate this part
 1000     //if (symbolsStyle != Symbol::Style::NoSymbols || valuesType != XYCurve::NoValues ) {
 1001     {
 1002 #ifdef PERFTRACE_CURVES
 1003     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform(), map logical points to scene coordinates");
 1004 #endif
 1005 
 1006     const int numberOfPoints = m_logicalPoints.size();
 1007     DEBUG(Q_FUNC_INFO << ", number of logical points = " << numberOfPoints)
 1008     if (numberOfPoints > 0) {
 1009         // this is the old method considering DPI
 1010         DEBUG(Q_FUNC_INFO << ", plot->dataRect() width/height = " << plot->dataRect().width() << '/'  << plot->dataRect().height());
 1011         //const double widthDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().width(), Worksheet::Unit::Inch);
 1012         //const double heightDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().height(), Worksheet::Unit::Inch);
 1013         //DEBUG(Q_FUNC_INFO << ", widthDatarectInch/heightDatarectInch = " << widthDatarectInch << '/' << heightDatarectInch)
 1014         DEBUG(Q_FUNC_INFO << ", logical DPI X/Y = " << QApplication::desktop()->logicalDpiX() << '/' << QApplication::desktop()->logicalDpiY())
 1015         DEBUG(Q_FUNC_INFO << ", physical DPI X/Y = " << QApplication::desktop()->physicalDpiX() << '/' << QApplication::desktop()->physicalDpiY())
 1016         //const int numberOfPixelX = ceil(widthDatarectInch * QApplication::desktop()->physicalDpiX());
 1017         //const int numberOfPixelY = ceil(heightDatarectInch * QApplication::desktop()->physicalDpiY());
 1018 
 1019         // new method
 1020         const int numberOfPixelX = plot->dataRect().width();
 1021         const int numberOfPixelY = plot->dataRect().height();
 1022 
 1023         if (numberOfPixelX <= 0 || numberOfPixelY <= 0) {
 1024             DEBUG(Q_FUNC_INFO << ", number of pixel X <= 0 or number of pixel Y <= 0!")
 1025             RESET_CURSOR;
 1026             return;
 1027         }
 1028 
 1029         DEBUG(Q_FUNC_INFO << ", numberOfPixelX/numberOfPixelY = " << numberOfPixelX << '/' << numberOfPixelY)
 1030         //TODO: not needed with new method
 1031         const double minLogicalDiffX = plot->dataRect().width()/numberOfPixelX;
 1032         const double minLogicalDiffY = plot->dataRect().height()/numberOfPixelY;
 1033         DEBUG(Q_FUNC_INFO << ", -> minLogicalDiffX/Y = " << minLogicalDiffX << '/' << minLogicalDiffY)
 1034 
 1035         // eliminate multiple scene points (size (numberOfPixelX + 1) * (numberOfPixelY + 1))
 1036         QVector<QVector<bool>> scenePointsUsed(numberOfPixelX + 1);
 1037         for (auto& col: scenePointsUsed)
 1038             col.resize(numberOfPixelY + 1);
 1039 
 1040         const auto columnProperties = xColumn->properties();
 1041         int startIndex, endIndex;
 1042         if (columnProperties == AbstractColumn::Properties::MonotonicDecreasing ||
 1043             columnProperties == AbstractColumn::Properties::MonotonicIncreasing) {
 1044             DEBUG(Q_FUNC_INFO << ", column monotonic")
 1045             double xMin = cSystem->mapSceneToLogical(plot->dataRect().topLeft()).x();
 1046             double xMax = cSystem->mapSceneToLogical(plot->dataRect().bottomRight()).x();
 1047             DEBUG(Q_FUNC_INFO << ", xMin/xMax = " << xMin << '/' << xMax)
 1048 
 1049             startIndex = Column::indexForValue(xMin, m_logicalPoints, columnProperties);
 1050             endIndex = Column::indexForValue(xMax, m_logicalPoints, columnProperties);
 1051 
 1052             if (startIndex > endIndex && endIndex >= 0)
 1053                 std::swap(startIndex, endIndex);
 1054 
 1055             if (startIndex < 0)
 1056                 startIndex = 0;
 1057             if (endIndex < 0)
 1058                 endIndex = numberOfPoints - 1;
 1059 
 1060         } else {
 1061             DEBUG(Q_FUNC_INFO << ", column not monotonic")
 1062             startIndex = 0;
 1063             endIndex = numberOfPoints - 1;
 1064         }
 1065 
 1066         m_pointVisible.clear();
 1067         m_pointVisible.resize(numberOfPoints);
 1068         cSystem->mapLogicalToScene(startIndex, endIndex, m_logicalPoints, m_scenePoints,
 1069                 m_pointVisible, scenePointsUsed, minLogicalDiffX, minLogicalDiffY);
 1070     }
 1071     }
 1072     //} // (symbolsStyle != Symbol::Style::NoSymbols || valuesType != XYCurve::NoValues )
 1073 
 1074     m_suppressRecalc = true;
 1075     updateLines();
 1076     updateDropLines();
 1077     updateSymbols();
 1078     updateValues();
 1079     m_suppressRecalc = false;
 1080     updateErrorBars();
 1081 
 1082     RESET_CURSOR;
 1083     }
 1084 }
 1085 
 1086 /*!
 1087  * called if the x- or y-data was changed.
 1088  * copies the valid data points from the x- and y-columns into the internal container
 1089  */
 1090 void XYCurvePrivate::recalcLogicalPoints() {
 1091     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::recalcLogicalPoints()");
 1092 
 1093     m_pointVisible.clear();
 1094     m_logicalPoints.clear();
 1095     connectedPointsLogical.clear();
 1096     validPointsIndicesLogical.clear();
 1097 
 1098     if (!xColumn || !yColumn)
 1099         return;
 1100 
 1101     auto xColMode = xColumn->columnMode();
 1102     auto yColMode = yColumn->columnMode();
 1103     const int rows = xColumn->rowCount();
 1104     m_logicalPoints.reserve(rows);
 1105 
 1106     //take only valid and non masked points
 1107     for (int row{0}; row < rows; row++) {
 1108         if ( xColumn->isValid(row) && yColumn->isValid(row)
 1109                 && (!xColumn->isMasked(row)) && (!yColumn->isMasked(row)) ) {
 1110             QPointF tempPoint;
 1111 
 1112             switch (xColMode) {
 1113             case AbstractColumn::ColumnMode::Numeric:
 1114             case AbstractColumn::ColumnMode::Integer:
 1115             case AbstractColumn::ColumnMode::BigInt:
 1116                 tempPoint.setX(xColumn->valueAt(row));
 1117                 break;
 1118             case AbstractColumn::ColumnMode::DateTime:
 1119                 tempPoint.setX(xColumn->dateTimeAt(row).toMSecsSinceEpoch());
 1120                 break;
 1121             case AbstractColumn::ColumnMode::Text:
 1122             case AbstractColumn::ColumnMode::Month:
 1123             case AbstractColumn::ColumnMode::Day:
 1124                 break;
 1125             }
 1126 
 1127             switch (yColMode) {
 1128             case AbstractColumn::ColumnMode::Numeric:
 1129             case AbstractColumn::ColumnMode::Integer:
 1130             case AbstractColumn::ColumnMode::BigInt:
 1131                 tempPoint.setY(yColumn->valueAt(row));
 1132                 break;
 1133             case AbstractColumn::ColumnMode::DateTime:
 1134                 tempPoint.setY(yColumn->dateTimeAt(row).toMSecsSinceEpoch());
 1135                 break;
 1136             case AbstractColumn::ColumnMode::Text:
 1137             case AbstractColumn::ColumnMode::Month:
 1138             case AbstractColumn::ColumnMode::Day:
 1139                 break;
 1140             }
 1141 
 1142             m_logicalPoints.append(tempPoint);
 1143             //TODO: append, resize-reserve
 1144             connectedPointsLogical.push_back(true);
 1145             validPointsIndicesLogical.push_back(row);
 1146         } else {
 1147             if (!connectedPointsLogical.empty())
 1148                 connectedPointsLogical[connectedPointsLogical.size() - 1] = false;
 1149         }
 1150     }
 1151 
 1152     m_pointVisible.resize(m_logicalPoints.size());
 1153 }
 1154 
 1155 /*!
 1156  * Adds a line, which connects two points, but only if they don't lie on the same xAxis pixel.
 1157  * If they lie on the same x pixel, draw a vertical line between the minimum and maximum y value. So all points are included
 1158  * This function is only valid for linear x Axis scale!
 1159  * @param p0 first point
 1160  * @param p1 second point
 1161  * @param overlap if at the previous call was an overlap between the previous two points
 1162  * @param minLogicalDiffX logical difference between two pixels
 1163  * @param pixelDiff x pixel distance between two points
 1164  */
 1165 void XYCurvePrivate::addLinearLine(QPointF p0, QPointF p1, QPointF& lastPoint, double minLogicalDiffX, qint64& pixelDiff) {
 1166     pixelDiff = qRound64(p1.x() / minLogicalDiffX) - qRound64(p0.x() / minLogicalDiffX);
 1167     //QDEBUG(Q_FUNC_INFO << ", " << p0 << " -> " << p1  << "p0.x*minLogicalDiffX =" << p0.x()*minLogicalDiffX << ", p1.x*minLogicalDiffX =" << p1.x()*minLogicalDiffX << ", pixelDiff =" << pixelDiff);
 1168 
 1169     addUniqueLine(p0, p1, lastPoint, pixelDiff);
 1170 }
 1171 
 1172 /*!
 1173  * Adds a line, which connects two points, but only if they don't lie on the same xAxis pixel.
 1174  * If they lie on the same x pixel, draw a vertical line between the minimum and maximum y value. So all points are included
 1175  * This function can be used for all axis scalings (linear, log, sqrt, ...). For the linear case use the function above, because it's optimized for the linear case
 1176  * @param p0 first point
 1177  * @param p1 second point
 1178  * @param lastPoint remember last point in case of overlap
 1179  * @param pixelDiff x pixel distance between two points
 1180  * @param pixelCount pixel count
 1181  */
 1182 void XYCurvePrivate::addLine(QPointF p0, QPointF p1, QPointF& lastPoint, qint64& pixelDiff, int numberOfPixelX) {
 1183     DEBUG(Q_FUNC_INFO)
 1184 
 1185     if (plot->xScale() == CartesianPlot::Scale::Linear) {
 1186         double minLogicalDiffX = (plot->xMax() - plot->xMin())/numberOfPixelX;
 1187         //DEBUG("   plot->xMax() - plot->xMin() = " << plot->xMax() - plot->xMin())
 1188         //DEBUG("   plot->dataRect().width() = " << plot->dataRect().width())
 1189         //DEBUG("   -> minLogicalDiffX = " << minLogicalDiffX)
 1190         addLinearLine(p0, p1, lastPoint, minLogicalDiffX, pixelDiff);
 1191     } else {
 1192         // for nonlinear scaling the pixel distance must be calculated for every point pair
 1193         QPointF p0Scene = cSystem->mapLogicalToScene(p0, CartesianCoordinateSystem::MappingFlag::SuppressPageClipping);
 1194         QPointF p1Scene = cSystem->mapLogicalToScene(p1, CartesianCoordinateSystem::MappingFlag::SuppressPageClipping);
 1195 
 1196         // if the point is not valid, don't create a line
 1197         //if (std::isnan(p0Scene.x()) || std::isnan(p0Scene.y()))
 1198         if ((p0Scene.x() == 0 && p0Scene.y() == 0) || (p1Scene.x() == 0 && p1Scene.y() == 0)) { // not possible to create line
 1199             DEBUG(Q_FUNC_INFO << ", not possible to create a line between " << p0Scene.x() << ',' << p0Scene.y() << " and "<< p1Scene.x() << ',' << p1Scene.y())
 1200             return;
 1201         }
 1202 
 1203         // using only the difference between the points is not sufficient, because p0 is updated always
 1204         // if new line is added or not
 1205         qint64 p0Pixel = qRound64((p0Scene.x() - plot->dataRect().x()) / (double)plot->dataRect().width() * numberOfPixelX);
 1206         qint64 p1Pixel = qRound64((p1Scene.x() - plot->dataRect().x()) / (double)plot->dataRect().width() * numberOfPixelX);
 1207         //DEBUG(Q_FUNC_INFO << ", p0Pixel/p1Pixel = " << p0Pixel << ' ' << p1Pixel)
 1208         pixelDiff = p1Pixel - p0Pixel;
 1209         addUniqueLine(p0, p1, lastPoint, pixelDiff);
 1210     }
 1211 }
 1212 
 1213 /*!
 1214  * \brief XYCurvePrivate::addUniqueLine
 1215  * This function is called from the other two addLine() functions to avoid duplication
 1216  * @param p0 first point
 1217  * @param p1 second point
 1218  * @param lastPoint remember last point in case of overlap
 1219  * @param pixelDiff x pixel distance between two points
 1220  */
 1221 void XYCurvePrivate::addUniqueLine(QPointF p0, QPointF p1, QPointF& lastPoint, qint64& pixelDiff) {
 1222     //QDEBUG(Q_FUNC_INFO << " :" << p0 << " ->" << p1 << ", lastPoint =" << lastPoint << ", pixelDiff =" << pixelDiff)
 1223     if (pixelDiff == 0) {
 1224         //QDEBUG("  pixelDiff == 0!")
 1225         if (isnan(lastPoint.x()))   // save last point
 1226             lastPoint = p0;
 1227     } else {    // pixelDiff > 0
 1228         //QDEBUG("  pixelDiff =" << pixelDiff << ", last point : " << lastPoint)
 1229         if (!isnan(lastPoint.x())) { // when previously lastPoint, draw a line
 1230             //QDEBUG("  REDUCED LINE from " << lastPoint << " to " << p0)
 1231             //TODO: only when line in scene
 1232             //if ((p0.x() >= plot->xMin() && p0.x() <= plot->xMax()) || (p1.x() >= plot->xMin() && p1.x() <= plot->xMax()))
 1233             // || (p0.x() < plot->xMin() && p1.x() > plot->xMax()) || (p0.x() > plot->xMax() && p1.x() < plot->xMin())
 1234             // same for y
 1235             m_lines.append(QLineF(lastPoint, p0));
 1236 
 1237             lastPoint.setX(NAN);
 1238         }
 1239 
 1240         //QDEBUG("  LINE " << p0 << ' ' << p1)
 1241         //TODO only when line in scene (s.a.)
 1242         m_lines.append(QLineF(p0, p1));
 1243     }
 1244 }
 1245 
 1246 /*!
 1247   recalculates the painter path for the lines connecting the data points.
 1248   Called each time when the type of this connection is changed.
 1249 TODO: At the moment also the points which are outside of the scene are added. This algorithm can be improved by omitting lines
 1250   lines not visible in plot
 1251 */
 1252 void XYCurvePrivate::updateLines() {
 1253 #ifdef PERFTRACE_CURVES
 1254     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines()");
 1255 #endif
 1256     linePath = QPainterPath();
 1257     m_lines.clear();
 1258     if (lineType == XYCurve::LineType::NoLine) {
 1259         DEBUG(Q_FUNC_INFO << ", nothing to do, since line type is XYCurve::LineType::NoLine");
 1260         updateFilling();
 1261         recalcShapeAndBoundingRect();
 1262         return;
 1263     }
 1264 
 1265     int numberOfPoints{m_logicalPoints.size()};
 1266     if (numberOfPoints <= 1) {
 1267         DEBUG(Q_FUNC_INFO << ", nothing to do, since not enough data points available");
 1268         recalcShapeAndBoundingRect();
 1269         return;
 1270     }
 1271 
 1272     const QRectF pageRect = plot->dataRect();
 1273     // old method using DPI
 1274     //const double widthDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().width(), Worksheet::Unit::Inch);
 1275     //float heightDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().height(), Worksheet::Unit::Inch);    // unsed
 1276     //const int countPixelX = ceil(widthDatarectInch * QApplication::desktop()->physicalDpiX());
 1277     //int countPixelY = ceil(heightDatarectInch*QApplication::desktop()->physicalDpiY());   // unused
 1278     // new method
 1279     const int numberOfPixelX = pageRect.width();
 1280 
 1281     // only valid for linear scale
 1282     //double minLogicalDiffX = 1/((plot->xMax()-plot->xMin())/countPixelX); // unused
 1283     //double minLogicalDiffY = 1/((plot->yMax()-plot->yMin())/countPixelY); // unused
 1284 
 1285     //calculate the lines connecting the data points
 1286     {
 1287 #ifdef PERFTRACE_CURVES
 1288     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), calculate the lines connecting the data points");
 1289 #endif
 1290 
 1291     // find index for xMin and xMax to not loop through all values
 1292     int startIndex, endIndex;
 1293     auto columnProperties = q->xColumn()->properties();
 1294     if (columnProperties == AbstractColumn::Properties::MonotonicDecreasing ||
 1295         columnProperties == AbstractColumn::Properties::MonotonicIncreasing) {
 1296         DEBUG(Q_FUNC_INFO << ", monotonic")
 1297         const double xMin = cSystem->mapSceneToLogical(pageRect.topLeft()).x();
 1298         const double xMax = cSystem->mapSceneToLogical(pageRect.bottomRight()).x();
 1299 
 1300         startIndex = Column::indexForValue(xMin, m_logicalPoints, columnProperties);
 1301         endIndex = Column::indexForValue(xMax, m_logicalPoints, columnProperties);
 1302 
 1303         if (startIndex > endIndex)
 1304             std::swap(startIndex, endIndex);
 1305 
 1306         startIndex--; // use one value before
 1307         endIndex++;
 1308         if (startIndex < 0)
 1309             startIndex = 0;
 1310         if (endIndex < 0 || endIndex >= numberOfPoints)
 1311             endIndex = numberOfPoints - 1;
 1312 
 1313         numberOfPoints = endIndex - startIndex + 1;
 1314     } else {
 1315         DEBUG(Q_FUNC_INFO << ", non monotonic")
 1316         startIndex = 0;
 1317         endIndex = numberOfPoints - 1;
 1318     }
 1319     DEBUG(Q_FUNC_INFO << ", start/endIndex = " << startIndex << '/' << endIndex)
 1320 
 1321     QPointF tempPoint1, tempPoint2; // used as temporaryPoints to interpolate datapoints if set
 1322     if (columnProperties == AbstractColumn::Properties::Constant) {
 1323         DEBUG(Q_FUNC_INFO << ", CONSTANT column")
 1324         tempPoint1 = QPointF(plot->xMin(), plot->yMin());
 1325         tempPoint2 = QPointF(plot->xMin(), plot->yMax());
 1326         m_lines.append(QLineF(tempPoint1, tempPoint2));
 1327     } else {
 1328         QPointF lastPoint{NAN, NAN};    // last x value
 1329         qint64 pixelDiff;
 1330         QPointF p0, p1;
 1331 
 1332         switch (lineType) {
 1333         case XYCurve::LineType::NoLine:
 1334             break;
 1335         case XYCurve::LineType::Line: {
 1336             for (int i{startIndex}; i < endIndex; i++) {
 1337                 if (!lineSkipGaps && !connectedPointsLogical.at(i))
 1338                     continue;
 1339                 p0 = m_logicalPoints.at(i);
 1340                 p1 = m_logicalPoints.at(i+1);
 1341                 if (lineIncreasingXOnly && (p1.x() < p0.x())) // skip points
 1342                     continue;
 1343                 addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
 1344             }
 1345 
 1346             if (!isnan(lastPoint.x()))  // last line
 1347                 m_lines.append(QLineF(lastPoint, p1));
 1348 
 1349             break;
 1350         }
 1351         case XYCurve::LineType::StartHorizontal: {
 1352             for (int i{startIndex}; i < endIndex; i++) {
 1353                 if (!lineSkipGaps && !connectedPointsLogical[i])
 1354                     continue;
 1355                 p0 = m_logicalPoints.at(i);
 1356                 p1 = m_logicalPoints.at(i+1);
 1357                 if (lineIncreasingXOnly && (p1.x() < p0.x()))
 1358                     continue;
 1359 
 1360                 tempPoint1 = QPointF(p1.x(), p0.y());
 1361                 addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
 1362                 addLine(tempPoint1, p1, lastPoint, pixelDiff, numberOfPixelX);
 1363             }
 1364             if (!isnan(lastPoint.x()))  // last line
 1365                 m_lines.append(QLineF(lastPoint, p1));
 1366 
 1367             break;
 1368         }
 1369         case XYCurve::LineType::StartVertical: {
 1370             for (int i{startIndex}; i < endIndex; i++) {
 1371                 if (!lineSkipGaps && !connectedPointsLogical.at(i))
 1372                     continue;
 1373                 p0 = m_logicalPoints.at(i);
 1374                 p1 = m_logicalPoints.at(i+1);
 1375                 if (lineIncreasingXOnly && (p1.x() < p0.x()))
 1376                     continue;
 1377                 tempPoint1 = QPointF(p0.x(), p1.y());
 1378                 addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
 1379                 addLine(tempPoint1, p1, lastPoint, pixelDiff, numberOfPixelX);
 1380             }
 1381             if (!isnan(lastPoint.x()))  // last line
 1382                 m_lines.append(QLineF(lastPoint, p1));
 1383 
 1384             break;
 1385         }
 1386         case XYCurve::LineType::MidpointHorizontal: {
 1387             for (int i{startIndex}; i < endIndex; i++) {
 1388                 if (!lineSkipGaps && !connectedPointsLogical[i])
 1389                     continue;
 1390 
 1391                 p0 = m_logicalPoints.at(i);
 1392                 p1 = m_logicalPoints.at(i+1);
 1393                 if (lineIncreasingXOnly && (p1.x() < p0.x()))
 1394                     continue;
 1395                 tempPoint1 = QPointF(p0.x() + (p1.x()-p0.x())/2., p0.y());
 1396                 tempPoint2 = QPointF(p0.x() + (p1.x()-p0.x())/2., p1.y());
 1397                 addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
 1398                 addLine(tempPoint1, tempPoint2, lastPoint, pixelDiff, numberOfPixelX);
 1399                 addLine(tempPoint2, p1, lastPoint, pixelDiff, numberOfPixelX);
 1400             }
 1401             if (!isnan(lastPoint.x()))  // last line
 1402                 m_lines.append(QLineF(lastPoint, p1));
 1403 
 1404             break;
 1405         }
 1406         case XYCurve::LineType::MidpointVertical: {
 1407             for (int i{startIndex}; i < endIndex; i++) {
 1408                 if (!lineSkipGaps && !connectedPointsLogical[i])
 1409                     continue;
 1410 
 1411                 p0 = m_logicalPoints.at(i);
 1412                 p1 = m_logicalPoints.at(i+1);
 1413                 if (lineIncreasingXOnly && (p1.x() < p0.x()))
 1414                     continue;
 1415                 tempPoint1 = QPointF(p0.x(), p0.y() + (p1.y()-p0.y())/2.);
 1416                 tempPoint2 = QPointF(p1.x(), p0.y() + (p1.y()-p0.y())/2.);
 1417                 addLine(p0, tempPoint1, lastPoint, pixelDiff, numberOfPixelX);
 1418                 addLine(tempPoint1, tempPoint2, lastPoint, pixelDiff, numberOfPixelX);
 1419                 addLine(tempPoint2, p1, lastPoint, pixelDiff, numberOfPixelX);
 1420             }
 1421             if (!isnan(lastPoint.x()))  // last line
 1422                 m_lines.append(QLineF(lastPoint, p1));
 1423 
 1424             break;
 1425         }
 1426         case XYCurve::LineType::Segments2: {
 1427             int skip{0};
 1428             for (int i{startIndex}; i < endIndex; i++) {
 1429                 p0 = m_logicalPoints.at(i);
 1430                 p1 = m_logicalPoints.at(i+1);
 1431                 if (skip != 1) {
 1432                     if ( (!lineSkipGaps && !connectedPointsLogical[i])
 1433                         || (lineIncreasingXOnly && (p1.x() < p0.x())) ) {
 1434                         skip = 0;
 1435                         continue;
 1436                     }
 1437                     addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
 1438                     skip++;
 1439                 } else {
 1440                     skip = 0;
 1441                     if (!isnan(lastPoint.x())) {
 1442                         lastPoint.setX(NAN);
 1443                         m_lines.append(QLineF(lastPoint, p1));
 1444                     }
 1445                 }
 1446             }
 1447             if (!isnan(lastPoint.x()))  // last line
 1448                 m_lines.append(QLineF(m_logicalPoints.at(endIndex - 1), m_logicalPoints.at(endIndex)));
 1449 
 1450             break;
 1451         }
 1452         case XYCurve::LineType::Segments3: {
 1453             int skip{0};
 1454             for (int i{startIndex}; i < endIndex; i++) {
 1455                 if (skip != 2) {
 1456                     p0 = m_logicalPoints.at(i);
 1457                     p1 = m_logicalPoints.at(i+1);
 1458                     if ( (!lineSkipGaps && !connectedPointsLogical[i])
 1459                         || (lineIncreasingXOnly && (p1.x() < p0.x())) ) {
 1460                         skip = 0;
 1461                         continue;
 1462                     }
 1463                     addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
 1464                     skip++;
 1465                 } else {
 1466                     skip = 0;
 1467                     if (!isnan(lastPoint.x())) {
 1468                         lastPoint.setX(NAN);
 1469                         m_lines.append(QLineF(lastPoint, p1));
 1470                     }
 1471                     if (!isnan(lastPoint.x()))  // last line
 1472                         m_lines.append(QLineF(m_logicalPoints[endIndex-1], m_logicalPoints[endIndex]));
 1473 
 1474                     break;
 1475                 }
 1476             }
 1477             if (!isnan(lastPoint.x()))  // last line
 1478                 m_lines.append(QLineF(m_logicalPoints.at(endIndex - 1), m_logicalPoints.at(endIndex)));
 1479 
 1480             break;
 1481         }
 1482         case XYCurve::LineType::SplineCubicNatural:
 1483         case XYCurve::LineType::SplineCubicPeriodic:
 1484         case XYCurve::LineType::SplineAkimaNatural:
 1485         case XYCurve::LineType::SplineAkimaPeriodic: {
 1486             std::unique_ptr<double[]> x(new double[numberOfPoints]());
 1487             std::unique_ptr<double[]> y(new double[numberOfPoints]());
 1488             for (int i{0}; i < numberOfPoints; i++) { // TODO: interpolating only between the visible points?
 1489                 x[i] = m_logicalPoints.at(i+startIndex).x();
 1490                 y[i] = m_logicalPoints.at(i+startIndex).y();
 1491             }
 1492 
 1493             gsl_interp_accel *acc = gsl_interp_accel_alloc();
 1494             gsl_spline *spline{nullptr};
 1495             gsl_set_error_handler_off();
 1496             switch (lineType) {
 1497             case XYCurve::LineType::SplineCubicNatural:
 1498                 spline = gsl_spline_alloc(gsl_interp_cspline, numberOfPoints);
 1499                 break;
 1500             case XYCurve::LineType::SplineCubicPeriodic:
 1501                 spline = gsl_spline_alloc(gsl_interp_cspline_periodic, numberOfPoints);
 1502                 break;
 1503             case XYCurve::LineType::SplineAkimaNatural:
 1504                 spline = gsl_spline_alloc(gsl_interp_akima, numberOfPoints);
 1505                 break;
 1506             case XYCurve::LineType::SplineAkimaPeriodic:
 1507                 spline = gsl_spline_alloc(gsl_interp_akima_periodic, numberOfPoints);
 1508                 break;
 1509             case XYCurve::LineType::NoLine:
 1510             case XYCurve::LineType::Line:
 1511             case XYCurve::LineType::StartHorizontal:
 1512             case XYCurve::LineType::StartVertical:
 1513             case XYCurve::LineType::MidpointHorizontal:
 1514             case XYCurve::LineType::MidpointVertical:
 1515             case XYCurve::LineType::Segments2:
 1516             case XYCurve::LineType::Segments3:
 1517                 break;
 1518             }
 1519 
 1520             if (!spline) {
 1521                 QString msg;
 1522                 if ( (lineType == XYCurve::LineType::SplineAkimaNatural || lineType == XYCurve::LineType::SplineAkimaPeriodic) && numberOfPoints < 5)
 1523                     msg = i18n("Error: Akima spline interpolation requires a minimum of 5 points.");
 1524                 else
 1525                     msg = i18n("Error: Could not initialize the spline function.");
 1526                 emit q->info(msg);
 1527 
 1528                 recalcShapeAndBoundingRect();
 1529                 gsl_interp_accel_free(acc);
 1530                 return;
 1531             }
 1532 
 1533             int status = gsl_spline_init(spline, x.get(), y.get(), numberOfPoints);
 1534             if (status != 0) {
 1535                 //TODO: check in gsl/interp.c when GSL_EINVAL is thrown
 1536                 QString gslError;
 1537                 if (status == GSL_EINVAL)
 1538                     gslError = i18n("x values must be monotonically increasing.");
 1539                 else
 1540                     gslError = gslErrorToString(status);
 1541                 emit q->info( i18n("Error: %1", gslError) );
 1542 
 1543                 recalcShapeAndBoundingRect();
 1544                 gsl_spline_free(spline);
 1545                 gsl_interp_accel_free(acc);
 1546                 return;
 1547             }
 1548 
 1549             //create interpolating points
 1550             //TODO: QVector
 1551             std::vector<double> xinterp, yinterp;
 1552             for (int i{0}; i < numberOfPoints - 1; i++) {
 1553                 const double x1 = x[i];
 1554                 const double x2 = x[i+1];
 1555                 const double step = std::abs(x2 - x1)/(lineInterpolationPointsCount + 1);
 1556 
 1557                 for (int j{0}; j < (lineInterpolationPointsCount + 1); j++) {
 1558                     const double xi = x1 + j*step;
 1559                     const double yi = gsl_spline_eval(spline, xi, acc);
 1560                     xinterp.push_back(xi);
 1561                     yinterp.push_back(yi);
 1562                 }
 1563             }
 1564 
 1565             if (!xinterp.empty()) {
 1566                 for (unsigned int i{0}; i < xinterp.size() - 1; i++) {
 1567                     p0 = QPointF(xinterp[i], yinterp[i]);
 1568                     p1 = QPointF(xinterp[i + 1], yinterp[i + 1]);
 1569                     addLine(p0, p1, lastPoint, pixelDiff, numberOfPixelX);
 1570                 }
 1571 
 1572                 addLine(QPointF(xinterp[xinterp.size() - 1], yinterp[yinterp.size() - 1]), QPointF(x[numberOfPoints - 1], y[numberOfPoints - 1]),
 1573                         lastPoint, pixelDiff, numberOfPixelX);
 1574 
 1575                 // add last line
 1576                 if (!isnan(lastPoint.x()))
 1577                     m_lines.append(QLineF(QPointF(xinterp[xinterp.size() - 1], yinterp[yinterp.size() - 1]),
 1578                                 QPointF(x[numberOfPoints - 1], y[numberOfPoints - 1])));
 1579             }
 1580 
 1581             gsl_spline_free(spline);
 1582             gsl_interp_accel_free(acc);
 1583             break;
 1584             }
 1585         }
 1586     }
 1587     }
 1588 
 1589     //map the lines to scene coordinates
 1590     {
 1591 #ifdef PERFTRACE_CURVES
 1592         PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), map lines to scene coordinates");
 1593 #endif
 1594         m_lines = cSystem->mapLogicalToScene(m_lines);
 1595     }
 1596 
 1597     {
 1598 #ifdef PERFTRACE_CURVES
 1599     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), calculate new line path");
 1600 #endif
 1601         //new line path
 1602         for (const auto& line : qAsConst(m_lines)) {
 1603             linePath.moveTo(line.p1());
 1604             linePath.lineTo(line.p2());
 1605         }
 1606     }
 1607 
 1608     updateFilling();
 1609     recalcShapeAndBoundingRect();
 1610 }
 1611 
 1612 /*!
 1613   recalculates the painter path for the drop lines.
 1614   Called each time when the type of the drop lines is changed.
 1615 */
 1616 void XYCurvePrivate::updateDropLines() {
 1617     dropLinePath = QPainterPath();
 1618     if (dropLineType == XYCurve::DropLineType::NoDropLine) {
 1619         recalcShapeAndBoundingRect();
 1620         return;
 1621     }
 1622 
 1623     //calculate drop lines
 1624     QVector<QLineF> dlines;
 1625     const double xMin = plot->xMin();
 1626     const double yMin = plot->yMin();
 1627 
 1628     //don't skip the invisible points, we still need to calculate
 1629     //the drop lines falling into the plot region
 1630     switch (dropLineType) {
 1631     case XYCurve::DropLineType::NoDropLine:
 1632         break;
 1633     case XYCurve::DropLineType::X:
 1634         for (const auto& point: qAsConst(m_logicalPoints)) {
 1635             dlines.append(QLineF(point, QPointF(point.x(), yMin)));
 1636         }
 1637         break;
 1638     case XYCurve::DropLineType::Y:
 1639         for (const auto& point: qAsConst(m_logicalPoints)) {
 1640             dlines.append(QLineF(point, QPointF(xMin, point.y())));
 1641         }
 1642         break;
 1643     case XYCurve::DropLineType::XY:
 1644         for (const auto& point: qAsConst(m_logicalPoints)) {
 1645             dlines.append(QLineF(point, QPointF(point.x(), yMin)));
 1646             dlines.append(QLineF(point, QPointF(xMin, point.y())));
 1647         }
 1648         break;
 1649     case XYCurve::DropLineType::XZeroBaseline:
 1650         for (const auto& point: qAsConst(m_logicalPoints)) {
 1651             dlines.append(QLineF(point, QPointF(point.x(), 0)));
 1652         }
 1653         break;
 1654     case XYCurve::DropLineType::XMinBaseline:
 1655         for (const auto& point: qAsConst(m_logicalPoints)) {
 1656             dlines.append(QLineF(point, QPointF(point.x(), yColumn->minimum())));
 1657         }
 1658         break;
 1659     case XYCurve::DropLineType::XMaxBaseline:
 1660         for (const auto& point: qAsConst(m_logicalPoints)) {
 1661             dlines.append(QLineF(point, QPointF(point.x(), yColumn->maximum())));
 1662         }
 1663         break;
 1664     }
 1665 
 1666     //map the drop lines to scene coordinates
 1667     dlines = cSystem->mapLogicalToScene(dlines);
 1668 
 1669     //new painter path for the drop lines
 1670     for (const auto& line : qAsConst(dlines)) {
 1671         dropLinePath.moveTo(line.p1());
 1672         dropLinePath.lineTo(line.p2());
 1673     }
 1674 
 1675     recalcShapeAndBoundingRect();
 1676 }
 1677 
 1678 void XYCurvePrivate::updateSymbols() {
 1679 #ifdef PERFTRACE_CURVES
 1680     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateSymbols()");
 1681 #endif
 1682     symbolsPath = QPainterPath();
 1683     if (symbolsStyle != Symbol::Style::NoSymbols) {
 1684         QPainterPath path = Symbol::pathFromStyle(symbolsStyle);
 1685 
 1686         QTransform trafo;
 1687         trafo.scale(symbolsSize, symbolsSize);
 1688         path = trafo.map(path);
 1689         trafo.reset();
 1690 
 1691         if (symbolsRotationAngle != 0) {
 1692             trafo.rotate(symbolsRotationAngle);
 1693             path = trafo.map(path);
 1694         }
 1695 
 1696         for (const auto& point : qAsConst(m_scenePoints)) {
 1697             trafo.reset();
 1698             trafo.translate(point.x(), point.y());
 1699             symbolsPath.addPath(trafo.map(path));
 1700         }
 1701     }
 1702 
 1703     recalcShapeAndBoundingRect();
 1704 }
 1705 
 1706 /*!
 1707   recreates the value strings to be shown and recalculates their draw position.
 1708 */
 1709 void XYCurvePrivate::updateValues() {
 1710 #ifdef PERFTRACE_CURVES
 1711     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateValues()");
 1712 #endif
 1713     valuesPath = QPainterPath();
 1714     m_valuePoints.clear();
 1715     m_valueStrings.clear();
 1716 
 1717     const int numberOfPoints = m_logicalPoints.size();
 1718     if (valuesType == XYCurve::ValuesType::NoValues || numberOfPoints == 0) {
 1719         recalcShapeAndBoundingRect();
 1720         return;
 1721     }
 1722     m_valuePoints.reserve(numberOfPoints);
 1723     m_valueStrings.reserve(numberOfPoints);
 1724 
 1725     //determine the value string for all points that are currently visible in the plot
 1726     int i{0};
 1727     SET_NUMBER_LOCALE
 1728     switch (valuesType) {
 1729     case XYCurve::ValuesType::NoValues:
 1730     case XYCurve::ValuesType::X: {
 1731         CartesianPlot::RangeFormat rangeFormat = plot->xRangeFormat();
 1732         int precision = valuesPrecision;
 1733         if (xColumn->columnMode() == AbstractColumn::ColumnMode::Integer || xColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
 1734             precision = 0;
 1735         for (const auto& point : qAsConst(m_logicalPoints)) {
 1736             if (!m_pointVisible.at(i++)) continue;
 1737             QString value;
 1738             if (rangeFormat == CartesianPlot::RangeFormat::Numeric)
 1739                 value = numberLocale.toString(point.x(), valuesNumericFormat, precision);
 1740             else
 1741                 value = QDateTime::fromMSecsSinceEpoch(point.x()).toString(valuesDateTimeFormat);
 1742             m_valueStrings << valuesPrefix + value + valuesSuffix;
 1743         }
 1744         break;
 1745     }
 1746     case XYCurve::ValuesType::Y: {
 1747         CartesianPlot::RangeFormat rangeFormat = plot->yRangeFormat();
 1748         int precision = valuesPrecision;
 1749         if (yColumn->columnMode() == AbstractColumn::ColumnMode::Integer || yColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
 1750             precision = 0;
 1751         for (const auto& point : qAsConst(m_logicalPoints)) {
 1752             if (!m_pointVisible.at(i++)) continue;
 1753             QString value;
 1754             if (rangeFormat == CartesianPlot::RangeFormat::Numeric)
 1755                 value = numberLocale.toString(point.y(), valuesNumericFormat, precision);
 1756             else
 1757                 value = QDateTime::fromMSecsSinceEpoch(point.y()).toString(valuesDateTimeFormat);
 1758             m_valueStrings << valuesPrefix + value + valuesSuffix;
 1759         }
 1760         break;
 1761     }
 1762     case XYCurve::ValuesType::XY:
 1763     case XYCurve::ValuesType::XYBracketed: {
 1764         CartesianPlot::RangeFormat xRangeFormat = plot->xRangeFormat();
 1765         CartesianPlot::RangeFormat yRangeFormat = plot->yRangeFormat();
 1766 
 1767         int xPrecision = valuesPrecision;
 1768         if (xColumn->columnMode() == AbstractColumn::ColumnMode::Integer || xColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
 1769             xPrecision = 0;
 1770 
 1771         int yPrecision = valuesPrecision;
 1772         if (yColumn->columnMode() == AbstractColumn::ColumnMode::Integer || yColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
 1773             yPrecision = 0;
 1774 
 1775         for (const auto& point : qAsConst(m_logicalPoints)) {
 1776             if (!m_pointVisible.at(i++)) continue;
 1777             QString value;
 1778             if (valuesType == XYCurve::ValuesType::XYBracketed)
 1779                 value = '(';
 1780             if (xRangeFormat == CartesianPlot::RangeFormat::Numeric)
 1781                 value += numberLocale.toString(point.x(), valuesNumericFormat, xPrecision);
 1782             else
 1783                 value += QDateTime::fromMSecsSinceEpoch(point.x()).toString(valuesDateTimeFormat);
 1784 
 1785             if (yRangeFormat == CartesianPlot::RangeFormat::Numeric)
 1786                 value += ',' + numberLocale.toString(point.y(), valuesNumericFormat, yPrecision);
 1787             else
 1788                 value += ',' + QDateTime::fromMSecsSinceEpoch(point.y()).toString(valuesDateTimeFormat);
 1789 
 1790             if (valuesType == XYCurve::ValuesType::XYBracketed)
 1791                 value += ')';
 1792 
 1793             m_valueStrings << valuesPrefix + value + valuesSuffix;
 1794         }
 1795         break;
 1796     }
 1797     case XYCurve::ValuesType::CustomColumn: {
 1798         if (!valuesColumn) {
 1799             recalcShapeAndBoundingRect();
 1800             return;
 1801         }
 1802 
 1803         const int endRow{qMin(numberOfPoints, valuesColumn->rowCount())};
 1804         auto xColMode{valuesColumn->columnMode()};
 1805         for (int i = 0; i < endRow; ++i) {
 1806             if (!m_pointVisible[i]) continue;
 1807 
 1808             if ( !valuesColumn->isValid(i) || valuesColumn->isMasked(i) )
 1809                 continue;
 1810 
 1811             switch (xColMode) {
 1812             case AbstractColumn::ColumnMode::Numeric:
 1813                 m_valueStrings << valuesPrefix + numberLocale.toString(valuesColumn->valueAt(i), valuesNumericFormat, valuesPrecision) + valuesSuffix;
 1814                 break;
 1815             case AbstractColumn::ColumnMode::Integer:
 1816             case AbstractColumn::ColumnMode::BigInt:
 1817                 m_valueStrings << valuesPrefix + numberLocale.toString(valuesColumn->valueAt(i)) + valuesSuffix;
 1818                 break;
 1819             case AbstractColumn::ColumnMode::Text:
 1820                 m_valueStrings << valuesPrefix + valuesColumn->textAt(i) + valuesSuffix;
 1821                 break;
 1822             case AbstractColumn::ColumnMode::DateTime:
 1823             case AbstractColumn::ColumnMode::Month:
 1824             case AbstractColumn::ColumnMode::Day:
 1825                 m_valueStrings << valuesPrefix + valuesColumn->dateTimeAt(i).toString(valuesDateTimeFormat) + valuesSuffix;
 1826                 break;
 1827             }
 1828         }
 1829     }
 1830     }
 1831     m_valueStrings.squeeze();
 1832 
 1833     //Calculate the coordinates where to paint the value strings.
 1834     //The coordinates depend on the actual size of the string.
 1835     QPointF tempPoint;
 1836     QFontMetrics fm(valuesFont);
 1837     const int h{fm.ascent()};
 1838 
 1839     i = 0;
 1840     for (const auto& string : qAsConst(m_valueStrings)) {
 1841         const int w{fm.boundingRect(string).width()};
 1842         const double x{m_scenePoints.at(i).x()};
 1843         const double y{m_scenePoints.at(i).y()};
 1844         i++;
 1845 
 1846         switch (valuesPosition) {
 1847         case XYCurve::ValuesPosition::Above:
 1848             tempPoint = QPointF(x - w/2., y - valuesDistance);
 1849             break;
 1850         case XYCurve::ValuesPosition::Under:
 1851             tempPoint = QPointF(x - w/2., y + valuesDistance + h/2.);
 1852             break;
 1853         case XYCurve::ValuesPosition::Left:
 1854             tempPoint = QPointF(x - valuesDistance - w - 1., y);
 1855             break;
 1856         case XYCurve::ValuesPosition::Right:
 1857             tempPoint = QPointF(x + valuesDistance - 1., y);
 1858             break;
 1859         }
 1860         m_valuePoints.append(tempPoint);
 1861     }
 1862     m_valuePoints.squeeze();
 1863 
 1864     QTransform trafo;
 1865     QPainterPath path;
 1866     i = 0;
 1867     for (const auto& point : qAsConst(m_valuePoints)) {
 1868         path = QPainterPath();
 1869         path.addText(QPoint(0, 0), valuesFont, m_valueStrings.at(i++));
 1870 
 1871         trafo.reset();
 1872         trafo.translate(point.x(), point.y());
 1873         if (valuesRotationAngle != 0)
 1874             trafo.rotate(-valuesRotationAngle);
 1875 
 1876         valuesPath.addPath(trafo.map(path));
 1877     }
 1878 
 1879     recalcShapeAndBoundingRect();
 1880 }
 1881 
 1882 void XYCurvePrivate::updateFilling() {
 1883     if (m_suppressRetransform)
 1884         return;
 1885 
 1886     m_fillPolygons.clear();
 1887 
 1888     //don't try to calculate the filling polygons if
 1889     // - no filling was enabled
 1890     // - the number of visible points on the scene is too high
 1891     // - no scene points available, everything outside of the plot region or no scene points calculated yet
 1892     if (fillingPosition == XYCurve::FillingPosition::NoFilling || m_scenePoints.size() > 1000 || m_scenePoints.isEmpty()) {
 1893         recalcShapeAndBoundingRect();
 1894         return;
 1895     }
 1896 
 1897     QVector<QLineF> fillLines;
 1898 
 1899     //if there're no interpolation lines available (XYCurve::NoLine selected), create line-interpolation,
 1900     //use already available lines otherwise.
 1901     if (!m_lines.isEmpty())
 1902         fillLines = m_lines;
 1903     else {
 1904         for (int i = 0; i < m_logicalPoints.size() - 1; i++) {
 1905             if (!lineSkipGaps && !connectedPointsLogical[i]) continue;
 1906             fillLines.append(QLineF(m_logicalPoints.at(i), m_logicalPoints.at(i+1)));
 1907         }
 1908 
 1909         //no lines available (no points), nothing to do
 1910         if (fillLines.isEmpty())
 1911             return;
 1912 
 1913         fillLines = cSystem->mapLogicalToScene(fillLines);
 1914 
 1915         //no lines available (no points) after mapping, nothing to do
 1916         if (fillLines.isEmpty())
 1917             return;
 1918     }
 1919 
 1920     //create polygon(s):
 1921     //1. Depending on the current zoom-level, only a subset of the curve may be visible in the plot
 1922     //and more of the filling area should be shown than the area defined by the start and end points of the currently visible points.
 1923     //We check first whether the curve crosses the boundaries of the plot and determine new start and end points and put them to the boundaries.
 1924     //2. Furthermore, depending on the current filling type we determine the end point (x- or y-coordinate) where all polygons are closed at the end.
 1925     QPolygonF pol;
 1926     QPointF start = fillLines.at(0).p1(); //starting point of the current polygon, initialize with the first visible point
 1927     QPointF end = fillLines.at(fillLines.size()-1).p2(); //end point of the current polygon, initialize with the last visible point
 1928     const QPointF& first = m_logicalPoints.at(0); //first point of the curve, may not be visible currently
 1929     const QPointF& last = m_logicalPoints.at(m_logicalPoints.size()-1);//last point of the curve, may not be visible currently
 1930     QPointF edge;
 1931     double xEnd{0.}, yEnd{0.};
 1932     if (fillingPosition == XYCurve::FillingPosition::Above) {
 1933         edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin()));
 1934 
 1935         //start point
 1936         if (nsl_math_essentially_equal(start.y(), edge.y())) {
 1937             if (first.x() < plot->xMin())
 1938                 start = edge;
 1939             else if (first.x() > plot->xMax())
 1940                 start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
 1941             else
 1942                 start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin()));
 1943         }
 1944 
 1945         //end point
 1946         if (nsl_math_essentially_equal(end.y(), edge.y())) {
 1947             if (last.x() < plot->xMin())
 1948                 end = edge;
 1949             else if (last.x() > plot->xMax())
 1950                 end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
 1951             else
 1952                 end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin()));
 1953         }
 1954 
 1955         //coordinate at which to close all polygons
 1956         yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())).y();
 1957     } else if (fillingPosition == XYCurve::FillingPosition::Below) {
 1958         edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
 1959 
 1960         //start point
 1961         if (nsl_math_essentially_equal(start.y(), edge.y())) {
 1962             if (first.x() < plot->xMin())
 1963                 start = edge;
 1964             else if (first.x() > plot->xMax())
 1965                 start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
 1966             else
 1967                 start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax()));
 1968         }
 1969 
 1970         //end point
 1971         if (nsl_math_essentially_equal(end.y(), edge.y())) {
 1972             if (last.x() < plot->xMin())
 1973                 end = edge;
 1974             else if (last.x() > plot->xMax())
 1975                 end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
 1976             else
 1977                 end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax()));
 1978         }
 1979 
 1980         //coordinate at which to close all polygons
 1981         yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).y();
 1982     } else if (fillingPosition == XYCurve::FillingPosition::ZeroBaseline) {
 1983         edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
 1984 
 1985         //start point
 1986         if (nsl_math_essentially_equal(start.y(), edge.y())) {
 1987             if (plot->yMax() > 0) {
 1988                 if (first.x() < plot->xMin())
 1989                     start = edge;
 1990                 else if (first.x() > plot->xMax())
 1991                     start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
 1992                 else
 1993                     start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax()));
 1994             } else {
 1995                 if (first.x() < plot->xMin())
 1996                     start = edge;
 1997                 else if (first.x() > plot->xMax())
 1998                     start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
 1999                 else
 2000                     start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin()));
 2001             }
 2002         }
 2003 
 2004         //end point
 2005         if (nsl_math_essentially_equal(end.y(), edge.y())) {
 2006             if (plot->yMax() > 0) {
 2007                 if (last.x() < plot->xMin())
 2008                     end = edge;
 2009                 else if (last.x() > plot->xMax())
 2010                     end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
 2011                 else
 2012                     end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax()));
 2013             } else {
 2014                 if (last.x() < plot->xMin())
 2015                     end = edge;
 2016                 else if (last.x() > plot->xMax())
 2017                     end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
 2018                 else
 2019                     end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin()));
 2020             }
 2021         }
 2022 
 2023         yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin() > 0 ? plot->yMin() : 0)).y();
 2024     } else if (fillingPosition == XYCurve::FillingPosition::Left) {
 2025         edge = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin()));
 2026 
 2027         //start point
 2028         if (nsl_math_essentially_equal(start.x(), edge.x())) {
 2029             if (first.y() < plot->yMin())
 2030                 start = edge;
 2031             else if (first.y() > plot->yMax())
 2032                 start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
 2033             else
 2034                 start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), first.y()));
 2035         }
 2036 
 2037         //end point
 2038         if (nsl_math_essentially_equal(end.x(), edge.x())) {
 2039             if (last.y() < plot->yMin())
 2040                 end = edge;
 2041             else if (last.y() > plot->yMax())
 2042                 end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax()));
 2043             else
 2044                 end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), last.y()));
 2045         }
 2046 
 2047         //coordinate at which to close all polygons
 2048         xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).x();
 2049     } else { //FillingRight
 2050         edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin()));
 2051 
 2052         //start point
 2053         if (nsl_math_essentially_equal(start.x(), edge.x())) {
 2054             if (first.y() < plot->yMin())
 2055                 start = edge;
 2056             else if (first.y() > plot->yMax())
 2057                 start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
 2058             else
 2059                 start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), first.y()));
 2060         }
 2061 
 2062         //end point
 2063         if (nsl_math_essentially_equal(end.x(), edge.x())) {
 2064             if (last.y() < plot->yMin())
 2065                 end = edge;
 2066             else if (last.y() > plot->yMax())
 2067                 end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax()));
 2068             else
 2069                 end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), last.y()));
 2070         }
 2071 
 2072         //coordinate at which to close all polygons
 2073         xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())).x();
 2074     }
 2075 
 2076     if (start != fillLines.at(0).p1())
 2077         pol << start;
 2078 
 2079     QPointF p1, p2;
 2080     for (int i = 0; i < fillLines.size(); ++i) {
 2081         const QLineF& line = fillLines.at(i);
 2082         p1 = line.p1();
 2083         p2 = line.p2();
 2084         if (i != 0 && p1 != fillLines.at(i-1).p2()) {
 2085             //the first point of the current line is not equal to the last point of the previous line
 2086             //->check whether we have a break in between.
 2087             const bool gap = false; //TODO
 2088             if (!gap) {
 2089                 //-> we have no break in the curve -> connect the points by a horizontal/vertical line
 2090                 pol << fillLines.at(i-1).p2() << p1;
 2091             } else {
 2092                 //-> we have a break in the curve -> close the polygon, add it to the polygon list and start a new polygon
 2093                 if (fillingPosition == XYCurve::FillingPosition::Above || fillingPosition == XYCurve::FillingPosition::Below || fillingPosition == XYCurve::FillingPosition::ZeroBaseline) {
 2094                     pol << QPointF(fillLines.at(i-1).p2().x(), yEnd);
 2095                     pol << QPointF(start.x(), yEnd);
 2096                 } else {
 2097                     pol << QPointF(xEnd, fillLines.at(i-1).p2().y());
 2098                     pol << QPointF(xEnd, start.y());
 2099                 }
 2100 
 2101                 m_fillPolygons << pol;
 2102                 pol.clear();
 2103                 start = p1;
 2104             }
 2105         }
 2106         pol << p1 << p2;
 2107     }
 2108 
 2109     if (p2 != end)
 2110         pol << end;
 2111 
 2112     //close the last polygon
 2113     if (fillingPosition == XYCurve::FillingPosition::Above || fillingPosition == XYCurve::FillingPosition::Below || fillingPosition == XYCurve::FillingPosition::ZeroBaseline) {
 2114         pol << QPointF(end.x(), yEnd);
 2115         pol << QPointF(start.x(), yEnd);
 2116     } else {
 2117         pol << QPointF(xEnd, end.y());
 2118         pol << QPointF(xEnd, start.y());
 2119     }
 2120 
 2121     m_fillPolygons << pol;
 2122     recalcShapeAndBoundingRect();
 2123 }
 2124 
 2125  /*!
 2126  * Find y value which corresponds to a @p x . @p valueFound indicates, if value was found.
 2127  * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
 2128  * @param x
 2129  * @param valueFound
 2130  * @return
 2131  */
 2132 double XYCurve::y(double x, bool &valueFound) const {
 2133     if (!yColumn() || !xColumn()) {
 2134         valueFound = false;
 2135         return NAN;
 2136     }
 2137 
 2138     auto yColumnMode = yColumn()->columnMode();
 2139     const int index = xColumn()->indexForValue(x);
 2140     if (index < 0) {
 2141         valueFound = false;
 2142         return NAN;
 2143     }
 2144 
 2145     valueFound = true;
 2146     if (yColumnMode == AbstractColumn::ColumnMode::Numeric || yColumnMode == AbstractColumn::ColumnMode::Integer ||
 2147             yColumnMode == AbstractColumn::ColumnMode::BigInt) {
 2148         return yColumn()->valueAt(index);
 2149     } else {
 2150         valueFound = false;
 2151         return NAN;
 2152     }
 2153 }
 2154 
 2155 /*!
 2156 * Find y DateTime which corresponds to a @p x . @p valueFound indicates, if value was found.
 2157 * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
 2158 * @param x
 2159 * @param valueFound
 2160 * @return Return found value
 2161 */
 2162 QDateTime XYCurve::yDateTime(double x, bool &valueFound) const {
 2163     if (!yColumn() || !xColumn()) {
 2164         valueFound = false;
 2165         return QDateTime();
 2166     }
 2167 
 2168     auto yColumnMode = yColumn()->columnMode();
 2169     const int index = xColumn()->indexForValue(x);
 2170     if (index < 0) {
 2171         valueFound = false;
 2172         return QDateTime();
 2173     }
 2174 
 2175     valueFound = true;
 2176     if (yColumnMode == AbstractColumn::ColumnMode::Day ||
 2177             yColumnMode == AbstractColumn::ColumnMode::Month ||
 2178             yColumnMode == AbstractColumn::ColumnMode::DateTime)
 2179         return yColumn()->dateTimeAt(index);
 2180 
 2181     valueFound = false;
 2182     return QDateTime();
 2183 }
 2184 
 2185 bool XYCurve::minMaxY(int indexMin, int indexMax, double& yMin, double& yMax, bool includeErrorBars) const {
 2186     return minMax(yColumn(), xColumn(), yErrorType(), yErrorPlusColumn(), yErrorMinusColumn(), indexMin, indexMax, yMin, yMax, includeErrorBars);
 2187 }
 2188 
 2189 bool XYCurve::minMaxX(int indexMin, int indexMax, double& xMin, double& xMax, bool includeErrorBars) const {
 2190     return minMax(xColumn(), yColumn(), xErrorType(), xErrorPlusColumn(), xErrorMinusColumn(), indexMin, indexMax, xMin, xMax, includeErrorBars);
 2191 }
 2192 
 2193 /*!
 2194  * Calculates the minimum \p min and maximum \p max of a curve with optionally respecting the error bars
 2195  * This function does not check if the values are out of range
 2196  * \p indexMax is not included
 2197  * \p column
 2198  * \p errorType
 2199  * \p errorPlusColumn
 2200  * \p errorMinusColumn
 2201  * \p indexMin
 2202  * \p indexMax
 2203  * \p min
 2204  * \p max
 2205  * \ includeErrorBars If true respect the error bars in the min/max calculation
 2206  */
 2207 bool XYCurve::minMax(const AbstractColumn* column1, const AbstractColumn* column2, const ErrorType errorType, const AbstractColumn* errorPlusColumn, const AbstractColumn* errorMinusColumn, int indexMin, int indexMax, double& min, double& max, bool includeErrorBars) const {
 2208     // when property is increasing or decreasing there is a benefit in finding minimum and maximum
 2209     // for property == AbstractColumn::Properties::No it must be iterated over all values so it does not matter if this function or the below one is used
 2210     // if the property of the second column is not AbstractColumn::Properties::No means, that all values are valid and not masked
 2211     if ((!includeErrorBars || errorType == ErrorType::NoError) && column1->properties() != AbstractColumn::Properties::No && column2 && column2->properties() != AbstractColumn::Properties::No) {
 2212         min = column1->minimum(indexMin, indexMax);
 2213         max = column1->maximum(indexMin, indexMax);
 2214         return true;
 2215     }
 2216 
 2217     if (column1->rowCount() == 0)
 2218         return false;
 2219 
 2220     min = INFINITY;
 2221     max = -INFINITY;
 2222 
 2223     for (int i = indexMin; i < indexMax; ++i) {
 2224         if (!column1->isValid(i) || column1->isMasked(i) || (column2 && (!column2->isValid(i) || column2->isMasked(i))))
 2225             continue;
 2226 
 2227         if ( (errorPlusColumn && i >= errorPlusColumn->rowCount())
 2228             || (errorMinusColumn && i >= errorMinusColumn->rowCount()) )
 2229             continue;
 2230 
 2231         double value;
 2232         if (column1->columnMode() == AbstractColumn::ColumnMode::Numeric || column1->columnMode() == AbstractColumn::ColumnMode::Integer ||
 2233                 column1->columnMode() == AbstractColumn::ColumnMode::BigInt)
 2234             value = column1->valueAt(i);
 2235         else if (column1->columnMode() == AbstractColumn::ColumnMode::DateTime ||
 2236                  column1->columnMode() == AbstractColumn::ColumnMode::Month ||
 2237                  column1->columnMode() == AbstractColumn::ColumnMode::Day) {
 2238             value = column1->dateTimeAt(i).toMSecsSinceEpoch();
 2239         } else
 2240             return false;
 2241 
 2242         if (errorType == ErrorType::NoError) {
 2243             if (value < min)
 2244                 min = value;
 2245 
 2246             if (value > max)
 2247                 max = value;
 2248         } else {
 2249             //determine the values for the errors
 2250             double errorPlus, errorMinus;
 2251             if (errorPlusColumn && errorPlusColumn->isValid(i) && !errorPlusColumn->isMasked(i))
 2252                 if (errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Numeric ||
 2253                         errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Integer ||
 2254                         errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
 2255                     errorPlus = errorPlusColumn->valueAt(i);
 2256                 else if (errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::DateTime ||
 2257                         errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Month ||
 2258                         errorPlusColumn->columnMode() == AbstractColumn::ColumnMode::Day)
 2259                     errorPlus = errorPlusColumn->dateTimeAt(i).toMSecsSinceEpoch();
 2260                 else
 2261                     return false;
 2262             else
 2263                 errorPlus = 0;
 2264 
 2265             if (errorType == ErrorType::Symmetric)
 2266                 errorMinus = errorPlus;
 2267             else {
 2268                 if (errorMinusColumn && errorMinusColumn->isValid(i) && !errorMinusColumn->isMasked(i))
 2269                     if (errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Numeric ||
 2270                         errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Integer ||
 2271                         errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::BigInt)
 2272                         errorMinus = errorMinusColumn->valueAt(i);
 2273                     else if (errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::DateTime ||
 2274                             errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Month ||
 2275                             errorMinusColumn->columnMode() == AbstractColumn::ColumnMode::Day)
 2276                         errorMinus = errorMinusColumn->dateTimeAt(i).toMSecsSinceEpoch();
 2277                     else
 2278                         return false;
 2279                 else
 2280                     errorMinus = 0;
 2281             }
 2282 
 2283             if (value - errorMinus < min)
 2284                 min = value - errorMinus;
 2285 
 2286             if (value + errorPlus > max)
 2287                 max = value + errorPlus;
 2288         }
 2289     }
 2290     return true;
 2291 }
 2292 
 2293 bool XYCurvePrivate::activateCurve(QPointF mouseScenePos, double maxDist) {
 2294     if (!isVisible())
 2295         return false;
 2296 
 2297     int rowCount{0};
 2298     if (lineType != XYCurve::LineType::NoLine)
 2299         rowCount = m_lines.count();
 2300     else if (symbolsStyle != Symbol::Style::NoSymbols)
 2301         rowCount = m_scenePoints.size();
 2302     else
 2303         return false;
 2304 
 2305     if (rowCount == 0)
 2306         return false;
 2307 
 2308     if (maxDist < 0)
 2309         maxDist = (linePen.width() < 10) ? 10. : linePen.width();
 2310 
 2311     auto properties{q->xColumn()->properties()};
 2312     if (properties == AbstractColumn::Properties::No) {
 2313         // assumption: points exist if no line. otherwise previously returned false
 2314         if (lineType == XYCurve::LineType::NoLine) {
 2315             QPointF curvePosPrevScene = m_scenePoints.at(0);
 2316             QPointF curvePosScene = curvePosPrevScene;
 2317             for (int row = 0; row < rowCount; row ++) {
 2318                 if (gsl_hypot(mouseScenePos.x() - curvePosScene.x(), mouseScenePos.y() - curvePosScene.y()) <= maxDist)
 2319                     return true;
 2320 
 2321                 curvePosPrevScene = curvePosScene;
 2322                 curvePosScene = m_scenePoints.at(row);
 2323             }
 2324         } else {
 2325             for (int row = 0; row < rowCount; row++) {
 2326                 QLineF line = m_lines.at(row);
 2327                 if (pointLiesNearLine(line.p1(), line.p2(), mouseScenePos, maxDist))
 2328                     return true;
 2329             }
 2330         }
 2331 
 2332     } else if (properties == AbstractColumn::Properties::MonotonicIncreasing ||
 2333             properties == AbstractColumn::Properties::MonotonicDecreasing) {
 2334 
 2335         bool increase{true};
 2336         if (properties == AbstractColumn::Properties::MonotonicDecreasing)
 2337             increase = false;
 2338 
 2339         double x{mouseScenePos.x() - maxDist};
 2340         int index{0};
 2341 
 2342         QPointF curvePosScene;
 2343         QPointF curvePosPrevScene;
 2344 
 2345         if (lineType == XYCurve::LineType::NoLine) {
 2346             curvePosScene  = m_scenePoints.at(index);
 2347             curvePosPrevScene = curvePosScene;
 2348             index = Column::indexForValue(x, m_scenePoints, static_cast<AbstractColumn::Properties>(properties));
 2349         } else
 2350             index = Column::indexForValue(x, m_lines, static_cast<AbstractColumn::Properties>(properties));
 2351 
 2352         if (index >= 1)
 2353             index --; // use one before so it is secured that I'm before point.x()
 2354         else if (index == -1)
 2355             return false;
 2356 
 2357         const double xMax{mouseScenePos.x() + maxDist};
 2358         bool stop{false};
 2359         while (true) {
 2360             // assumption: points exist if no line. otherwise previously returned false
 2361             if (lineType == XYCurve::LineType::NoLine) {// check points only if no line otherwise check only the lines
 2362                 if (curvePosScene.x() > xMax)
 2363                     stop = true; // one more time if bigger
 2364                 if (gsl_hypot(mouseScenePos.x()- curvePosScene.x(), mouseScenePos.y()-curvePosScene.y()) <= maxDist)
 2365                     return true;
 2366             } else {
 2367                 if (m_lines.at(index).p1().x() > xMax)
 2368                     stop = true; // one more time if bigger
 2369 
 2370                 QLineF line = m_lines.at(index);
 2371                 if (pointLiesNearLine(line.p1(), line.p2(), mouseScenePos, maxDist))
 2372                     return true;
 2373             }
 2374 
 2375             if (stop || (index >= rowCount - 1 && increase) || (index <= 0 && !increase))
 2376                 break;
 2377 
 2378             if (increase)
 2379                 index++;
 2380             else
 2381                 index--;
 2382 
 2383             if (lineType == XYCurve::LineType::NoLine) {
 2384                 curvePosPrevScene = curvePosScene;
 2385                 curvePosScene = m_scenePoints.at(index);
 2386             }
 2387         }
 2388     }
 2389 
 2390     return false;
 2391 }
 2392 
 2393 /*!
 2394  * \brief XYCurve::pointLiesNearLine
 2395  * Calculates if a point \p pos lies near than maxDist to the line created by the points \p p1 and \p p2
 2396  * https://stackoverflow.com/questions/11604680/point-laying-near-line
 2397  * \p p1 first point of the line
 2398  * \p p2 second point of the line
 2399  * \p pos Position to check
 2400  * \p maxDist Maximal distance away from the curve, which is valid
 2401  * \return Return true if point lies next to the line
 2402  */
 2403 bool XYCurvePrivate::pointLiesNearLine(const QPointF p1, const QPointF p2, const QPointF pos, const double maxDist) const {
 2404     const double dx12{p2.x() - p1.x()};
 2405     const double dy12{p2.y() - p1.y()};
 2406     const double vecLength{gsl_hypot(dx12, dy12)};
 2407 
 2408     const double dx1m{pos.x() - p1.x()};
 2409     const double dy1m{pos.y() - p1.y()};
 2410     if (vecLength == 0) {
 2411         if (gsl_hypot(dx1m, dy1m) <= maxDist)
 2412             return true;
 2413          return false;
 2414     }
 2415     QPointF unitvec(dx12/vecLength, dy12/vecLength);
 2416 
 2417     const double dist_segm{std::abs(dx1m*unitvec.y() - dy1m*unitvec.x())};
 2418     const double scalarProduct{dx1m*unitvec.x() + dy1m*unitvec.y()};
 2419 
 2420     if (scalarProduct > 0) {
 2421         if (scalarProduct < vecLength && dist_segm < maxDist)
 2422             return true;
 2423     }
 2424     return false;
 2425 }
 2426 
 2427 // TODO: curvePosScene.x() >= mouseScenePos.x() &&
 2428 // curvePosPrevScene.x() < mouseScenePos.x()
 2429 // should not be here
 2430 bool XYCurvePrivate::pointLiesNearCurve(const QPointF mouseScenePos, const QPointF curvePosPrevScene, const QPointF curvePosScene, const int index, const double maxDist) const {
 2431     if (q->lineType() != XYCurve::LineType::NoLine &&
 2432             curvePosScene.x() >= mouseScenePos.x() &&
 2433             curvePosPrevScene.x() < mouseScenePos.x()) {
 2434 
 2435         if (q->lineType() == XYCurve::LineType::Line) {
 2436             // point is not in the near of the point, but it can be in the near of the connection line of two points
 2437             if (pointLiesNearLine(curvePosPrevScene,curvePosScene, mouseScenePos, maxDist))
 2438                 return true;
 2439         } else if (q->lineType() == XYCurve::LineType::StartHorizontal) {
 2440             QPointF tempPoint = curvePosPrevScene;
 2441             tempPoint.setX(curvePosScene.x());
 2442             if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
 2443                 return true;
 2444             if (pointLiesNearLine(tempPoint,curvePosScene, mouseScenePos, maxDist))
 2445                 return true;
 2446         } else if (q->lineType() == XYCurve::LineType::StartVertical) {
 2447             QPointF tempPoint = curvePosPrevScene;
 2448             tempPoint.setY(curvePosScene.y());
 2449             if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
 2450                 return true;
 2451             if (pointLiesNearLine(tempPoint,curvePosScene, mouseScenePos, maxDist))
 2452                 return true;
 2453         } else if (q->lineType() == XYCurve::LineType::MidpointHorizontal) {
 2454             QPointF tempPoint = curvePosPrevScene;
 2455             tempPoint.setX(curvePosPrevScene.x()+(curvePosScene.x()-curvePosPrevScene.x())/2);
 2456             if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
 2457                 return true;
 2458             QPointF tempPoint2(tempPoint.x(), curvePosScene.y());
 2459             if (pointLiesNearLine(tempPoint,tempPoint2, mouseScenePos, maxDist))
 2460                 return true;
 2461 
 2462             if (pointLiesNearLine(tempPoint2,curvePosScene, mouseScenePos, maxDist))
 2463                 return true;
 2464         } else if (q->lineType() == XYCurve::LineType::MidpointVertical) {
 2465             QPointF tempPoint = curvePosPrevScene;
 2466             tempPoint.setY(curvePosPrevScene.y()+(curvePosScene.y()-curvePosPrevScene.y())/2);
 2467             if (pointLiesNearLine(curvePosPrevScene,tempPoint, mouseScenePos, maxDist))
 2468                 return true;
 2469             QPointF tempPoint2(tempPoint.y(), curvePosScene.x());
 2470             if (pointLiesNearLine(tempPoint,tempPoint2, mouseScenePos, maxDist))
 2471                 return true;
 2472 
 2473             if (pointLiesNearLine(tempPoint2,curvePosScene, mouseScenePos, maxDist))
 2474                 return true;
 2475         } else if (q->lineType() == XYCurve::LineType::SplineAkimaNatural ||
 2476                    q->lineType() == XYCurve::LineType::SplineCubicNatural ||
 2477                    q->lineType() == XYCurve::LineType::SplineAkimaPeriodic ||
 2478                    q->lineType() == XYCurve::LineType::SplineCubicPeriodic) {
 2479             for (int i = 0; i < q->lineInterpolationPointsCount() + 1; i++) {
 2480                 QLineF line = m_lines.at(index*(q->lineInterpolationPointsCount()+1)+i);
 2481                 QPointF p1{line.p1()}; //cSystem->mapLogicalToScene(line.p1());
 2482                 QPointF p2{line.p2()}; //cSystem->mapLogicalToScene(line.p2());
 2483                 if (pointLiesNearLine(p1, p2, mouseScenePos, maxDist))
 2484                     return true;
 2485             }
 2486         } else {
 2487             // point is not in the near of the point, but it can be in the near of the connection line of two points
 2488             if (pointLiesNearLine(curvePosPrevScene, curvePosScene, mouseScenePos, maxDist))
 2489                 return true;
 2490         }
 2491     }
 2492     return false;
 2493 }
 2494 
 2495 void XYCurvePrivate::updateErrorBars() {
 2496     errorBarsPath = QPainterPath();
 2497     if (xErrorType == XYCurve::ErrorType::NoError && yErrorType == XYCurve::ErrorType::NoError) {
 2498         recalcShapeAndBoundingRect();
 2499         return;
 2500     }
 2501 
 2502     QVector<QLineF> elines;
 2503     QVector<QPointF> pointsErrorBarAnchorX;
 2504     QVector<QPointF> pointsErrorBarAnchorY;
 2505 
 2506     for (int i = 0; i < m_logicalPoints.size(); ++i) {
 2507         if (!m_pointVisible.at(i))
 2508             continue;
 2509 
 2510         const QPointF& point{m_logicalPoints.at(i)};
 2511         const int index{validPointsIndicesLogical.at(i)};
 2512         double errorPlus, errorMinus;
 2513 
 2514         //error bars for x
 2515         if (xErrorType != XYCurve::ErrorType::NoError) {
 2516             //determine the values for the errors
 2517             if (xErrorPlusColumn && xErrorPlusColumn->isValid(index) && !xErrorPlusColumn->isMasked(index))
 2518                 errorPlus = xErrorPlusColumn->valueAt(index);
 2519             else
 2520                 errorPlus = 0;
 2521 
 2522             if (xErrorType == XYCurve::ErrorType::Symmetric)
 2523                 errorMinus = errorPlus;
 2524             else {
 2525                 if (xErrorMinusColumn && xErrorMinusColumn->isValid(index) && !xErrorMinusColumn->isMasked(index))
 2526                     errorMinus = xErrorMinusColumn->valueAt(index);
 2527                 else
 2528                     errorMinus = 0;
 2529             }
 2530 
 2531             //draw the error bars
 2532             if (errorMinus != 0 || errorPlus != 0)
 2533                 elines.append(QLineF(QPointF(point.x()-errorMinus, point.y()),
 2534                                     QPointF(point.x()+errorPlus, point.y())));
 2535 
 2536             //determine the end points of the errors bars in logical coordinates to draw later the cap
 2537             if (errorBarsType == XYCurve::ErrorBarsType::WithEnds) {
 2538                 if (errorMinus != 0)
 2539                     pointsErrorBarAnchorX << QPointF(point.x() - errorMinus, point.y());
 2540                 if (errorPlus != 0)
 2541                     pointsErrorBarAnchorX << QPointF(point.x() + errorPlus, point.y());
 2542             }
 2543         }
 2544 
 2545         //error bars for y
 2546         if (yErrorType != XYCurve::ErrorType::NoError) {
 2547             //determine the values for the errors
 2548             if (yErrorPlusColumn && yErrorPlusColumn->isValid(index) && !yErrorPlusColumn->isMasked(index))
 2549                 errorPlus = yErrorPlusColumn->valueAt(index);
 2550             else
 2551                 errorPlus = 0;
 2552 
 2553             if (yErrorType == XYCurve::ErrorType::Symmetric)
 2554                 errorMinus = errorPlus;
 2555             else {
 2556                 if (yErrorMinusColumn && yErrorMinusColumn->isValid(index) && !yErrorMinusColumn->isMasked(index) )
 2557                     errorMinus = yErrorMinusColumn->valueAt(index);
 2558                 else
 2559                     errorMinus = 0;
 2560             }
 2561 
 2562             //draw the error bars
 2563             if (errorMinus != 0 || errorPlus != 0)
 2564                 elines.append(QLineF(QPointF(point.x(), point.y() + errorPlus),
 2565                                     QPointF(point.x(), point.y() - errorMinus)));
 2566 
 2567             //determine the end points of the errors bars in logical coordinates to draw later the cap
 2568             if (errorBarsType == XYCurve::ErrorBarsType::WithEnds) {
 2569                 if (errorMinus != 0)
 2570                     pointsErrorBarAnchorY << QPointF(point.x(), point.y() + errorPlus);
 2571                 if (errorPlus != 0)
 2572                     pointsErrorBarAnchorY << QPointF(point.x(), point.y() - errorMinus);
 2573             }
 2574         }
 2575     }
 2576 
 2577     //map the error bars to scene coordinates
 2578     elines = cSystem->mapLogicalToScene(elines);
 2579 
 2580     //new painter path for the error bars
 2581     for (const auto& line : qAsConst(elines)) {
 2582         errorBarsPath.moveTo(line.p1());
 2583         errorBarsPath.lineTo(line.p2());
 2584     }
 2585 
 2586     //add caps for x error bars
 2587     if (!pointsErrorBarAnchorX.isEmpty()) {
 2588         pointsErrorBarAnchorX = cSystem->mapLogicalToScene(pointsErrorBarAnchorX);
 2589         for (const auto& point : qAsConst(pointsErrorBarAnchorX)) {
 2590             errorBarsPath.moveTo(QPointF(point.x(), point.y() - errorBarsCapSize/2.));
 2591             errorBarsPath.lineTo(QPointF(point.x(), point.y() + errorBarsCapSize/2.));
 2592         }
 2593     }
 2594 
 2595     //add caps for y error bars
 2596     if (!pointsErrorBarAnchorY.isEmpty()) {
 2597         pointsErrorBarAnchorY = cSystem->mapLogicalToScene(pointsErrorBarAnchorY);
 2598         for (const auto& point : qAsConst(pointsErrorBarAnchorY)) {
 2599             errorBarsPath.moveTo(QPointF(point.x() - errorBarsCapSize/2., point.y()));
 2600             errorBarsPath.lineTo(QPointF(point.x() + errorBarsCapSize/2., point.y()));
 2601         }
 2602     }
 2603 
 2604     recalcShapeAndBoundingRect();
 2605 }
 2606 
 2607 /*!
 2608   recalculates the outer bounds and the shape of the curve.
 2609 */
 2610 void XYCurvePrivate::recalcShapeAndBoundingRect() {
 2611     DEBUG(Q_FUNC_INFO << ", m_suppressRecalc = " << m_suppressRecalc);
 2612     if (m_suppressRecalc)
 2613         return;
 2614 
 2615 #ifdef PERFTRACE_CURVES
 2616     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::recalcShapeAndBoundingRect()");
 2617 #endif
 2618 
 2619     prepareGeometryChange();
 2620     curveShape = QPainterPath();
 2621     if (lineType != XYCurve::LineType::NoLine)
 2622         curveShape.addPath(WorksheetElement::shapeFromPath(linePath, linePen));
 2623 
 2624     if (dropLineType != XYCurve::DropLineType::NoDropLine)
 2625         curveShape.addPath(WorksheetElement::shapeFromPath(dropLinePath, dropLinePen));
 2626 
 2627     if (symbolsStyle != Symbol::Style::NoSymbols)
 2628         curveShape.addPath(symbolsPath);
 2629 
 2630     if (valuesType != XYCurve::ValuesType::NoValues)
 2631         curveShape.addPath(valuesPath);
 2632 
 2633     if (xErrorType != XYCurve::ErrorType::NoError || yErrorType != XYCurve::ErrorType::NoError)
 2634         curveShape.addPath(WorksheetElement::shapeFromPath(errorBarsPath, errorBarsPen));
 2635 
 2636     boundingRectangle = curveShape.boundingRect();
 2637 
 2638     for (const auto& pol : qAsConst(m_fillPolygons))
 2639         boundingRectangle = boundingRectangle.united(pol.boundingRect());
 2640 
 2641     //TODO: when the selection is painted, line intersections are visible.
 2642     //simplified() removes those artifacts but is horrible slow for curves with large number of points.
 2643     //search for an alternative.
 2644     //curveShape = curveShape.simplified();
 2645 
 2646     updatePixmap();
 2647 }
 2648 
 2649 void XYCurvePrivate::draw(QPainter* painter) {
 2650 #ifdef PERFTRACE_CURVES
 2651     PERFTRACE(name().toLatin1() + ", XYCurvePrivate::draw()");
 2652 #endif
 2653 
 2654     //draw filling
 2655     if (fillingPosition != XYCurve::FillingPosition::NoFilling) {
 2656         painter->setOpacity(fillingOpacity);
 2657         painter->setPen(Qt::SolidLine);
 2658         drawFilling(painter);
 2659     }
 2660 
 2661     //draw lines
 2662     if (lineType != XYCurve::LineType::NoLine) {
 2663         painter->setOpacity(lineOpacity);
 2664         painter->setPen(linePen);
 2665         painter->setBrush(Qt::NoBrush);
 2666         painter->drawPath(linePath);
 2667     }
 2668 
 2669     //draw drop lines
 2670     if (dropLineType != XYCurve::DropLineType::NoDropLine) {
 2671         painter->setOpacity(dropLineOpacity);
 2672         painter->setPen(dropLinePen);
 2673         painter->setBrush(Qt::NoBrush);
 2674         painter->drawPath(dropLinePath);
 2675     }
 2676 
 2677     //draw error bars
 2678     if ( (xErrorType != XYCurve::ErrorType::NoError) || (yErrorType != XYCurve::ErrorType::NoError) ) {
 2679         painter->setOpacity(errorBarsOpacity);
 2680         painter->setPen(errorBarsPen);
 2681         painter->setBrush(Qt::NoBrush);
 2682         painter->drawPath(errorBarsPath);
 2683     }
 2684 
 2685     //draw symbols
 2686     if (symbolsStyle != Symbol::Style::NoSymbols) {
 2687         painter->setOpacity(symbolsOpacity);
 2688         painter->setPen(symbolsPen);
 2689         painter->setBrush(symbolsBrush);
 2690         drawSymbols(painter);
 2691     }
 2692 
 2693     //draw values
 2694     if (valuesType != XYCurve::ValuesType::NoValues) {
 2695         painter->setOpacity(valuesOpacity);
 2696         painter->setPen(QPen(valuesColor));
 2697         painter->setFont(valuesFont);
 2698         drawValues(painter);
 2699     }
 2700 }
 2701 
 2702 void XYCurvePrivate::updatePixmap() {
 2703     DEBUG(Q_FUNC_INFO << ", m_suppressRecalc = " << m_suppressRecalc);
 2704     if (m_suppressRecalc)
 2705         return;
 2706 
 2707     WAIT_CURSOR;
 2708 
 2709     m_hoverEffectImageIsDirty = true;
 2710     m_selectionEffectImageIsDirty = true;
 2711     if (boundingRectangle.width() == 0 || boundingRectangle.height() == 0) {
 2712         DEBUG(Q_FUNC_INFO << ", boundingRectangle.width() or boundingRectangle.height() == 0");
 2713         m_pixmap = QPixmap();
 2714         RESET_CURSOR;
 2715         return;
 2716     }
 2717     QPixmap pixmap(ceil(boundingRectangle.width()), ceil(boundingRectangle.height()));
 2718     pixmap.fill(Qt::transparent);
 2719     QPainter painter(&pixmap);
 2720     painter.setRenderHint(QPainter::Antialiasing, true);
 2721     painter.translate(-boundingRectangle.topLeft());
 2722 
 2723     draw(&painter);
 2724     painter.end();
 2725     m_pixmap = pixmap;
 2726 
 2727     update();
 2728     RESET_CURSOR;
 2729 }
 2730 
 2731 /*!
 2732   Reimplementation of QGraphicsItem::paint(). This function does the actual painting of the curve.
 2733   \sa QGraphicsItem::paint().
 2734 */
 2735 void XYCurvePrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
 2736     Q_UNUSED(option);
 2737     Q_UNUSED(widget);
 2738     if (!isVisible())
 2739         return;
 2740 
 2741     painter->setPen(Qt::NoPen);
 2742     painter->setBrush(Qt::NoBrush);
 2743     painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
 2744 
 2745     if ( !m_printing && KSharedConfig::openConfig()->group("Settings_Worksheet").readEntry<bool>("DoubleBuffering", true) )
 2746         painter->drawPixmap(boundingRectangle.topLeft(), m_pixmap); //draw the cached pixmap (fast)
 2747     else
 2748         draw(painter); //draw directly again (slow)
 2749 
 2750 
 2751     if (m_hovered && !isSelected() && !m_printing) {
 2752         if (m_hoverEffectImageIsDirty) {
 2753             QPixmap pix = m_pixmap;
 2754             QPainter p(&pix);
 2755             p.setCompositionMode(QPainter::CompositionMode_SourceIn);   // source (shadow) pixels merged with the alpha channel of the destination (m_pixmap)
 2756             p.fillRect(pix.rect(), QApplication::palette().color(QPalette::Shadow));
 2757             p.end();
 2758 
 2759             m_hoverEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5);
 2760             m_hoverEffectImageIsDirty = false;
 2761         }
 2762 
 2763         painter->drawImage(boundingRectangle.topLeft(), m_hoverEffectImage, m_pixmap.rect());
 2764         return;
 2765     }
 2766 
 2767     if (isSelected() && !m_printing) {
 2768         if (m_selectionEffectImageIsDirty) {
 2769             QPixmap pix = m_pixmap;
 2770             QPainter p(&pix);
 2771             p.setCompositionMode(QPainter::CompositionMode_SourceIn);
 2772             p.fillRect(pix.rect(), QApplication::palette().color(QPalette::Highlight));
 2773             p.end();
 2774 
 2775             m_selectionEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5);
 2776             m_selectionEffectImageIsDirty = false;
 2777         }
 2778 
 2779         painter->drawImage(boundingRectangle.topLeft(), m_selectionEffectImage, m_pixmap.rect());
 2780     }
 2781 }
 2782 
 2783 /*!
 2784     Drawing of symbolsPath is very slow, so we draw every symbol in the loop which is much faster (factor 10)
 2785 */
 2786 void XYCurvePrivate::drawSymbols(QPainter* painter) {
 2787     QPainterPath path = Symbol::pathFromStyle(symbolsStyle);
 2788 
 2789     QTransform trafo;
 2790     trafo.scale(symbolsSize, symbolsSize);
 2791 
 2792     if (symbolsRotationAngle != 0)
 2793         trafo.rotate(-symbolsRotationAngle);
 2794 
 2795     path = trafo.map(path);
 2796 
 2797     for (const auto& point : qAsConst(m_scenePoints)) {
 2798         trafo.reset();
 2799         trafo.translate(point.x(), point.y());
 2800         painter->drawPath(trafo.map(path));
 2801     }
 2802 }
 2803 
 2804 void XYCurvePrivate::drawValues(QPainter* painter) {
 2805     int i = 0;
 2806     for (const auto& point : qAsConst(m_valuePoints)) {
 2807         painter->translate(point);
 2808         if (valuesRotationAngle != 0)
 2809             painter->rotate(-valuesRotationAngle);
 2810 
 2811         painter->drawText(QPoint(0, 0), m_valueStrings.at(i++));
 2812 
 2813         if (valuesRotationAngle != 0)
 2814             painter->rotate(valuesRotationAngle);
 2815         painter->translate(-point);
 2816     }
 2817 }
 2818 
 2819 void XYCurvePrivate::drawFilling(QPainter* painter) {
 2820     for (const auto& pol : qAsConst(m_fillPolygons)) {
 2821         QRectF rect = pol.boundingRect();
 2822         if (fillingType == PlotArea::BackgroundType::Color) {
 2823             switch (fillingColorStyle) {
 2824             case PlotArea::BackgroundColorStyle::SingleColor: {
 2825                     painter->setBrush(QBrush(fillingFirstColor));
 2826                     break;
 2827                 }
 2828             case PlotArea::BackgroundColorStyle::HorizontalLinearGradient: {
 2829                     QLinearGradient linearGrad(rect.topLeft(), rect.topRight());
 2830                     linearGrad.setColorAt(0, fillingFirstColor);
 2831                     linearGrad.setColorAt(1, fillingSecondColor);
 2832                     painter->setBrush(QBrush(linearGrad));
 2833                     break;
 2834                 }
 2835             case PlotArea::BackgroundColorStyle::VerticalLinearGradient: {
 2836                     QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft());
 2837                     linearGrad.setColorAt(0, fillingFirstColor);
 2838                     linearGrad.setColorAt(1, fillingSecondColor);
 2839                     painter->setBrush(QBrush(linearGrad));
 2840                     break;
 2841                 }
 2842             case PlotArea::BackgroundColorStyle::TopLeftDiagonalLinearGradient: {
 2843                     QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight());
 2844                     linearGrad.setColorAt(0, fillingFirstColor);
 2845                     linearGrad.setColorAt(1, fillingSecondColor);
 2846                     painter->setBrush(QBrush(linearGrad));
 2847                     break;
 2848                 }
 2849             case PlotArea::BackgroundColorStyle::BottomLeftDiagonalLinearGradient: {
 2850                     QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight());
 2851                     linearGrad.setColorAt(0, fillingFirstColor);
 2852                     linearGrad.setColorAt(1, fillingSecondColor);
 2853                     painter->setBrush(QBrush(linearGrad));
 2854                     break;
 2855                 }
 2856             case PlotArea::BackgroundColorStyle::RadialGradient: {
 2857                     QRadialGradient radialGrad(rect.center(), rect.width()/2);
 2858                     radialGrad.setColorAt(0, fillingFirstColor);
 2859                     radialGrad.setColorAt(1, fillingSecondColor);
 2860                     painter->setBrush(QBrush(radialGrad));
 2861                     break;
 2862                 }
 2863             }
 2864         } else if (fillingType == PlotArea::BackgroundType::Image) {
 2865             if ( !fillingFileName.trimmed().isEmpty() ) {
 2866                 QPixmap pix(fillingFileName);
 2867                 switch (fillingImageStyle) {
 2868                 case PlotArea::BackgroundImageStyle::ScaledCropped:
 2869                     pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
 2870                     painter->setBrush(QBrush(pix));
 2871                     painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
 2872                     break;
 2873                 case PlotArea::BackgroundImageStyle::Scaled:
 2874                     pix = pix.scaled(rect.size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
 2875                     painter->setBrush(QBrush(pix));
 2876                     painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
 2877                     break;
 2878                 case PlotArea::BackgroundImageStyle::ScaledAspectRatio:
 2879                     pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
 2880                     painter->setBrush(QBrush(pix));
 2881                     painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
 2882                     break;
 2883                 case PlotArea::BackgroundImageStyle::Centered: {
 2884                         QPixmap backpix(rect.size().toSize());
 2885                         backpix.fill();
 2886                         QPainter p(&backpix);
 2887                         p.drawPixmap(QPointF(0, 0), pix);
 2888                         p.end();
 2889                         painter->setBrush(QBrush(backpix));
 2890                         painter->setBrushOrigin(-pix.size().width()/2, -pix.size().height()/2);
 2891                         break;
 2892                     }
 2893                 case PlotArea::BackgroundImageStyle::Tiled:
 2894                     painter->setBrush(QBrush(pix));
 2895                     break;
 2896                 case PlotArea::BackgroundImageStyle::CenterTiled:
 2897                     painter->setBrush(QBrush(pix));
 2898                     painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2);
 2899                 }
 2900             }
 2901         } else if (fillingType == PlotArea::BackgroundType::Pattern)
 2902             painter->setBrush(QBrush(fillingFirstColor, fillingBrushStyle));
 2903 
 2904         painter->drawPolygon(pol);
 2905     }
 2906 }
 2907 
 2908 void XYCurvePrivate::setPrinting(bool on) {
 2909     m_printing = on;
 2910 }
 2911 
 2912 void XYCurvePrivate::suppressRetransform(bool on) {
 2913     m_suppressRetransform = on;
 2914     m_suppressRecalc = on;
 2915 }
 2916 
 2917 /*!
 2918  * checks if the mousePress event was done near the histogram shape
 2919  * and selects the graphics item if it is the case.
 2920  * \p event
 2921  */
 2922 void XYCurvePrivate::mousePressEvent(QGraphicsSceneMouseEvent* event) {
 2923     if (plot->mouseMode() != CartesianPlot::MouseMode::Selection) {
 2924         event->ignore();
 2925         return QGraphicsItem::mousePressEvent(event);
 2926     }
 2927 
 2928     if(q->activateCurve(event->pos())){
 2929         setSelected(true);
 2930         return;
 2931     }
 2932 
 2933     event->ignore();
 2934     setSelected(false);
 2935     QGraphicsItem::mousePressEvent(event);
 2936 }
 2937 
 2938 /*!
 2939  * Is called in CartesianPlot::hoverMoveEvent where it is determined which curve to hover.
 2940  * \p on
 2941  */
 2942 void XYCurvePrivate::setHover(bool on) {
 2943     if(on == m_hovered)
 2944         return; // don't update if state not changed
 2945 
 2946     m_hovered = on;
 2947     on ? emit q->hovered() : emit q->unhovered();
 2948     update();
 2949 }
 2950 
 2951 //##############################################################################
 2952 //##################  Serialization/Deserialization  ###########################
 2953 //##############################################################################
 2954 //! Save as XML
 2955 void XYCurve::save(QXmlStreamWriter* writer) const {
 2956     Q_D(const XYCurve);
 2957 
 2958     writer->writeStartElement( "xyCurve" );
 2959     writeBasicAttributes( writer );
 2960     writeCommentElement( writer );
 2961 
 2962     //general
 2963     writer->writeStartElement( "general" );
 2964     WRITE_COLUMN(d->xColumn, xColumn);
 2965     WRITE_COLUMN(d->yColumn, yColumn);
 2966     writer->writeAttribute( "visible", QString::number(d->isVisible()) );
 2967     writer->writeEndElement();
 2968 
 2969     //Line
 2970     writer->writeStartElement( "lines" );
 2971     writer->writeAttribute( "type", QString::number(static_cast<int>(d->lineType)) );
 2972     writer->writeAttribute( "skipGaps", QString::number(d->lineSkipGaps) );
 2973     writer->writeAttribute( "increasingXOnly", QString::number(d->lineIncreasingXOnly) );
 2974     writer->writeAttribute( "interpolationPointsCount", QString::number(d->lineInterpolationPointsCount) );
 2975     WRITE_QPEN(d->linePen);
 2976     writer->writeAttribute( "opacity", QString::number(d->lineOpacity) );
 2977     writer->writeEndElement();
 2978 
 2979     //Drop lines
 2980     writer->writeStartElement( "dropLines" );
 2981     writer->writeAttribute( "type", QString::number(static_cast<int>(d->dropLineType)) );
 2982     WRITE_QPEN(d->dropLinePen);
 2983     writer->writeAttribute( "opacity", QString::number(d->dropLineOpacity) );
 2984     writer->writeEndElement();
 2985 
 2986     //Symbols
 2987     writer->writeStartElement( "symbols" );
 2988     writer->writeAttribute( "symbolsStyle", QString::number(static_cast<int>(d->symbolsStyle)) );
 2989     writer->writeAttribute( "opacity", QString::number(d->symbolsOpacity) );
 2990     writer->writeAttribute( "rotation", QString::number(d->symbolsRotationAngle) );
 2991     writer->writeAttribute( "size", QString::number(d->symbolsSize) );
 2992     WRITE_QBRUSH(d->symbolsBrush);
 2993     WRITE_QPEN(d->symbolsPen);
 2994     writer->writeEndElement();
 2995 
 2996     //Values
 2997     writer->writeStartElement( "values" );
 2998     writer->writeAttribute( "type", QString::number(static_cast<int>(d->valuesType)) );
 2999     WRITE_COLUMN(d->valuesColumn, valuesColumn);
 3000     writer->writeAttribute( "position", QString::number(static_cast<int>(d->valuesPosition)) );
 3001     writer->writeAttribute( "distance", QString::number(d->valuesDistance) );
 3002     writer->writeAttribute( "rotation", QString::number(d->valuesRotationAngle) );
 3003     writer->writeAttribute( "opacity", QString::number(d->valuesOpacity) );
 3004     writer->writeAttribute("numericFormat", QString(d->valuesNumericFormat));
 3005     writer->writeAttribute("dateTimeFormat", d->valuesDateTimeFormat);
 3006     writer->writeAttribute( "precision", QString::number(d->valuesPrecision) );
 3007     writer->writeAttribute( "prefix", d->valuesPrefix );
 3008     writer->writeAttribute( "suffix", d->valuesSuffix );
 3009     WRITE_QCOLOR(d->valuesColor);
 3010     WRITE_QFONT(d->valuesFont);
 3011     writer->writeEndElement();
 3012 
 3013     //Filling
 3014     writer->writeStartElement( "filling" );
 3015     writer->writeAttribute( "position", QString::number(static_cast<int>(d->fillingPosition)) );
 3016     writer->writeAttribute( "type", QString::number(static_cast<int>(d->fillingType)) );
 3017     writer->writeAttribute( "colorStyle", QString::number(static_cast<int>(d->fillingColorStyle)) );
 3018     writer->writeAttribute( "imageStyle", QString::number(static_cast<int>(d->fillingImageStyle)) );
 3019     writer->writeAttribute( "brushStyle", QString::number(d->fillingBrushStyle) );
 3020     writer->writeAttribute( "firstColor_r", QString::number(d->fillingFirstColor.red()) );
 3021     writer->writeAttribute( "firstColor_g", QString::number(d->fillingFirstColor.green()) );
 3022     writer->writeAttribute( "firstColor_b", QString::number(d->fillingFirstColor.blue()) );
 3023     writer->writeAttribute( "secondColor_r", QString::number(d->fillingSecondColor.red()) );
 3024     writer->writeAttribute( "secondColor_g", QString::number(d->fillingSecondColor.green()) );
 3025     writer->writeAttribute( "secondColor_b", QString::number(d->fillingSecondColor.blue()) );
 3026     writer->writeAttribute( "fileName", d->fillingFileName );
 3027     writer->writeAttribute( "opacity", QString::number(d->fillingOpacity) );
 3028     writer->writeEndElement();
 3029 
 3030     //Error bars
 3031     writer->writeStartElement( "errorBars" );
 3032     writer->writeAttribute( "xErrorType", QString::number(static_cast<int>(d->xErrorType)) );
 3033     WRITE_COLUMN(d->xErrorPlusColumn, xErrorPlusColumn);
 3034     WRITE_COLUMN(d->xErrorMinusColumn, xErrorMinusColumn);
 3035     writer->writeAttribute( "yErrorType", QString::number(static_cast<int>(d->yErrorType)) );
 3036     WRITE_COLUMN(d->yErrorPlusColumn, yErrorPlusColumn);
 3037     WRITE_COLUMN(d->yErrorMinusColumn, yErrorMinusColumn);
 3038     writer->writeAttribute( "type", QString::number(static_cast<int>(d->errorBarsType)) );
 3039     writer->writeAttribute( "capSize", QString::number(d->errorBarsCapSize) );
 3040     WRITE_QPEN(d->errorBarsPen);
 3041     writer->writeAttribute( "opacity", QString::number(d->errorBarsOpacity) );
 3042     writer->writeEndElement();
 3043 
 3044     writer->writeEndElement(); //close "xyCurve" section
 3045 }
 3046 
 3047 //! Load from XML
 3048 bool XYCurve::load(XmlStreamReader* reader, bool preview) {
 3049     Q_D(XYCurve);
 3050 
 3051     if (!readBasicAttributes(reader))
 3052         return false;
 3053 
 3054     KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
 3055     QXmlStreamAttributes attribs;
 3056     QString str;
 3057 
 3058     while (!reader->atEnd()) {
 3059         reader->readNext();
 3060         if (reader->isEndElement() && reader->name() == "xyCurve")
 3061             break;
 3062 
 3063         if (!reader->isStartElement())
 3064             continue;
 3065 
 3066         if (reader->name() == "comment") {
 3067             if (!readCommentElement(reader)) return false;
 3068         } else if (reader->name() == "general") {
 3069             attribs = reader->attributes();
 3070             READ_COLUMN(xColumn);
 3071             READ_COLUMN(yColumn);
 3072 
 3073             str = attribs.value("visible").toString();
 3074             if (str.isEmpty())
 3075                 reader->raiseWarning(attributeWarning.subs("visible").toString());
 3076             else
 3077                 d->setVisible(str.toInt());
 3078         } else if (!preview && reader->name() == "lines") {
 3079             attribs = reader->attributes();
 3080 
 3081             READ_INT_VALUE("type", lineType, LineType);
 3082             READ_INT_VALUE("skipGaps", lineSkipGaps, bool);
 3083             READ_INT_VALUE("increasingXOnly", lineIncreasingXOnly, bool);
 3084             READ_INT_VALUE("interpolationPointsCount", lineInterpolationPointsCount, int);
 3085             READ_QPEN(d->linePen);
 3086             READ_DOUBLE_VALUE("opacity", lineOpacity);
 3087         } else if (!preview && reader->name() == "dropLines") {
 3088             attribs = reader->attributes();
 3089 
 3090             READ_INT_VALUE("type", dropLineType, DropLineType);
 3091             READ_QPEN(d->dropLinePen);
 3092             READ_DOUBLE_VALUE("opacity", dropLineOpacity);
 3093 
 3094         } else if (!preview && reader->name() == "symbols") {
 3095             attribs = reader->attributes();
 3096 
 3097             READ_INT_VALUE("symbolsStyle", symbolsStyle, Symbol::Style);
 3098             READ_DOUBLE_VALUE("opacity", symbolsOpacity);
 3099             READ_DOUBLE_VALUE("rotation", symbolsRotationAngle);
 3100             READ_DOUBLE_VALUE("size", symbolsSize);
 3101 
 3102             READ_QBRUSH(d->symbolsBrush);
 3103             READ_QPEN(d->symbolsPen);
 3104         } else if (!preview && reader->name() == "values") {
 3105             attribs = reader->attributes();
 3106 
 3107             READ_INT_VALUE("type", valuesType, ValuesType);
 3108             READ_COLUMN(valuesColumn);
 3109 
 3110             READ_INT_VALUE("position", valuesPosition, ValuesPosition);
 3111             READ_DOUBLE_VALUE("distance", valuesDistance);
 3112             READ_DOUBLE_VALUE("rotation", valuesRotationAngle);
 3113             READ_DOUBLE_VALUE("opacity", valuesOpacity);
 3114 
 3115             str = attribs.value("numericFormat").toString();
 3116             if (str.isEmpty())
 3117                 reader->raiseWarning(attributeWarning.subs("numericFormat").toString());
 3118             else
 3119                 d->valuesNumericFormat = *(str.toLatin1().data());
 3120 
 3121             READ_STRING_VALUE("dateTimeFormat", valuesDateTimeFormat);
 3122             READ_INT_VALUE("precision", valuesPrecision, int);
 3123 
 3124             //don't produce any warning if no prefix or suffix is set (empty string is allowed here in xml)
 3125             d->valuesPrefix = attribs.value("prefix").toString();
 3126             d->valuesSuffix = attribs.value("suffix").toString();
 3127 
 3128             READ_QCOLOR(d->valuesColor);
 3129             READ_QFONT(d->valuesFont);
 3130         } else if (!preview && reader->name() == "filling") {
 3131             attribs = reader->attributes();
 3132 
 3133             READ_INT_VALUE("position", fillingPosition, FillingPosition);
 3134             READ_INT_VALUE("type", fillingType, PlotArea::BackgroundType);
 3135             READ_INT_VALUE("colorStyle", fillingColorStyle, PlotArea::BackgroundColorStyle);
 3136             READ_INT_VALUE("imageStyle", fillingImageStyle, PlotArea::BackgroundImageStyle );
 3137             READ_INT_VALUE("brushStyle", fillingBrushStyle, Qt::BrushStyle);
 3138 
 3139             str = attribs.value("firstColor_r").toString();
 3140             if (str.isEmpty())
 3141                 reader->raiseWarning(attributeWarning.subs("firstColor_r").toString());
 3142             else
 3143                 d->fillingFirstColor.setRed(str.toInt());
 3144 
 3145             str = attribs.value("firstColor_g").toString();
 3146             if (str.isEmpty())
 3147                 reader->raiseWarning(attributeWarning.subs("firstColor_g").toString());
 3148             else
 3149                 d->fillingFirstColor.setGreen(str.toInt());
 3150 
 3151             str = attribs.value("firstColor_b").toString();
 3152             if (str.isEmpty())
 3153                 reader->raiseWarning(attributeWarning.subs("firstColor_b").toString());
 3154             else
 3155                 d->fillingFirstColor.setBlue(str.toInt());
 3156 
 3157             str = attribs.value("secondColor_r").toString();
 3158             if (str.isEmpty())
 3159                 reader->raiseWarning(attributeWarning.subs("secondColor_r").toString());
 3160             else
 3161                 d->fillingSecondColor.setRed(str.toInt());
 3162 
 3163             str = attribs.value("secondColor_g").toString();
 3164             if (str.isEmpty())
 3165                 reader->raiseWarning(attributeWarning.subs("secondColor_g").toString());
 3166             else
 3167                 d->fillingSecondColor.setGreen(str.toInt());
 3168 
 3169             str = attribs.value("secondColor_b").toString();
 3170             if (str.isEmpty())
 3171                 reader->raiseWarning(attributeWarning.subs("secondColor_b").toString());
 3172             else
 3173                 d->fillingSecondColor.setBlue(str.toInt());
 3174 
 3175             READ_STRING_VALUE("fileName", fillingFileName);
 3176             READ_DOUBLE_VALUE("opacity", fillingOpacity);
 3177         } else if (!preview && reader->name() == "errorBars") {
 3178             attribs = reader->attributes();
 3179 
 3180             READ_INT_VALUE("xErrorType", xErrorType, ErrorType);
 3181             READ_COLUMN(xErrorPlusColumn);
 3182             READ_COLUMN(xErrorMinusColumn);
 3183 
 3184             READ_INT_VALUE("yErrorType", yErrorType, ErrorType);
 3185             READ_COLUMN(yErrorPlusColumn);
 3186             READ_COLUMN(yErrorMinusColumn);
 3187 
 3188             READ_INT_VALUE("type", errorBarsType, ErrorBarsType);
 3189             READ_DOUBLE_VALUE("capSize", errorBarsCapSize);
 3190 
 3191             READ_QPEN(d->errorBarsPen);
 3192 
 3193             READ_DOUBLE_VALUE("opacity", errorBarsOpacity);
 3194         }
 3195     }
 3196 
 3197     return true;
 3198 }
 3199 
 3200 //##############################################################################
 3201 //#########################  Theme management ##################################
 3202 //##############################################################################
 3203 void XYCurve::loadThemeConfig(const KConfig& config) {
 3204     KConfigGroup group = config.group("XYCurve");
 3205 
 3206     int index = parentAspect()->indexOfChild<XYCurve>(this);
 3207     const auto* plot = dynamic_cast<const CartesianPlot*>(parentAspect());
 3208     QColor themeColor;
 3209     if (index<plot->themeColorPalette().size())
 3210         themeColor = plot->themeColorPalette().at(index);
 3211     else {
 3212         if (plot->themeColorPalette().size())
 3213             themeColor = plot->themeColorPalette().last();
 3214     }
 3215 
 3216     QPen p;
 3217 
 3218     Q_D(XYCurve);
 3219     d->m_suppressRecalc = true;
 3220 
 3221     //Line
 3222     p.setStyle((Qt::PenStyle)group.readEntry("LineStyle", (int)Qt::SolidLine));
 3223     p.setWidthF(group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
 3224     p.setColor(themeColor);
 3225     this->setLinePen(p);
 3226     this->setLineOpacity(group.readEntry("LineOpacity", 1.0));
 3227 
 3228     //Drop line
 3229     p.setStyle((Qt::PenStyle)group.readEntry("DropLineStyle", (int)Qt::SolidLine));
 3230     p.setWidthF(group.readEntry("DropLineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
 3231     p.setColor(themeColor);
 3232     this->setDropLinePen(p);
 3233     this->setDropLineOpacity(group.readEntry("DropLineOpacity", 1.0));
 3234 
 3235     //Symbol
 3236     this->setSymbolsOpacity(group.readEntry("SymbolOpacity", 1.0));
 3237     QBrush brush;
 3238     brush.setStyle((Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern));
 3239     brush.setColor(themeColor);
 3240     this->setSymbolsBrush(brush);
 3241     p.setStyle((Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine));
 3242     p.setColor(themeColor);
 3243     p.setWidthF(group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Unit::Point)));
 3244     this->setSymbolsPen(p);
 3245 
 3246     //Values
 3247     this->setValuesOpacity(group.readEntry("ValuesOpacity", 1.0));
 3248     this->setValuesColor(group.readEntry("ValuesColor", themeColor));
 3249 
 3250     //Filling
 3251     this->setFillingBrushStyle((Qt::BrushStyle)group.readEntry("FillingBrushStyle", (int)Qt::SolidPattern));
 3252     this->setFillingColorStyle((PlotArea::BackgroundColorStyle)group.readEntry("FillingColorStyle", static_cast<int>(PlotArea::BackgroundColorStyle::SingleColor)));
 3253     this->setFillingOpacity(group.readEntry("FillingOpacity", 1.0));
 3254     this->setFillingPosition((FillingPosition)group.readEntry("FillingPosition", static_cast<int>(FillingPosition::NoFilling)));
 3255     this->setFillingFirstColor(themeColor);
 3256     this->setFillingSecondColor(group.readEntry("FillingSecondColor", QColor(Qt::black)));
 3257     this->setFillingType((PlotArea::BackgroundType)group.readEntry("FillingType", static_cast<int>(PlotArea::BackgroundType::Color)));
 3258 
 3259     //Error Bars
 3260     p.setStyle((Qt::PenStyle)group.readEntry("ErrorBarsStyle", (int)Qt::SolidLine));
 3261     p.setWidthF(group.readEntry("ErrorBarsWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
 3262     p.setColor(themeColor);
 3263     this->setErrorBarsPen(p);
 3264     this->setErrorBarsOpacity(group.readEntry("ErrorBarsOpacity", 1.0));
 3265 
 3266     d->m_suppressRecalc = false;
 3267     d->recalcShapeAndBoundingRect();
 3268 }
 3269 
 3270 void XYCurve::saveThemeConfig(const KConfig& config) {
 3271     KConfigGroup group = config.group("XYCurve");
 3272 
 3273     //Drop line
 3274     group.writeEntry("DropLineColor",(QColor) this->dropLinePen().color());
 3275     group.writeEntry("DropLineStyle",(int) this->dropLinePen().style());
 3276     group.writeEntry("DropLineWidth", this->dropLinePen().widthF());
 3277     group.writeEntry("DropLineOpacity",this->dropLineOpacity());
 3278 
 3279     //Error Bars
 3280     group.writeEntry("ErrorBarsCapSize",this->errorBarsCapSize());
 3281     group.writeEntry("ErrorBarsOpacity",this->errorBarsOpacity());
 3282     group.writeEntry("ErrorBarsColor",(QColor) this->errorBarsPen().color());
 3283     group.writeEntry("ErrorBarsStyle",(int) this->errorBarsPen().style());
 3284     group.writeEntry("ErrorBarsWidth", this->errorBarsPen().widthF());
 3285 
 3286     //Filling
 3287     group.writeEntry("FillingBrushStyle",(int) this->fillingBrushStyle());
 3288     group.writeEntry("FillingColorStyle",(int) this->fillingColorStyle());
 3289     group.writeEntry("FillingOpacity", this->fillingOpacity());
 3290     group.writeEntry("FillingPosition",(int) this->fillingPosition());
 3291     group.writeEntry("FillingSecondColor",(QColor) this->fillingSecondColor());
 3292     group.writeEntry("FillingType",(int) this->fillingType());
 3293 
 3294     //Line
 3295     group.writeEntry("LineOpacity", this->lineOpacity());
 3296     group.writeEntry("LineStyle",(int) this->linePen().style());
 3297     group.writeEntry("LineWidth", this->linePen().widthF());
 3298 
 3299     //Symbol
 3300     group.writeEntry("SymbolOpacity", this->symbolsOpacity());
 3301 
 3302     //Values
 3303     group.writeEntry("ValuesOpacity", this->valuesOpacity());
 3304     group.writeEntry("ValuesColor", (QColor) this->valuesColor());
 3305     group.writeEntry("ValuesFont", this->valuesFont());
 3306 
 3307     int index = parentAspect()->indexOfChild<XYCurve>(this);
 3308     if (index < 5) {
 3309         KConfigGroup themeGroup = config.group("Theme");
 3310         for (int i = index; i < 5; i++) {
 3311             QString s = "ThemePaletteColor" + QString::number(i+1);
 3312             themeGroup.writeEntry(s, (QColor)this->linePen().color());
 3313         }
 3314     }
 3315 }