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

0001 /*
0002     File                 : CartesianPlotLegend.cpp
0003     Project              : LabPlot
0004     Description          : Legend for the cartesian plot
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2013-2022 Alexander Semke <alexander.semke@web.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 /*!
0012   \class CartesianPlotLegend
0013   \brief Legend for the cartesian plot.
0014 
0015   \ingroup kdefrontend
0016 */
0017 #include "CartesianPlotLegend.h"
0018 #include "backend/core/AbstractColumn.h"
0019 #include "backend/core/Project.h"
0020 #include "backend/lib/XmlStreamReader.h"
0021 #include "backend/lib/commandtemplates.h"
0022 #include "backend/worksheet/Background.h"
0023 #include "backend/worksheet/Line.h"
0024 #include "backend/worksheet/TextLabel.h"
0025 #include "backend/worksheet/Worksheet.h"
0026 #include "backend/worksheet/plots/cartesian/BarPlot.h"
0027 #include "backend/worksheet/plots/cartesian/BoxPlot.h"
0028 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
0029 #include "backend/worksheet/plots/cartesian/CartesianPlotLegendPrivate.h"
0030 #include "backend/worksheet/plots/cartesian/ErrorBar.h"
0031 #include "backend/worksheet/plots/cartesian/ErrorBarStyle.h"
0032 #include "backend/worksheet/plots/cartesian/Histogram.h"
0033 #include "backend/worksheet/plots/cartesian/KDEPlot.h"
0034 #include "backend/worksheet/plots/cartesian/LollipopPlot.h"
0035 #include "backend/worksheet/plots/cartesian/QQPlot.h"
0036 #include "backend/worksheet/plots/cartesian/Symbol.h"
0037 #include "backend/worksheet/plots/cartesian/XYCurve.h"
0038 
0039 #include <QKeyEvent>
0040 #include <QMenu>
0041 #include <QPainter>
0042 #include <QPainterPath>
0043 
0044 #include <KConfig>
0045 #include <KConfigGroup>
0046 #include <KLocalizedString>
0047 
0048 CartesianPlotLegend::CartesianPlotLegend(const QString& name)
0049     : WorksheetElement(name, new CartesianPlotLegendPrivate(this), AspectType::CartesianPlotLegend) {
0050     init();
0051 }
0052 
0053 CartesianPlotLegend::CartesianPlotLegend(const QString& name, CartesianPlotLegendPrivate* dd)
0054     : WorksheetElement(name, dd, AspectType::CartesianPlotLegend) {
0055     init();
0056 }
0057 
0058 void CartesianPlotLegend::finalizeAdd() {
0059     Q_D(CartesianPlotLegend);
0060     d->plot = static_cast<const CartesianPlot*>(parentAspect());
0061 }
0062 
0063 // no need to delete the d-pointer here - it inherits from QGraphicsItem
0064 // and is deleted during the cleanup in QGraphicsScene
0065 CartesianPlotLegend::~CartesianPlotLegend() = default;
0066 
0067 void CartesianPlotLegend::init() {
0068     Q_D(CartesianPlotLegend);
0069 
0070     KConfig config;
0071     KConfigGroup group = config.group(QStringLiteral("CartesianPlotLegend"));
0072 
0073     d->labelFont = group.readEntry(QStringLiteral("LabelsFont"), QFont());
0074     d->labelFont.setPixelSize(Worksheet::convertToSceneUnits(10, Worksheet::Unit::Point));
0075 
0076     d->labelColor = group.readEntry(QStringLiteral("FontColor"), QColor(Qt::black));
0077     d->labelColumnMajor = true;
0078     d->lineSymbolWidth = group.readEntry(QStringLiteral("LineSymbolWidth"), Worksheet::convertToSceneUnits(1, Worksheet::Unit::Centimeter));
0079     d->rowCount = 0;
0080     d->columnCount = 0;
0081 
0082     d->position.horizontalPosition = WorksheetElement::HorizontalPosition::Right;
0083     d->position.verticalPosition = WorksheetElement::VerticalPosition::Top;
0084     d->horizontalAlignment = WorksheetElement::HorizontalAlignment::Right;
0085     d->verticalAlignment = WorksheetElement::VerticalAlignment::Top;
0086     d->position.point = QPointF(0, 0);
0087 
0088     d->setRotation(group.readEntry(QStringLiteral("Rotation"), 0.0));
0089 
0090     // Title
0091     d->title = new TextLabel(this->name(), TextLabel::Type::PlotLegendTitle);
0092     d->title->setBorderShape(TextLabel::BorderShape::NoBorder);
0093     addChild(d->title);
0094     d->title->setHidden(true);
0095     d->title->setParentGraphicsItem(d);
0096     d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
0097     d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsFocusable, false);
0098     connect(d->title, &TextLabel::changed, this, &CartesianPlotLegend::retransform);
0099 
0100     // Background
0101     d->background = new Background(QString());
0102     addChild(d->background);
0103     d->background->setHidden(true);
0104     d->background->init(group);
0105     connect(d->background, &Background::updateRequested, [=] {
0106         d->update();
0107     });
0108 
0109     // Border
0110     d->borderLine = new Line(QString());
0111     d->borderLine->setPrefix(QStringLiteral("Border"));
0112     d->borderLine->setCreateXmlElement(false);
0113     d->borderLine->setHidden(true);
0114     addChild(d->borderLine);
0115     d->borderLine->init(group);
0116     connect(d->borderLine, &Line::updatePixmapRequested, [=] {
0117         d->update();
0118     });
0119     connect(d->borderLine, &Line::updateRequested, [=] {
0120         d->recalcShapeAndBoundingRect();
0121     });
0122 
0123     d->borderCornerRadius = group.readEntry(QStringLiteral("BorderCornerRadius"), 0.0);
0124 
0125     // Layout
0126     d->layoutTopMargin = group.readEntry(QStringLiteral("LayoutTopMargin"), Worksheet::convertToSceneUnits(0.2, Worksheet::Unit::Centimeter));
0127     d->layoutBottomMargin = group.readEntry(QStringLiteral("LayoutBottomMargin"), Worksheet::convertToSceneUnits(0.2, Worksheet::Unit::Centimeter));
0128     d->layoutLeftMargin = group.readEntry(QStringLiteral("LayoutLeftMargin"), Worksheet::convertToSceneUnits(0.2, Worksheet::Unit::Centimeter));
0129     d->layoutRightMargin = group.readEntry(QStringLiteral("LayoutRightMargin"), Worksheet::convertToSceneUnits(0.2, Worksheet::Unit::Centimeter));
0130     d->layoutVerticalSpacing = group.readEntry(QStringLiteral("LayoutVerticalSpacing"), Worksheet::convertToSceneUnits(0.1, Worksheet::Unit::Centimeter));
0131     d->layoutHorizontalSpacing = group.readEntry(QStringLiteral("LayoutHorizontalSpacing"), Worksheet::convertToSceneUnits(0.1, Worksheet::Unit::Centimeter));
0132     d->layoutColumnCount = group.readEntry(QStringLiteral("LayoutColumnCount"), 1);
0133 
0134     this->initActions();
0135 }
0136 
0137 void CartesianPlotLegend::initActions() {
0138 }
0139 
0140 /*!
0141     Returns an icon to be used in the project explorer.
0142 */
0143 QIcon CartesianPlotLegend::icon() const {
0144     return QIcon::fromTheme(QStringLiteral("text-field"));
0145 }
0146 
0147 void CartesianPlotLegend::retransform() {
0148     d_ptr->retransform();
0149 }
0150 
0151 /*!
0152  * overrides the implementation in WorksheetElement and sets the z-value to the maximal possible,
0153  * legends are drawn on top of all other object in the plot.
0154  */
0155 void CartesianPlotLegend::setZValue(qreal) {
0156     Q_D(CartesianPlotLegend);
0157     d->setZValue(std::numeric_limits<double>::max());
0158 }
0159 
0160 void CartesianPlotLegend::handleResize(double /*horizontalRatio*/, double /*verticalRatio*/, bool /*pageResize*/) {
0161     // TODO
0162     //  Q_D(const CartesianPlotLegend);
0163 }
0164 
0165 // ##############################################################################
0166 // ################################  getter methods  ############################
0167 // ##############################################################################
0168 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, QFont, labelFont, labelFont)
0169 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, QColor, labelColor, labelColor)
0170 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, bool, labelColumnMajor, labelColumnMajor)
0171 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, lineSymbolWidth, lineSymbolWidth)
0172 
0173 // Title
0174 TextLabel* CartesianPlotLegend::title() {
0175     D(CartesianPlotLegend);
0176     return d->title;
0177 }
0178 
0179 // background
0180 Background* CartesianPlotLegend::background() const {
0181     Q_D(const CartesianPlotLegend);
0182     return d->background;
0183 }
0184 
0185 // Border
0186 Line* CartesianPlotLegend::borderLine() const {
0187     Q_D(const CartesianPlotLegend);
0188     return d->borderLine;
0189 }
0190 
0191 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, borderCornerRadius, borderCornerRadius)
0192 
0193 // Layout
0194 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, layoutTopMargin, layoutTopMargin)
0195 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, layoutBottomMargin, layoutBottomMargin)
0196 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, layoutLeftMargin, layoutLeftMargin)
0197 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, layoutRightMargin, layoutRightMargin)
0198 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, layoutHorizontalSpacing, layoutHorizontalSpacing)
0199 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, qreal, layoutVerticalSpacing, layoutVerticalSpacing)
0200 BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, int, layoutColumnCount, layoutColumnCount)
0201 
0202 // ##############################################################################
0203 // ######################  setter methods and undo commands  ####################
0204 // ##############################################################################
0205 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLabelFont, QFont, labelFont, retransform)
0206 void CartesianPlotLegend::setLabelFont(const QFont& font) {
0207     Q_D(CartesianPlotLegend);
0208     if (font != d->labelFont)
0209         exec(new CartesianPlotLegendSetLabelFontCmd(d, font, ki18n("%1: set font")));
0210 }
0211 
0212 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLabelColor, QColor, labelColor, update)
0213 void CartesianPlotLegend::setLabelColor(const QColor& color) {
0214     Q_D(CartesianPlotLegend);
0215     if (color != d->labelColor)
0216         exec(new CartesianPlotLegendSetLabelColorCmd(d, color, ki18n("%1: set font color")));
0217 }
0218 
0219 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLabelColumnMajor, bool, labelColumnMajor, retransform)
0220 void CartesianPlotLegend::setLabelColumnMajor(bool columnMajor) {
0221     Q_D(CartesianPlotLegend);
0222     if (columnMajor != d->labelColumnMajor)
0223         exec(new CartesianPlotLegendSetLabelColumnMajorCmd(d, columnMajor, ki18n("%1: change column order")));
0224 }
0225 
0226 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLineSymbolWidth, double, lineSymbolWidth, retransform)
0227 void CartesianPlotLegend::setLineSymbolWidth(double width) {
0228     Q_D(CartesianPlotLegend);
0229     if (width != d->lineSymbolWidth)
0230         exec(new CartesianPlotLegendSetLineSymbolWidthCmd(d, width, ki18n("%1: change line+symbol width")));
0231 }
0232 
0233 // Border
0234 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBorderCornerRadius, qreal, borderCornerRadius, update)
0235 void CartesianPlotLegend::setBorderCornerRadius(qreal radius) {
0236     Q_D(CartesianPlotLegend);
0237     if (radius != d->borderCornerRadius)
0238         exec(new CartesianPlotLegendSetBorderCornerRadiusCmd(d, radius, ki18n("%1: set border corner radius")));
0239 }
0240 
0241 // Layout
0242 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutTopMargin, double, layoutTopMargin, retransform)
0243 void CartesianPlotLegend::setLayoutTopMargin(double margin) {
0244     Q_D(CartesianPlotLegend);
0245     if (margin != d->layoutTopMargin)
0246         exec(new CartesianPlotLegendSetLayoutTopMarginCmd(d, margin, ki18n("%1: set layout top margin")));
0247 }
0248 
0249 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutBottomMargin, double, layoutBottomMargin, retransform)
0250 void CartesianPlotLegend::setLayoutBottomMargin(double margin) {
0251     Q_D(CartesianPlotLegend);
0252     if (margin != d->layoutBottomMargin)
0253         exec(new CartesianPlotLegendSetLayoutBottomMarginCmd(d, margin, ki18n("%1: set layout bottom margin")));
0254 }
0255 
0256 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutLeftMargin, double, layoutLeftMargin, retransform)
0257 void CartesianPlotLegend::setLayoutLeftMargin(double margin) {
0258     Q_D(CartesianPlotLegend);
0259     if (margin != d->layoutLeftMargin)
0260         exec(new CartesianPlotLegendSetLayoutLeftMarginCmd(d, margin, ki18n("%1: set layout left margin")));
0261 }
0262 
0263 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutRightMargin, double, layoutRightMargin, retransform)
0264 void CartesianPlotLegend::setLayoutRightMargin(double margin) {
0265     Q_D(CartesianPlotLegend);
0266     if (margin != d->layoutRightMargin)
0267         exec(new CartesianPlotLegendSetLayoutRightMarginCmd(d, margin, ki18n("%1: set layout right margin")));
0268 }
0269 
0270 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutVerticalSpacing, double, layoutVerticalSpacing, retransform)
0271 void CartesianPlotLegend::setLayoutVerticalSpacing(double spacing) {
0272     Q_D(CartesianPlotLegend);
0273     if (spacing != d->layoutVerticalSpacing)
0274         exec(new CartesianPlotLegendSetLayoutVerticalSpacingCmd(d, spacing, ki18n("%1: set layout vertical spacing")));
0275 }
0276 
0277 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutHorizontalSpacing, double, layoutHorizontalSpacing, retransform)
0278 void CartesianPlotLegend::setLayoutHorizontalSpacing(double spacing) {
0279     Q_D(CartesianPlotLegend);
0280     if (spacing != d->layoutHorizontalSpacing)
0281         exec(new CartesianPlotLegendSetLayoutHorizontalSpacingCmd(d, spacing, ki18n("%1: set layout horizontal spacing")));
0282 }
0283 
0284 STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutColumnCount, int, layoutColumnCount, retransform)
0285 void CartesianPlotLegend::setLayoutColumnCount(int count) {
0286     Q_D(CartesianPlotLegend);
0287     if (count != d->layoutColumnCount)
0288         exec(new CartesianPlotLegendSetLayoutColumnCountCmd(d, count, ki18n("%1: set layout column count")));
0289 }
0290 
0291 // ##############################################################################
0292 // #################################  SLOTS  ####################################
0293 // ##############################################################################
0294 
0295 // ##############################################################################
0296 // ######################### Private implementation #############################
0297 // ##############################################################################
0298 CartesianPlotLegendPrivate::CartesianPlotLegendPrivate(CartesianPlotLegend* owner)
0299     : WorksheetElementPrivate(owner)
0300     , q(owner) {
0301     setFlag(QGraphicsItem::ItemIsSelectable, true);
0302     setFlag(QGraphicsItem::ItemIsMovable);
0303     setFlag(QGraphicsItem::ItemSendsGeometryChanges);
0304     setFlag(QGraphicsItem::ItemIsFocusable);
0305     setAcceptHoverEvents(true);
0306 }
0307 
0308 /*!
0309   Returns the shape of the CartesianPlotLegend as a QPainterPath in local coordinates
0310 */
0311 QPainterPath CartesianPlotLegendPrivate::shape() const {
0312     QPainterPath path;
0313     if (qFuzzyIsNull(borderCornerRadius))
0314         path.addRect(m_boundingRectangle);
0315     else
0316         path.addRoundedRect(m_boundingRectangle, borderCornerRadius, borderCornerRadius);
0317 
0318     return path;
0319 }
0320 
0321 void CartesianPlotLegendPrivate::recalcShapeAndBoundingRect() {
0322     retransform();
0323 }
0324 
0325 /*!
0326   recalculates the rectangular of the legend.
0327 */
0328 void CartesianPlotLegendPrivate::retransform() {
0329     const bool suppress = suppressRetransform || !plot || q->isLoading();
0330     trackRetransformCalled(suppress);
0331 
0332     // Assert cannot be used, because the Textlabel sends the
0333     // changed signal during load and so a retransform is triggered
0334     // assert(!q->isLoading());
0335     if (suppress)
0336         return;
0337 
0338     prepareGeometryChange();
0339 
0340     m_plots.clear();
0341     m_names.clear();
0342 
0343     const auto& plots = this->plot->children<Plot>();
0344     for (auto* plot : plots) {
0345         if (!plot->isVisible() || !plot->legendVisible())
0346             continue;
0347 
0348         // add the names for plot types which can show multiple datasets
0349         auto* boxPlot = dynamic_cast<BoxPlot*>(plot);
0350         if (boxPlot) {
0351             m_plots << boxPlot;
0352             const auto& columns = boxPlot->dataColumns();
0353             for (auto* column : columns)
0354                 m_names << column->name();
0355 
0356             continue;
0357         }
0358 
0359         auto* barPlot = dynamic_cast<BarPlot*>(plot);
0360         if (barPlot) {
0361             m_plots << barPlot;
0362             const auto& columns = barPlot->dataColumns();
0363             for (auto* column : columns)
0364                 m_names << column->name();
0365 
0366             continue;
0367         }
0368 
0369         auto* lollipopPlot = dynamic_cast<LollipopPlot*>(plot);
0370         if (lollipopPlot) {
0371             m_plots << lollipopPlot;
0372             const auto& columns = lollipopPlot->dataColumns();
0373             for (auto* column : columns)
0374                 m_names << column->name();
0375 
0376             continue;
0377         }
0378 
0379         m_plots << plot;
0380         m_names << plot->name();
0381         continue;
0382     }
0383 
0384     int namesCount = m_names.count();
0385     columnCount = (namesCount < layoutColumnCount) ? namesCount : layoutColumnCount;
0386     if (columnCount == 0) // no curves available
0387         rowCount = 0;
0388     else
0389         rowCount = ceil(double(namesCount) / double(columnCount));
0390 
0391     maxColumnTextWidths.clear();
0392 
0393     // determine the width of the legend
0394     QFontMetrics fm(labelFont);
0395 
0396     qreal legendWidth = 0;
0397     for (int c = 0; c < columnCount; ++c) {
0398         int maxTextWidth = 0, index;
0399         for (int r = 0; r < rowCount; ++r) {
0400             if (labelColumnMajor)
0401                 index = c * rowCount + r;
0402             else
0403                 index = r * columnCount + c;
0404 
0405             if (index >= namesCount)
0406                 break;
0407 
0408             int w = fm.boundingRect(m_names.at(index)).width();
0409             if (w > maxTextWidth)
0410                 maxTextWidth = w;
0411         }
0412         maxColumnTextWidths.append(maxTextWidth);
0413         legendWidth += maxTextWidth;
0414     }
0415 
0416     legendWidth += layoutLeftMargin + layoutRightMargin; // margins
0417     legendWidth += columnCount * (lineSymbolWidth + layoutHorizontalSpacing); // width of the columns without the text
0418     legendWidth += (columnCount - 1) * 2. * layoutHorizontalSpacing; // spacings between the columns
0419 
0420     // add title width if title is available
0421     if (title->isVisible() && !title->text().text.isEmpty()) {
0422         qreal titleWidth;
0423         titleWidth = title->graphicsItem()->boundingRect().width();
0424 
0425         if (titleWidth > legendWidth)
0426             legendWidth = titleWidth;
0427     }
0428 
0429     // determine the height of the legend
0430     int h = fm.ascent();
0431     qreal legendHeight = layoutTopMargin + layoutBottomMargin; // margins
0432     legendHeight += rowCount * h; // height of the rows
0433     legendHeight += (rowCount - 1) * layoutVerticalSpacing; // spacing between the rows
0434     if (title->isVisible() && !title->text().text.isEmpty())
0435         legendHeight += title->graphicsItem()->boundingRect().height(); // legend titl
0436 
0437     m_boundingRectangle.setX(-legendWidth / 2.);
0438     m_boundingRectangle.setY(-legendHeight / 2.);
0439     m_boundingRectangle.setWidth(legendWidth);
0440     m_boundingRectangle.setHeight(legendHeight);
0441 
0442     updatePosition();
0443 }
0444 
0445 /*!
0446     calculates the position of the legend, when the position relative to the parent was specified (left, right, etc.)
0447 */
0448 void CartesianPlotLegendPrivate::updatePosition() {
0449     WorksheetElementPrivate::updatePosition();
0450 
0451     suppressRetransform = true;
0452     title->retransform();
0453     suppressRetransform = false;
0454 }
0455 
0456 /*!
0457   Reimplementation of QGraphicsItem::paint(). This function does the actual painting of the legend.
0458   \sa QGraphicsItem::paint().
0459 */
0460 void CartesianPlotLegendPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget*) {
0461     if (!isVisible())
0462         return;
0463 
0464     painter->save();
0465 
0466     // draw the area
0467     painter->setOpacity(background->opacity());
0468     painter->setPen(Qt::NoPen);
0469     if (background->type() == Background::Type::Color) {
0470         switch (background->colorStyle()) {
0471         case Background::ColorStyle::SingleColor: {
0472             painter->setBrush(QBrush(background->firstColor()));
0473             break;
0474         }
0475         case Background::ColorStyle::HorizontalLinearGradient: {
0476             QLinearGradient linearGrad(m_boundingRectangle.topLeft(), m_boundingRectangle.topRight());
0477             linearGrad.setColorAt(0, background->firstColor());
0478             linearGrad.setColorAt(1, background->secondColor());
0479             painter->setBrush(QBrush(linearGrad));
0480             break;
0481         }
0482         case Background::ColorStyle::VerticalLinearGradient: {
0483             QLinearGradient linearGrad(m_boundingRectangle.topLeft(), m_boundingRectangle.bottomLeft());
0484             linearGrad.setColorAt(0, background->firstColor());
0485             linearGrad.setColorAt(1, background->secondColor());
0486             painter->setBrush(QBrush(linearGrad));
0487             break;
0488         }
0489         case Background::ColorStyle::TopLeftDiagonalLinearGradient: {
0490             QLinearGradient linearGrad(m_boundingRectangle.topLeft(), m_boundingRectangle.bottomRight());
0491             linearGrad.setColorAt(0, background->firstColor());
0492             linearGrad.setColorAt(1, background->secondColor());
0493             painter->setBrush(QBrush(linearGrad));
0494             break;
0495         }
0496         case Background::ColorStyle::BottomLeftDiagonalLinearGradient: {
0497             QLinearGradient linearGrad(m_boundingRectangle.bottomLeft(), m_boundingRectangle.topRight());
0498             linearGrad.setColorAt(0, background->firstColor());
0499             linearGrad.setColorAt(1, background->secondColor());
0500             painter->setBrush(QBrush(linearGrad));
0501             break;
0502         }
0503         case Background::ColorStyle::RadialGradient: {
0504             QRadialGradient radialGrad(m_boundingRectangle.center(), m_boundingRectangle.width() / 2);
0505             radialGrad.setColorAt(0, background->firstColor());
0506             radialGrad.setColorAt(1, background->secondColor());
0507             painter->setBrush(QBrush(radialGrad));
0508             break;
0509         }
0510         }
0511     } else if (background->type() == Background::Type::Image) {
0512         if (!background->fileName().trimmed().isEmpty()) {
0513             QPixmap pix(background->fileName());
0514             switch (background->imageStyle()) {
0515             case Background::ImageStyle::ScaledCropped:
0516                 pix = pix.scaled(m_boundingRectangle.size().toSize(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
0517                 painter->drawPixmap(m_boundingRectangle.topLeft(), pix);
0518                 break;
0519             case Background::ImageStyle::Scaled:
0520                 pix = pix.scaled(m_boundingRectangle.size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0521                 painter->drawPixmap(m_boundingRectangle.topLeft(), pix);
0522                 break;
0523             case Background::ImageStyle::ScaledAspectRatio:
0524                 pix = pix.scaled(m_boundingRectangle.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
0525                 painter->drawPixmap(m_boundingRectangle.topLeft(), pix);
0526                 break;
0527             case Background::ImageStyle::Centered:
0528                 painter->drawPixmap(
0529                     QPointF(m_boundingRectangle.center().x() - pix.size().width() / 2, m_boundingRectangle.center().y() - pix.size().height() / 2),
0530                     pix);
0531                 break;
0532             case Background::ImageStyle::Tiled:
0533                 painter->drawTiledPixmap(m_boundingRectangle, pix);
0534                 break;
0535             case Background::ImageStyle::CenterTiled:
0536                 painter->drawTiledPixmap(m_boundingRectangle, pix, QPoint(m_boundingRectangle.size().width() / 2, m_boundingRectangle.size().height() / 2));
0537             }
0538         }
0539     } else if (background->type() == Background::Type::Pattern) {
0540         painter->setBrush(QBrush(background->firstColor(), background->brushStyle()));
0541     }
0542 
0543     if (qFuzzyIsNull(borderCornerRadius))
0544         painter->drawRect(m_boundingRectangle);
0545     else
0546         painter->drawRoundedRect(m_boundingRectangle, borderCornerRadius, borderCornerRadius);
0547 
0548     // draw the border
0549     if (borderLine->style() != Qt::NoPen) {
0550         painter->setPen(borderLine->pen());
0551         painter->setBrush(Qt::NoBrush);
0552         painter->setOpacity(borderLine->opacity());
0553         if (qFuzzyIsNull(borderCornerRadius))
0554             painter->drawRect(m_boundingRectangle);
0555         else
0556             painter->drawRoundedRect(m_boundingRectangle, borderCornerRadius, borderCornerRadius);
0557     }
0558 
0559     // draw curve's line+symbol and the names
0560     QFontMetrics fm(labelFont);
0561     float h = fm.ascent();
0562     painter->setFont(labelFont);
0563 
0564     // translate to left upper corner of the bounding rect plus the layout offset and the height of the title
0565     painter->translate(-m_boundingRectangle.width() / 2 + layoutLeftMargin, -m_boundingRectangle.height() / 2 + layoutTopMargin);
0566     if (title->isVisible() && !title->text().text.isEmpty())
0567         painter->translate(0, title->graphicsItem()->boundingRect().height());
0568 
0569     painter->save();
0570 
0571     int col = 0, row = 0;
0572     for (auto* plot : m_plots) {
0573         // process the curves
0574         // TODO: move the logic below into the plot classes
0575         const auto* curve = dynamic_cast<const XYCurve*>(plot);
0576         const auto* hist = dynamic_cast<const Histogram*>(plot);
0577         const auto* boxPlot = dynamic_cast<const BoxPlot*>(plot);
0578         const auto* barPlot = dynamic_cast<const BarPlot*>(plot);
0579         const auto* lollipopPlot = dynamic_cast<const LollipopPlot*>(plot);
0580         const auto* kdePlot = dynamic_cast<const KDEPlot*>(plot);
0581         const auto* qqPlot = dynamic_cast<const QQPlot*>(plot);
0582 
0583         if (curve) { // draw the legend item for xy-curve
0584             // curve's line (painted at the half of the ascent size)
0585             if (curve->lineType() != XYCurve::LineType::NoLine) {
0586                 painter->setPen(curve->line()->pen());
0587                 painter->setOpacity(curve->line()->opacity());
0588                 painter->drawLine(0, h / 2, lineSymbolWidth, h / 2);
0589             }
0590 
0591             // error bars
0592             const auto xErrorType = curve->xErrorBar()->type();
0593             const auto yErrorType = curve->yErrorBar()->type();
0594             const auto* errorBarsLine = curve->errorBarStyle()->line();
0595             if ((xErrorType != ErrorBar::Type::NoError && curve->xErrorBar()->plusColumn())
0596                 || (yErrorType != ErrorBar::Type::NoError && curve->yErrorBar()->plusColumn())) {
0597                 painter->setOpacity(errorBarsLine->opacity());
0598                 painter->setPen(errorBarsLine->pen());
0599 
0600                 // curve's error bars for x
0601                 float errorBarsSize = Worksheet::convertToSceneUnits(10, Worksheet::Unit::Point);
0602                 if (curve->symbol()->style() != Symbol::Style::NoSymbols && errorBarsSize < curve->symbol()->size() * 1.4)
0603                     errorBarsSize = curve->symbol()->size() * 1.4;
0604 
0605                 switch (curve->errorBarStyle()->type()) {
0606                 case ErrorBarStyle::Type::Simple:
0607                     // horiz. line
0608                     if (xErrorType != ErrorBar::Type::NoError)
0609                         painter->drawLine(lineSymbolWidth / 2 - errorBarsSize / 2, h / 2, lineSymbolWidth / 2 + errorBarsSize / 2, h / 2);
0610                     // vert. line
0611                     if (yErrorType != ErrorBar::Type::NoError)
0612                         painter->drawLine(lineSymbolWidth / 2, h / 2 - errorBarsSize / 2, lineSymbolWidth / 2, h / 2 + errorBarsSize / 2);
0613                     break;
0614                 case ErrorBarStyle::Type::WithEnds:
0615                     // horiz. line
0616                     if (xErrorType != ErrorBar::Type::NoError) {
0617                         painter->drawLine(lineSymbolWidth / 2 - errorBarsSize / 2, h / 2, lineSymbolWidth / 2 + errorBarsSize / 2, h / 2);
0618 
0619                         // caps for the horiz. line
0620                         painter->drawLine(lineSymbolWidth / 2 - errorBarsSize / 2,
0621                                           h / 2 - errorBarsSize / 4,
0622                                           lineSymbolWidth / 2 - errorBarsSize / 2,
0623                                           h / 2 + errorBarsSize / 4);
0624                         painter->drawLine(lineSymbolWidth / 2 + errorBarsSize / 2,
0625                                           h / 2 - errorBarsSize / 4,
0626                                           lineSymbolWidth / 2 + errorBarsSize / 2,
0627                                           h / 2 + errorBarsSize / 4);
0628                     }
0629 
0630                     // vert. line
0631                     if (yErrorType != ErrorBar::Type::NoError) {
0632                         painter->drawLine(lineSymbolWidth / 2, h / 2 - errorBarsSize / 2, lineSymbolWidth / 2, h / 2 + errorBarsSize / 2);
0633 
0634                         // caps for the vert. line
0635                         painter->drawLine(lineSymbolWidth / 2 - errorBarsSize / 4,
0636                                           h / 2 - errorBarsSize / 2,
0637                                           lineSymbolWidth / 2 + errorBarsSize / 4,
0638                                           h / 2 - errorBarsSize / 2);
0639                         painter->drawLine(lineSymbolWidth / 2 - errorBarsSize / 4,
0640                                           h / 2 + errorBarsSize / 2,
0641                                           lineSymbolWidth / 2 + errorBarsSize / 4,
0642                                           h / 2 + errorBarsSize / 2);
0643                     }
0644                     break;
0645                 }
0646             }
0647 
0648             // curve's symbol
0649             const auto* symbol = curve->symbol();
0650             if (symbol->style() != Symbol::Style::NoSymbols) {
0651                 painter->setOpacity(symbol->opacity());
0652                 painter->setBrush(symbol->brush());
0653                 painter->setPen(symbol->pen());
0654 
0655                 auto path = Symbol::stylePath(symbol->style());
0656                 QTransform trafo;
0657                 trafo.scale(symbol->size(), symbol->size());
0658                 path = trafo.map(path);
0659 
0660                 if (symbol->rotationAngle() != 0) {
0661                     trafo.reset();
0662                     trafo.rotate(symbol->rotationAngle());
0663                     path = trafo.map(path);
0664                 }
0665 
0666                 painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0667                 painter->drawPath(path);
0668                 painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0669             }
0670 
0671             // curve's name
0672             painter->setPen(QPen(labelColor));
0673             painter->setOpacity(1.0);
0674             painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), curve->name());
0675 
0676             if (!translatePainter(painter, row, col, h))
0677                 break;
0678         } else if (hist) { // draw the legend item for histogram (simple rectangular with the sizes of the ascent)
0679             // use line's pen (histogram bars, envelope or drop lines) if available,
0680             if (hist->line()->histogramLineType() != Histogram::NoLine && hist->line()->pen() != Qt::NoPen) {
0681                 painter->setOpacity(hist->line()->opacity());
0682                 painter->setPen(hist->line()->pen());
0683             }
0684 
0685             // for the brush, use the histogram filling or symbols filling or no brush
0686             if (hist->background()->enabled())
0687                 painter->setBrush(QBrush(hist->background()->firstColor(), hist->background()->brushStyle()));
0688             else if (hist->symbol()->style() != Symbol::Style::NoSymbols)
0689                 painter->setBrush(hist->symbol()->brush());
0690             else
0691                 painter->setBrush(Qt::NoBrush);
0692 
0693             painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0694             painter->drawRect(QRectF(-h / 2, -h / 2, h, h));
0695             painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0696 
0697             // curve's name
0698             painter->setPen(QPen(labelColor));
0699             painter->setOpacity(1.0);
0700             painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), hist->name());
0701 
0702             if (!translatePainter(painter, row, col, h))
0703                 break;
0704         } else if (boxPlot) { // draw a legend item for every dataset in the box plot
0705             const auto& columns = boxPlot->dataColumns();
0706             int index = 0;
0707             for (auto* column : columns) {
0708                 // draw the whiskers
0709                 painter->setOpacity(boxPlot->whiskersLine()->opacity());
0710                 painter->setPen(boxPlot->whiskersLine()->pen());
0711                 painter->drawLine(lineSymbolWidth / 2, 0, lineSymbolWidth / 2, 0.3 * h);
0712                 painter->drawLine(lineSymbolWidth / 2, 0.7 * h, lineSymbolWidth / 2, h);
0713 
0714                 // draw the box
0715                 auto* background = boxPlot->backgroundAt(index);
0716                 painter->setOpacity(background->opacity());
0717                 painter->setBrush(QBrush(background->firstColor(), background->brushStyle()));
0718                 painter->setPen(Qt::NoPen);
0719                 painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0720                 painter->drawRect(QRectF(-h * 0.25, -0.2 * h, 0.5 * h, 0.4 * h));
0721                 painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0722 
0723                 auto* borderLine = boxPlot->borderLineAt(index);
0724                 painter->setOpacity(borderLine->opacity());
0725                 // painter->setBrush(QBrush(background->firstColor(), background->brushStyle()));
0726                 painter->setPen(borderLine->pen());
0727                 painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0728                 painter->drawRect(QRectF(-h * 0.25, -0.2 * h, 0.5 * h, 0.4 * h));
0729                 painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0730 
0731                 // draw the name text
0732                 painter->setPen(QPen(labelColor));
0733                 painter->setOpacity(1.0);
0734                 painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), column->name());
0735                 ++index;
0736                 if (!translatePainter(painter, row, col, h))
0737                     break;
0738             }
0739         } else if (barPlot) { // draw a legend item for every dataset in the bar plot
0740             const auto& columns = barPlot->dataColumns();
0741             int index = 0;
0742             for (auto* column : columns) {
0743                 // draw the bar
0744                 auto* background = barPlot->backgroundAt(index);
0745                 painter->setOpacity(background->opacity());
0746                 painter->setBrush(QBrush(background->firstColor(), background->brushStyle()));
0747                 painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0748                 painter->drawRect(QRectF(-h * 0.25, -h / 2, h * 0.5, h));
0749                 painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0750 
0751                 auto* line = barPlot->lineAt(index);
0752                 painter->setPen(line->pen());
0753                 painter->setOpacity(line->opacity());
0754                 painter->setBrush(Qt::NoBrush);
0755                 painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0756                 painter->drawRect(QRectF(-h * 0.25, -h / 2, h * 0.5, h));
0757                 painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0758 
0759                 // draw the name text
0760                 painter->setPen(QPen(labelColor));
0761                 painter->setOpacity(1.0);
0762                 painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), column->name());
0763                 ++index;
0764                 if (!translatePainter(painter, row, col, h))
0765                     break;
0766             }
0767         } else if (lollipopPlot) { // draw a legend item for every dataset in the lollipop plot
0768             const auto& columns = lollipopPlot->dataColumns();
0769             int index = 0;
0770             for (auto* column : columns) {
0771                 // draw the line
0772                 auto* line = lollipopPlot->lineAt(index);
0773                 painter->setPen(line->pen());
0774                 painter->setOpacity(line->opacity());
0775                 painter->setBrush(Qt::NoBrush);
0776                 painter->drawLine(lineSymbolWidth / 2, h * 0.25, lineSymbolWidth / 2, h);
0777 
0778                 // draw the symbol
0779                 const auto* symbol = lollipopPlot->symbolAt(index);
0780                 if (symbol->style() != Symbol::Style::NoSymbols) {
0781                     painter->setOpacity(symbol->opacity());
0782                     painter->setBrush(symbol->brush());
0783                     painter->setPen(symbol->pen());
0784 
0785                     auto path = Symbol::stylePath(symbol->style());
0786                     QTransform trafo;
0787                     trafo.scale(symbol->size(), symbol->size());
0788                     path = trafo.map(path);
0789 
0790                     if (symbol->rotationAngle() != 0) {
0791                         trafo.reset();
0792                         trafo.rotate(symbol->rotationAngle());
0793                         path = trafo.map(path);
0794                     }
0795 
0796                     painter->translate(QPointF(lineSymbolWidth / 2, h * 0.25));
0797                     painter->drawPath(path);
0798                     painter->translate(-QPointF(lineSymbolWidth / 2, h * 0.25));
0799                 }
0800 
0801                 // draw the name text
0802                 painter->setPen(QPen(labelColor));
0803                 painter->setOpacity(1.0);
0804                 painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), column->name());
0805                 ++index;
0806                 if (!translatePainter(painter, row, col, h))
0807                     break;
0808             }
0809         } else if (kdePlot) {
0810             // line
0811             const auto* line = kdePlot->estimationCurve()->line();
0812             painter->setPen(line->pen());
0813             painter->setOpacity(line->opacity());
0814             painter->drawLine(0, h / 2, lineSymbolWidth, h / 2);
0815 
0816             // name
0817             painter->setPen(QPen(labelColor));
0818             painter->setOpacity(1.0);
0819             painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), kdePlot->name());
0820 
0821             if (!translatePainter(painter, row, col, h))
0822                 break;
0823         } else if (qqPlot) {
0824             // line
0825             const auto* line = qqPlot->line();
0826             painter->setPen(line->pen());
0827             painter->setOpacity(line->opacity());
0828             painter->drawLine(0, h / 2, lineSymbolWidth, h / 2);
0829 
0830             // symbol
0831             const auto* symbol = qqPlot->symbol();
0832             if (symbol->style() != Symbol::Style::NoSymbols) {
0833                 painter->setOpacity(symbol->opacity());
0834                 painter->setBrush(symbol->brush());
0835                 painter->setPen(symbol->pen());
0836 
0837                 auto path = Symbol::stylePath(symbol->style());
0838                 QTransform trafo;
0839                 trafo.scale(symbol->size(), symbol->size());
0840                 path = trafo.map(path);
0841 
0842                 if (symbol->rotationAngle() != 0) {
0843                     trafo.reset();
0844                     trafo.rotate(symbol->rotationAngle());
0845                     path = trafo.map(path);
0846                 }
0847 
0848                 painter->translate(QPointF(lineSymbolWidth / 2, h / 2));
0849                 painter->drawPath(path);
0850                 painter->translate(-QPointF(lineSymbolWidth / 2, h / 2));
0851             }
0852 
0853             // name
0854             painter->setPen(QPen(labelColor));
0855             painter->setOpacity(1.0);
0856             painter->drawText(QPoint(lineSymbolWidth + layoutHorizontalSpacing, h), qqPlot->name());
0857 
0858             if (!translatePainter(painter, row, col, h))
0859                 break;
0860         }
0861     }
0862 
0863     painter->restore();
0864     painter->restore();
0865 
0866     if (m_hovered && !isSelected() && !q->isPrinting()) {
0867         painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine));
0868         painter->drawPath(shape());
0869     }
0870 
0871     if (isSelected() && !q->isPrinting()) {
0872         painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine));
0873         painter->drawPath(shape());
0874     }
0875 }
0876 
0877 /*!
0878  * helper function translating the painter to the next row and column when painting the next legend item in paint().
0879  */
0880 bool CartesianPlotLegendPrivate::translatePainter(QPainter* painter, int& row, int& col, int height) {
0881     if (labelColumnMajor) { // column major order
0882         ++row;
0883         if (row != rowCount)
0884             painter->translate(0, layoutVerticalSpacing + height);
0885         else {
0886             ++col;
0887             if (col == columnCount)
0888                 return false;
0889 
0890             row = 0;
0891             painter->restore();
0892 
0893             double deltaX =
0894                 lineSymbolWidth + layoutHorizontalSpacing + maxColumnTextWidths.at(col - 1); // width of the current column (subtract 1 because of ++col above)
0895             deltaX += 2 * layoutHorizontalSpacing; // spacing between two columns
0896             painter->translate(deltaX, 0);
0897             painter->save();
0898         }
0899     } else { // row major order
0900         ++col;
0901         if (col != columnCount) {
0902             double deltaX =
0903                 lineSymbolWidth + layoutHorizontalSpacing + maxColumnTextWidths.at(col - 1); // width of the current column (subtract 1 because of ++col above)
0904             deltaX += 2 * layoutHorizontalSpacing; // spacing between two columns
0905             painter->translate(deltaX, 0);
0906         } else {
0907             ++row;
0908             if (row == rowCount)
0909                 return false;
0910 
0911             painter->restore();
0912             painter->translate(0, layoutVerticalSpacing + height);
0913             painter->save();
0914         }
0915     }
0916 
0917     return true;
0918 }
0919 
0920 // ##############################################################################
0921 // ##################  Serialization/Deserialization  ###########################
0922 // ##############################################################################
0923 //! Save as XML
0924 void CartesianPlotLegend::save(QXmlStreamWriter* writer) const {
0925     Q_D(const CartesianPlotLegend);
0926 
0927     writer->writeStartElement(QStringLiteral("cartesianPlotLegend"));
0928     writeBasicAttributes(writer);
0929     writeCommentElement(writer);
0930 
0931     // general
0932     writer->writeStartElement(QStringLiteral("general"));
0933     WRITE_QCOLOR(d->labelColor);
0934     WRITE_QFONT(d->labelFont);
0935     writer->writeAttribute(QStringLiteral("columnMajor"), QString::number(d->labelColumnMajor));
0936     writer->writeAttribute(QStringLiteral("lineSymbolWidth"), QString::number(d->lineSymbolWidth));
0937     writer->writeAttribute(QStringLiteral("visible"), QString::number(d->isVisible()));
0938     writer->writeEndElement();
0939 
0940     // geometry
0941     writer->writeStartElement(QStringLiteral("geometry"));
0942     WorksheetElement::save(writer);
0943     writer->writeEndElement();
0944 
0945     // title
0946     d->title->save(writer);
0947 
0948     // background
0949     d->background->save(writer);
0950 
0951     // border
0952     writer->writeStartElement(QStringLiteral("border"));
0953     d->borderLine->save(writer);
0954     writer->writeAttribute(QStringLiteral("borderCornerRadius"), QString::number(d->borderCornerRadius));
0955     writer->writeEndElement();
0956 
0957     // layout
0958     writer->writeStartElement(QStringLiteral("layout"));
0959     writer->writeAttribute(QStringLiteral("topMargin"), QString::number(d->layoutTopMargin));
0960     writer->writeAttribute(QStringLiteral("bottomMargin"), QString::number(d->layoutBottomMargin));
0961     writer->writeAttribute(QStringLiteral("leftMargin"), QString::number(d->layoutLeftMargin));
0962     writer->writeAttribute(QStringLiteral("rightMargin"), QString::number(d->layoutRightMargin));
0963     writer->writeAttribute(QStringLiteral("verticalSpacing"), QString::number(d->layoutVerticalSpacing));
0964     writer->writeAttribute(QStringLiteral("horizontalSpacing"), QString::number(d->layoutHorizontalSpacing));
0965     writer->writeAttribute(QStringLiteral("columnCount"), QString::number(d->layoutColumnCount));
0966     writer->writeEndElement();
0967 
0968     writer->writeEndElement(); // close "cartesianPlotLegend" section
0969 }
0970 
0971 //! Load from XML
0972 bool CartesianPlotLegend::load(XmlStreamReader* reader, bool preview) {
0973     Q_D(CartesianPlotLegend);
0974 
0975     if (!readBasicAttributes(reader))
0976         return false;
0977 
0978     QXmlStreamAttributes attribs;
0979     QString str;
0980 
0981     while (!reader->atEnd()) {
0982         reader->readNext();
0983         if (reader->isEndElement() && reader->name() == QLatin1String("cartesianPlotLegend"))
0984             break;
0985 
0986         if (!reader->isStartElement())
0987             continue;
0988 
0989         if (!preview && reader->name() == QLatin1String("comment")) {
0990             if (!readCommentElement(reader))
0991                 return false;
0992         } else if (!preview && reader->name() == QLatin1String("general")) {
0993             attribs = reader->attributes();
0994 
0995             READ_QCOLOR(d->labelColor);
0996             READ_QFONT(d->labelFont);
0997             READ_INT_VALUE("columnMajor", labelColumnMajor, int);
0998             READ_DOUBLE_VALUE("lineSymbolWidth", lineSymbolWidth);
0999 
1000             if (Project::xmlVersion() < 6) {
1001                 str = attribs.value(QStringLiteral("visible")).toString();
1002                 if (str.isEmpty())
1003                     reader->raiseMissingAttributeWarning(QStringLiteral("visible"));
1004                 else
1005                     d->setVisible(str.toInt());
1006             }
1007         } else if (!preview && reader->name() == QLatin1String("geometry")) {
1008             if (Project::xmlVersion() >= 6)
1009                 WorksheetElement::load(reader, preview);
1010             else {
1011                 // Visible is in "general" before version 6
1012                 // therefore WorksheetElement::load() cannot be used
1013                 attribs = reader->attributes();
1014 
1015                 str = attribs.value(QStringLiteral("x")).toString();
1016                 if (str.isEmpty())
1017                     reader->raiseMissingAttributeWarning(QStringLiteral("x"));
1018                 else
1019                     d->position.point.setX(str.toDouble());
1020 
1021                 str = attribs.value(QStringLiteral("y")).toString();
1022                 if (str.isEmpty())
1023                     reader->raiseMissingAttributeWarning(QStringLiteral("y"));
1024                 else
1025                     d->position.point.setY(str.toDouble());
1026 
1027                 str = attribs.value(QStringLiteral("horizontalPosition")).toString();
1028                 if (str.isEmpty())
1029                     reader->raiseMissingAttributeWarning(QStringLiteral("horizontalPosition"));
1030                 else {
1031                     const auto pos = (WorksheetElement::HorizontalPosition)str.toInt();
1032                     if (pos == WorksheetElement::HorizontalPosition::Relative)
1033                         d->position.horizontalPosition = WorksheetElement::HorizontalPosition::Center;
1034                     else
1035                         d->position.horizontalPosition = pos;
1036                 }
1037 
1038                 str = attribs.value(QStringLiteral("verticalPosition")).toString();
1039                 if (str.isEmpty())
1040                     reader->raiseMissingAttributeWarning(QStringLiteral("verticalPosition"));
1041                 else {
1042                     const auto pos = (WorksheetElement::VerticalPosition)str.toInt();
1043                     if (pos == WorksheetElement::VerticalPosition::Relative)
1044                         d->position.verticalPosition = WorksheetElement::VerticalPosition::Center;
1045                     else
1046                         d->position.verticalPosition = pos;
1047                 }
1048 
1049                 // in the old format the order was reversed, multiple by -1 here
1050                 d->position.point.setY(-d->position.point.y());
1051 
1052                 d->horizontalAlignment = WorksheetElement::HorizontalAlignment::Center;
1053                 d->verticalAlignment = WorksheetElement::VerticalAlignment::Center;
1054 
1055                 QGRAPHICSITEM_READ_DOUBLE_VALUE("rotation", Rotation);
1056             }
1057         } else if (reader->name() == QLatin1String("textLabel")) {
1058             if (!d->title->load(reader, preview)) {
1059                 delete d->title;
1060                 d->title = nullptr;
1061                 return false;
1062             }
1063         } else if (!preview && reader->name() == QLatin1String("background"))
1064             d->background->load(reader, preview);
1065         else if (!preview && reader->name() == QLatin1String("border")) {
1066             attribs = reader->attributes();
1067             d->borderLine->load(reader, preview);
1068             READ_DOUBLE_VALUE("borderCornerRadius", borderCornerRadius);
1069         } else if (!preview && reader->name() == QLatin1String("layout")) {
1070             attribs = reader->attributes();
1071             READ_DOUBLE_VALUE("topMargin", layoutTopMargin);
1072             READ_DOUBLE_VALUE("bottomMargin", layoutBottomMargin);
1073             READ_DOUBLE_VALUE("leftMargin", layoutLeftMargin);
1074             READ_DOUBLE_VALUE("rightMargin", layoutRightMargin);
1075             READ_DOUBLE_VALUE("verticalSpacing", layoutVerticalSpacing);
1076             READ_DOUBLE_VALUE("horizontalSpacing", layoutHorizontalSpacing);
1077             READ_INT_VALUE("columnCount", layoutColumnCount, int);
1078         } else { // unknown element
1079             reader->raiseUnknownElementWarning();
1080             if (!reader->skipToEndElement())
1081                 return false;
1082         }
1083     }
1084 
1085     return true;
1086 }
1087 
1088 void CartesianPlotLegend::loadThemeConfig(const KConfig& config) {
1089     KConfigGroup group;
1090 
1091     // for the font color use the value defined in the theme config for Label
1092     if (config.hasGroup(QStringLiteral("Theme")))
1093         group = config.group(QStringLiteral("Label"));
1094     else
1095         group = config.group(QStringLiteral("CartesianPlotLegend"));
1096 
1097     this->setLabelColor(group.readEntry(QStringLiteral("FontColor"), QColor(Qt::black)));
1098 
1099     // for other theme dependent settings use the values defined in the theme config for CartesianPlot
1100     if (config.hasGroup(QStringLiteral("Theme")))
1101         group = config.group(QStringLiteral("CartesianPlot"));
1102 
1103     // background
1104     background()->loadThemeConfig(group);
1105 
1106     // border
1107     borderLine()->loadThemeConfig(group);
1108     this->setBorderCornerRadius(group.readEntry(QStringLiteral("BorderCornerRadius"), 0.0));
1109 
1110     title()->loadThemeConfig(config);
1111 }