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 }