File indexing completed on 2025-01-26 03:34:15
0001 /* 0002 File : Value.cpp 0003 Project : LabPlot 0004 Description : Value 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2022-2023 Alexander Semke <alexander.semke@web.de> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 /*! 0011 \class Value 0012 \brief This class contains the properties of values that are shown besides the data points in XYCurve, etc. 0013 0014 \ingroup worksheet 0015 */ 0016 0017 #include "Value.h" 0018 #include "ValuePrivate.h" 0019 #include "backend/core/AbstractColumn.h" 0020 #include "backend/lib/XmlStreamReader.h" 0021 #include "backend/lib/commandtemplates.h" 0022 0023 #include <KConfigGroup> 0024 #include <KLocalizedString> 0025 #include <QPainter> 0026 0027 Value::Value(const QString& name) 0028 : AbstractAspect(name, AspectType::AbstractAspect) 0029 , d_ptr(new ValuePrivate(this)) { 0030 } 0031 0032 Value::~Value() { 0033 delete d_ptr; 0034 } 0035 0036 void Value::init(const KConfigGroup& group) { 0037 Q_D(Value); 0038 0039 d->type = (Value::Type)group.readEntry("ValueType", (int)Value::NoValues); 0040 d->position = (Value::Position)group.readEntry("ValuePosition", (int)Value::Position::Above); 0041 d->distance = group.readEntry("ValueDistance", Worksheet::convertToSceneUnits(5, Worksheet::Unit::Point)); 0042 d->rotationAngle = group.readEntry("ValueRotation", 0.0); 0043 d->opacity = group.readEntry("ValueOpacity", 1.0); 0044 d->numericFormat = group.readEntry("ValueNumericFormat", "f").at(0).toLatin1(); 0045 d->precision = group.readEntry("ValuePrecision", 2); 0046 d->dateTimeFormat = group.readEntry("ValueDateTimeFormat", "yyyy-MM-dd"); 0047 d->prefix = group.readEntry("ValuePrefix", ""); 0048 d->suffix = group.readEntry("ValueSuffix", ""); 0049 auto defaultFont = QFont(); 0050 d->font = group.readEntry("ValueFont", QFont()); 0051 d->font.setPixelSize(Worksheet::convertToSceneUnits(defaultFont.pointSizeF(), Worksheet::Unit::Point)); 0052 d->color = group.readEntry("ValueColor", QColor(Qt::black)); 0053 } 0054 0055 void Value::draw(QPainter* painter, const QVector<QPointF>& points, const QVector<QString>& strings) { 0056 Q_D(Value); 0057 0058 if (d->type == Value::NoValues) 0059 return; 0060 0061 painter->setOpacity(d->opacity); 0062 painter->setPen(QPen(d->color)); 0063 painter->setFont(d->font); 0064 0065 int i = 0; 0066 for (const auto& point : points) { 0067 painter->translate(point); 0068 if (d->rotationAngle != 0.) 0069 painter->rotate(-d->rotationAngle); 0070 0071 painter->drawText(QPoint(0, 0), strings.at(i++)); 0072 0073 if (d->rotationAngle != 0.) 0074 painter->rotate(d->rotationAngle); 0075 painter->translate(-point); 0076 } 0077 } 0078 0079 // ############################################################################## 0080 // ########################## getter methods ################################## 0081 // ############################################################################## 0082 BASIC_SHARED_D_READER_IMPL(Value, Value::Type, type, type) 0083 BASIC_SHARED_D_READER_IMPL(Value, const AbstractColumn*, column, column) 0084 QString& Value::columnPath() const { 0085 D(Value); 0086 return d->columnPath; 0087 } 0088 void Value::setColumnPath(const QString& path) { 0089 D(Value); 0090 d->columnPath = path; 0091 } 0092 0093 BASIC_SHARED_D_READER_IMPL(Value, Value::Position, position, position) 0094 BASIC_SHARED_D_READER_IMPL(Value, bool, centerPositionAvailable, centerPositionAvailable) 0095 BASIC_SHARED_D_READER_IMPL(Value, double, distance, distance) 0096 BASIC_SHARED_D_READER_IMPL(Value, double, rotationAngle, rotationAngle) 0097 BASIC_SHARED_D_READER_IMPL(Value, double, opacity, opacity) 0098 BASIC_SHARED_D_READER_IMPL(Value, char, numericFormat, numericFormat) 0099 BASIC_SHARED_D_READER_IMPL(Value, int, precision, precision) 0100 BASIC_SHARED_D_READER_IMPL(Value, QString, dateTimeFormat, dateTimeFormat) 0101 BASIC_SHARED_D_READER_IMPL(Value, QString, prefix, prefix) 0102 BASIC_SHARED_D_READER_IMPL(Value, QString, suffix, suffix) 0103 BASIC_SHARED_D_READER_IMPL(Value, QColor, color, color) 0104 BASIC_SHARED_D_READER_IMPL(Value, QFont, font, font) 0105 0106 // ############################################################################## 0107 // ################# setter methods and undo commands ########################## 0108 // ############################################################################## 0109 STD_SETTER_CMD_IMPL_F_S(Value, SetType, Value::Type, type, updateValue) 0110 void Value::setType(Value::Type type) { 0111 Q_D(Value); 0112 if (type != d->type) 0113 exec(new ValueSetTypeCmd(d, type, ki18n("%1: set values type"))); 0114 } 0115 void Value::setcenterPositionAvailable(bool available) { 0116 Q_D(Value); 0117 d->centerPositionAvailable = available; 0118 } 0119 0120 STD_SETTER_CMD_IMPL_F_S(Value, SetColumn, const AbstractColumn*, column, updateValue) 0121 void Value::setColumn(const AbstractColumn* column) { 0122 Q_D(Value); 0123 if (column != d->column) { 0124 exec(new ValueSetColumnCmd(d, column, ki18n("%1: set values column"))); 0125 if (column) { 0126 connect(column, &AbstractColumn::dataChanged, this, &Value::updateRequested); 0127 connect(column->parentAspect(), &AbstractAspect::childAspectAboutToBeRemoved, this, &Value::columnAboutToBeRemoved); 0128 } 0129 } 0130 } 0131 0132 STD_SETTER_CMD_IMPL_F_S(Value, SetPosition, Value::Position, position, updateValue) 0133 void Value::setPosition(Position position) { 0134 Q_D(Value); 0135 if (position != d->position) 0136 exec(new ValueSetPositionCmd(d, position, ki18n("%1: set values position"))); 0137 } 0138 0139 STD_SETTER_CMD_IMPL_F_S(Value, SetDistance, double, distance, updateValue) 0140 void Value::setDistance(double distance) { 0141 Q_D(Value); 0142 if (distance != d->distance) 0143 exec(new ValueSetDistanceCmd(d, distance, ki18n("%1: set values distance"))); 0144 } 0145 0146 STD_SETTER_CMD_IMPL_F_S(Value, SetRotationAngle, double, rotationAngle, updateValue) 0147 void Value::setRotationAngle(double angle) { 0148 Q_D(Value); 0149 if (!qFuzzyCompare(1 + angle, 1 + d->rotationAngle)) 0150 exec(new ValueSetRotationAngleCmd(d, angle, ki18n("%1: rotate values"))); 0151 } 0152 0153 STD_SETTER_CMD_IMPL_F_S(Value, SetOpacity, double, opacity, updatePixmap) 0154 void Value::setOpacity(double opacity) { 0155 Q_D(Value); 0156 if (opacity != d->opacity) 0157 exec(new ValueSetOpacityCmd(d, opacity, ki18n("%1: set values opacity"))); 0158 } 0159 0160 STD_SETTER_CMD_IMPL_F_S(Value, SetNumericFormat, char, numericFormat, updateValue) 0161 void Value::setNumericFormat(char format) { 0162 Q_D(Value); 0163 if (format != d->numericFormat) 0164 exec(new ValueSetNumericFormatCmd(d, format, ki18n("%1: set values numeric format"))); 0165 } 0166 0167 STD_SETTER_CMD_IMPL_F_S(Value, SetPrecision, int, precision, updateValue) 0168 void Value::setPrecision(int precision) { 0169 Q_D(Value); 0170 if (precision != d->precision) 0171 exec(new ValueSetPrecisionCmd(d, precision, ki18n("%1: set values precision"))); 0172 } 0173 0174 STD_SETTER_CMD_IMPL_F_S(Value, SetDateTimeFormat, QString, dateTimeFormat, updateValue) 0175 void Value::setDateTimeFormat(const QString& format) { 0176 Q_D(Value); 0177 if (format != d->dateTimeFormat) 0178 exec(new ValueSetDateTimeFormatCmd(d, format, ki18n("%1: set values datetime format"))); 0179 } 0180 0181 STD_SETTER_CMD_IMPL_F_S(Value, SetPrefix, QString, prefix, updateValue) 0182 void Value::setPrefix(const QString& prefix) { 0183 Q_D(Value); 0184 if (prefix != d->prefix) 0185 exec(new ValueSetPrefixCmd(d, prefix, ki18n("%1: set values prefix"))); 0186 } 0187 0188 STD_SETTER_CMD_IMPL_F_S(Value, SetSuffix, QString, suffix, updateValue) 0189 void Value::setSuffix(const QString& suffix) { 0190 Q_D(Value); 0191 if (suffix != d->suffix) 0192 exec(new ValueSetSuffixCmd(d, suffix, ki18n("%1: set values suffix"))); 0193 } 0194 0195 STD_SETTER_CMD_IMPL_F_S(Value, SetFont, QFont, font, updateValue) 0196 void Value::setFont(const QFont& font) { 0197 Q_D(Value); 0198 if (font != d->font) 0199 exec(new ValueSetFontCmd(d, font, ki18n("%1: set values font"))); 0200 } 0201 0202 STD_SETTER_CMD_IMPL_F_S(Value, SetColor, QColor, color, updatePixmap) 0203 void Value::setColor(const QColor& color) { 0204 Q_D(Value); 0205 if (color != d->color) 0206 exec(new ValueSetColorCmd(d, color, ki18n("%1: set values color"))); 0207 } 0208 0209 void Value::columnAboutToBeRemoved(const AbstractAspect* aspect) { 0210 Q_D(Value); 0211 if (aspect == d->column) { 0212 d->column = nullptr; 0213 d->updateValue(); 0214 } 0215 } 0216 0217 // ############################################################################## 0218 // ####################### Private implementation ############################### 0219 // ############################################################################## 0220 ValuePrivate::ValuePrivate(Value* owner) 0221 : q(owner) { 0222 } 0223 0224 QString ValuePrivate::name() const { 0225 return q->parentAspect()->name(); 0226 } 0227 0228 void ValuePrivate::updateValue() { 0229 Q_EMIT q->updateRequested(); 0230 } 0231 0232 void ValuePrivate::updatePixmap() { 0233 Q_EMIT q->updatePixmapRequested(); 0234 } 0235 0236 // ############################################################################## 0237 // ################## Serialization/Deserialization ########################### 0238 // ############################################################################## 0239 //! Save as XML 0240 void Value::save(QXmlStreamWriter* writer) const { 0241 Q_D(const Value); 0242 0243 writer->writeStartElement(QStringLiteral("values")); 0244 writer->writeAttribute(QStringLiteral("type"), QString::number(d->type)); 0245 WRITE_COLUMN(d->column, column); 0246 writer->writeAttribute(QStringLiteral("position"), QString::number(d->position)); 0247 writer->writeAttribute(QStringLiteral("distance"), QString::number(d->distance)); 0248 writer->writeAttribute(QStringLiteral("rotation"), QString::number(d->rotationAngle)); 0249 writer->writeAttribute(QStringLiteral("opacity"), QString::number(d->opacity)); 0250 writer->writeAttribute(QStringLiteral("numericFormat"), QChar::fromLatin1(d->numericFormat)); 0251 writer->writeAttribute(QStringLiteral("dateTimeFormat"), d->dateTimeFormat); 0252 writer->writeAttribute(QStringLiteral("precision"), QString::number(d->precision)); 0253 writer->writeAttribute(QStringLiteral("prefix"), d->prefix); 0254 writer->writeAttribute(QStringLiteral("suffix"), d->suffix); 0255 WRITE_QCOLOR(d->color); 0256 WRITE_QFONT(d->font); 0257 writer->writeEndElement(); 0258 } 0259 0260 //! Load from XML 0261 bool Value::load(XmlStreamReader* reader, bool preview) { 0262 if (preview) 0263 return true; 0264 0265 Q_D(Value); 0266 QString str; 0267 0268 auto attribs = reader->attributes(); 0269 0270 READ_INT_VALUE("type", type, Value::Type); 0271 READ_COLUMN(column); 0272 READ_INT_VALUE("position", position, Value::Position); 0273 READ_DOUBLE_VALUE("distance", rotationAngle); 0274 READ_DOUBLE_VALUE("rotation", rotationAngle); 0275 READ_DOUBLE_VALUE("opacity", opacity); 0276 0277 str = attribs.value(QStringLiteral("numericFormat")).toString(); 0278 if (str.isEmpty()) 0279 reader->raiseMissingAttributeWarning(QStringLiteral("numericFormat")); 0280 else 0281 d->numericFormat = *(str.toLatin1().data()); 0282 0283 READ_STRING_VALUE("dateTimeFormat", dateTimeFormat); 0284 READ_INT_VALUE("precision", precision, int); 0285 0286 // don't produce any warning if no prefix or suffix is set (empty string is allowed here in xml) 0287 d->prefix = attribs.value(QStringLiteral("prefix")).toString(); 0288 d->suffix = attribs.value(QStringLiteral("suffix")).toString(); 0289 0290 READ_QCOLOR(d->color); 0291 READ_QFONT(d->font); 0292 0293 return true; 0294 } 0295 0296 // ############################################################################## 0297 // ######################### Theme management ################################## 0298 // ############################################################################## 0299 void Value::loadThemeConfig(const KConfigGroup& group, const QColor& themeColor) { 0300 setOpacity(group.readEntry("ValueOpacity", 1.0)); 0301 setColor(group.readEntry("ValueColor", themeColor)); 0302 } 0303 0304 void Value::saveThemeConfig(KConfigGroup& group) const { 0305 Q_D(const Value); 0306 group.writeEntry("ValueOpacity", d->opacity); 0307 group.writeEntry("ValueColor", d->color); 0308 group.writeEntry("ValueFont", d->font); 0309 }