File indexing completed on 2024-05-12 03:48:20

0001 /*
0002     File                 : Line.cpp
0003     Project              : LabPlot
0004     Description          : Line
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2022-2024 Alexander Semke <alexander.semke@web.de>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 /*!
0011   \class Line
0012   \brief This class contains the line properties of worksheet elements.
0013 
0014   \ingroup worksheet
0015 */
0016 
0017 #include "Line.h"
0018 #include "LinePrivate.h"
0019 #include "backend/lib/XmlStreamReader.h"
0020 #include "backend/lib/commandtemplates.h"
0021 #include "backend/worksheet/plots/cartesian/ErrorBarStyle.h"
0022 
0023 #include <KConfigGroup>
0024 #include <KLocalizedString>
0025 
0026 Line::Line(const QString& name)
0027     : AbstractAspect(name, AspectType::AbstractAspect)
0028     , d_ptr(new LinePrivate(this)) {
0029 }
0030 
0031 Line::~Line() {
0032     delete d_ptr;
0033 }
0034 
0035 void Line::setPrefix(const QString& prefix) {
0036     Q_D(Line);
0037     d->prefix = prefix;
0038 }
0039 
0040 const QString& Line::prefix() const {
0041     Q_D(const Line);
0042     return d->prefix;
0043 }
0044 
0045 /*!
0046  * defines whether an XML element needs to be create in write(). For objects where the line
0047  * properties are serialized together with some other properties, the XML element is created
0048  * in objects's save() already and there is not need to create it once more in Line::save():
0049  */
0050 void Line::setCreateXmlElement(bool create) {
0051     Q_D(Line);
0052     d->createXmlElement = create;
0053 }
0054 
0055 void Line::init(const KConfigGroup& group) {
0056     Q_D(Line);
0057 
0058     if (d->histogramLineTypeAvailable)
0059         d->histogramLineType = (Histogram::LineType)group.readEntry(d->prefix + QStringLiteral("Type"), (int)Histogram::Bars);
0060 
0061     if (d->prefix == QLatin1String("DropLine"))
0062         d->dropLineType = (XYCurve::DropLineType)group.readEntry(d->prefix + QStringLiteral("Type"), (int)XYCurve::DropLineType::NoDropLine);
0063 
0064     d->style = static_cast<Qt::PenStyle>(group.readEntry("BorderStyle", (int)Qt::SolidLine));
0065     d->width = group.readEntry(d->prefix + QStringLiteral("Width"), Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point));
0066     d->color = group.readEntry(d->prefix + QStringLiteral("Color"), QColor(Qt::black));
0067     d->pen.setStyle(d->style);
0068     d->pen.setColor(d->color);
0069     d->pen.setWidthF(d->width);
0070     d->pen.setCapStyle(Qt::FlatCap);
0071     d->pen.setJoinStyle(Qt::MiterJoin);
0072     d->opacity = group.readEntry(d->prefix + QStringLiteral("Opacity"), 1.0);
0073 }
0074 
0075 // ##############################################################################
0076 // ##########################  getter methods  ##################################
0077 // ##############################################################################
0078 BASIC_SHARED_D_READER_IMPL(Line, bool, histogramLineTypeAvailable, histogramLineTypeAvailable)
0079 BASIC_SHARED_D_READER_IMPL(Line, Histogram::LineType, histogramLineType, histogramLineType)
0080 BASIC_SHARED_D_READER_IMPL(Line, XYCurve::DropLineType, dropLineType, dropLineType)
0081 
0082 BASIC_SHARED_D_READER_IMPL(Line, QPen, pen, pen)
0083 BASIC_SHARED_D_READER_IMPL(Line, Qt::PenStyle, style, style)
0084 BASIC_SHARED_D_READER_IMPL(Line, QColor, color, color)
0085 BASIC_SHARED_D_READER_IMPL(Line, double, width, width)
0086 BASIC_SHARED_D_READER_IMPL(Line, double, opacity, opacity)
0087 
0088 // ##############################################################################
0089 // #################  setter methods and undo commands ##########################
0090 // ##############################################################################
0091 void Line::setHistogramLineTypeAvailable(bool available) {
0092     Q_D(Line);
0093     d->histogramLineTypeAvailable = available;
0094 }
0095 
0096 STD_SETTER_CMD_IMPL_S(Line, SetHistogramLineType, Histogram::LineType, histogramLineType)
0097 void Line::setHistogramLineType(Histogram::LineType type) {
0098     Q_D(Line);
0099     if (type != d->histogramLineType)
0100         exec(new LineSetHistogramLineTypeCmd(d, type, ki18n("%1: line type changed")));
0101 }
0102 
0103 STD_SETTER_CMD_IMPL_S(Line, SetDropLineType, XYCurve::DropLineType, dropLineType)
0104 void Line::setDropLineType(XYCurve::DropLineType type) {
0105     Q_D(Line);
0106     if (type != d->dropLineType)
0107         exec(new LineSetDropLineTypeCmd(d, type, ki18n("%1: drop line type changed")));
0108 }
0109 
0110 STD_SETTER_CMD_IMPL_F_S(Line, SetStyle, Qt::PenStyle, style, update)
0111 void Line::setStyle(Qt::PenStyle style) {
0112     Q_D(Line);
0113     if (style != d->style)
0114         exec(new LineSetStyleCmd(d, style, ki18n("%1: set line style")));
0115 }
0116 
0117 STD_SETTER_CMD_IMPL_F_S(Line, SetWidth, double, width, update)
0118 void Line::setWidth(double width) {
0119     Q_D(Line);
0120     if (width != d->width)
0121         exec(new LineSetWidthCmd(d, width, ki18n("%1: set line width")));
0122 }
0123 
0124 STD_SETTER_CMD_IMPL_F_S(Line, SetColor, QColor, color, update)
0125 void Line::setColor(const QColor& color) {
0126     Q_D(Line);
0127     if (color != d->color)
0128         exec(new LineSetColorCmd(d, color, ki18n("%1: set line color")));
0129 }
0130 
0131 STD_SETTER_CMD_IMPL_F_S(Line, SetOpacity, double, opacity, updatePixmap)
0132 void Line::setOpacity(qreal opacity) {
0133     Q_D(Line);
0134     if (opacity != d->opacity)
0135         exec(new LineSetOpacityCmd(d, opacity, ki18n("%1: set line opacity")));
0136 }
0137 
0138 // ##############################################################################
0139 // ####################### Private implementation ###############################
0140 // ##############################################################################
0141 LinePrivate::LinePrivate(Line* owner)
0142     : q(owner) {
0143 }
0144 
0145 QString LinePrivate::name() const {
0146     if (dynamic_cast<ErrorBarStyle*>(q->parentAspect()))
0147         return q->parentAspect()->parentAspect()->name(); // for error bars we need to go one level higher to get the curve/plot name
0148     else
0149         return q->parentAspect()->name();
0150 }
0151 
0152 void LinePrivate::update() {
0153     pen.setStyle(style);
0154     pen.setColor(color);
0155     pen.setWidthF(width);
0156     Q_EMIT q->updateRequested();
0157 }
0158 
0159 void LinePrivate::updatePixmap() {
0160     Q_EMIT q->updatePixmapRequested();
0161 }
0162 
0163 // ##############################################################################
0164 // ##################  Serialization/Deserialization  ###########################
0165 // ##############################################################################
0166 //! Save as XML
0167 void Line::save(QXmlStreamWriter* writer) const {
0168     Q_D(const Line);
0169 
0170     if (d->createXmlElement) {
0171         // for names in the XML file, the first letter is lower case but the camel case still remains.
0172         // so, we just convert the first character for lower case. e.g. MedianLine -> medianLine.
0173         if (d->prefix != QLatin1String("DropLine")) {
0174             QString newPrefix = d->prefix;
0175             newPrefix.replace(0, 1, d->prefix.at(0).toLower());
0176             writer->writeStartElement(newPrefix);
0177         } else
0178             writer->writeStartElement(QLatin1String("dropLines")); // use "dropLines" for backward compatibility in XYCurve
0179     }
0180 
0181     if (d->histogramLineTypeAvailable)
0182         writer->writeAttribute(QStringLiteral("type"), QString::number(d->histogramLineType));
0183     else if (d->prefix == QLatin1String("DropLine"))
0184         writer->writeAttribute(QStringLiteral("type"), QString::number(static_cast<int>(d->dropLineType)));
0185 
0186     WRITE_QPEN(d->pen);
0187     writer->writeAttribute(QStringLiteral("opacity"), QString::number(d->opacity));
0188 
0189     if (d->createXmlElement)
0190         writer->writeEndElement();
0191 }
0192 
0193 //! Load from XML
0194 bool Line::load(XmlStreamReader* reader, bool preview) {
0195     if (preview)
0196         return true;
0197 
0198     Q_D(Line);
0199     QString str;
0200     auto attribs = reader->attributes();
0201 
0202     if (d->histogramLineTypeAvailable)
0203         READ_INT_VALUE("type", histogramLineType, Histogram::LineType);
0204 
0205     if (d->prefix == QLatin1String("DropLine"))
0206         READ_INT_VALUE("type", dropLineType, XYCurve::DropLineType);
0207 
0208     READ_QPEN(d->pen);
0209     d->style = d->pen.style();
0210     d->color = d->pen.color();
0211     d->width = d->pen.widthF();
0212 
0213     READ_DOUBLE_VALUE("opacity", opacity);
0214 
0215     return true;
0216 }
0217 
0218 // ##############################################################################
0219 // #########################  Theme management ##################################
0220 // ##############################################################################
0221 void Line::loadThemeConfig(const KConfigGroup& group) {
0222     Q_D(const Line);
0223     const auto& themeColor = group.readEntry(d->prefix + QStringLiteral("Color"), QColor(Qt::black));
0224     loadThemeConfig(group, themeColor);
0225 }
0226 
0227 void Line::loadThemeConfig(const KConfigGroup& group, const QColor& themeColor) {
0228     Q_D(const Line);
0229     setStyle((Qt::PenStyle)group.readEntry(d->prefix + QStringLiteral("Style"), (int)Qt::SolidLine));
0230     setWidth(group.readEntry(d->prefix + QStringLiteral("Width"), Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
0231     setColor(themeColor);
0232     setOpacity(group.readEntry(d->prefix + QStringLiteral("Opacity"), 1.0));
0233 }
0234 
0235 void Line::saveThemeConfig(KConfigGroup& group) const {
0236     Q_D(const Line);
0237     group.writeEntry(d->prefix + QStringLiteral("Style"), static_cast<int>(d->pen.style()));
0238     group.writeEntry(d->prefix + QStringLiteral("Width"), d->pen.widthF());
0239     group.writeEntry(d->prefix + QStringLiteral("Color"), d->pen.color());
0240     group.writeEntry(d->prefix + QStringLiteral("Opacity"), d->opacity);
0241 }