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 }