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 }