File indexing completed on 2024-05-12 15:56:57

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_DOM_UTILS_H
0008 #define __KIS_DOM_UTILS_H
0009 
0010 #include <float.h>
0011 
0012 #include <QPointF>
0013 #include <QVector3D>
0014 #include <QVector>
0015 #include <QDomElement>
0016 #include <QLocale>
0017 #include <QColor>
0018 
0019 #include <klocalizedstring.h>
0020 
0021 #include "kritaglobal_export.h"
0022 #include "kis_debug.h"
0023 #include "krita_container_utils.h"
0024 
0025 namespace KisDomUtils {
0026 
0027     inline QString toString(const QString &value) {
0028         return value;
0029     }
0030 
0031     template<typename T>
0032         inline QString toString(T value) {
0033         return QString::number(value);
0034     }
0035 
0036     inline QString toString(float value) {
0037         QString str;
0038         QTextStream stream;
0039         stream.setCodec("UTF-8");
0040         stream.setString(&str, QIODevice::WriteOnly);
0041         stream.setRealNumberPrecision(FLT_DIG);
0042         stream << value;
0043         return str;
0044     }
0045 
0046     inline QString toString(double value) {
0047         QString str;
0048         QTextStream stream;
0049         stream.setCodec("UTF-8");
0050         stream.setString(&str, QIODevice::WriteOnly);
0051         stream.setRealNumberPrecision(15);
0052         stream << value;
0053         return str;
0054     }
0055 
0056     inline int toInt(const QString &str, bool *ok=nullptr) {
0057         bool ok_locale = false;
0058         int value = 0;
0059 
0060         QLocale c(QLocale::German);
0061 
0062         value = str.toInt(&ok_locale);
0063         if (!ok_locale) {
0064             value = c.toInt(str, &ok_locale);
0065         }
0066 
0067         if (!ok_locale && ok == nullptr) {
0068             warnKrita << "WARNING: KisDomUtils::toInt failed:" << ppVar(str);
0069             value = 0;
0070         }
0071 
0072         if (ok != nullptr) {
0073             *ok = ok_locale;
0074         }
0075 
0076         return value;
0077     }
0078 
0079     inline double toDouble(const QString &str, bool *ok=nullptr) {
0080         bool ok_locale = false;
0081         double value = 0;
0082 
0083         QLocale c(QLocale::German);
0084 
0085         /**
0086          * A special workaround to handle ','/'.' decimal point
0087          * in different locales. Added for backward compatibility,
0088          * because we used to save qreals directly using
0089          *
0090          * e.setAttribute("w", (qreal)value),
0091          *
0092          * which did local-aware conversion.
0093          */
0094 
0095         value = str.toDouble(&ok_locale);
0096         if (!ok_locale) {
0097             value = c.toDouble(str, &ok_locale);
0098         }
0099 
0100         if (!ok_locale && ok == nullptr) {
0101             warnKrita << "WARNING: KisDomUtils::toDouble failed:" << ppVar(str);
0102             value = 0.0;
0103         }
0104 
0105         if (ok != nullptr) {
0106             *ok = ok_locale;
0107         }
0108 
0109         return value;
0110     }
0111 
0112 
0113     inline QString qColorToQString(QColor color)
0114     {
0115         // color channels will usually have 0-255
0116         QString customColor = QString::number(color.red()).append(",")
0117                              .append(QString::number(color.green())).append(",")
0118                              .append(QString::number(color.blue())).append(",")
0119                              .append(QString::number(color.alpha()));
0120 
0121         return customColor;
0122     }
0123 
0124     inline QColor qStringToQColor(QString colorString)
0125     {
0126         QStringList colorComponents = colorString.split(',');
0127         return QColor(colorComponents[0].toInt(), colorComponents[1].toInt(), colorComponents[2].toInt(), colorComponents[3].toInt());
0128     }
0129 
0130 
0131 
0132 
0133 /**
0134  * Save a value of type QRect into an XML tree. A child for \p parent
0135  * is created and assigned a tag \p tag.  The corresponding value can
0136  * be fetched from the XML using loadValue() later.
0137  *
0138  * \see loadValue()
0139  */
0140 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QRect &rc);
0141 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QRectF &rc);
0142 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QSize &size);
0143 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QPoint &pt);
0144 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QPointF &pt);
0145 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QVector3D &pt);
0146 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QTransform &t);
0147 void KRITAGLOBAL_EXPORT saveValue(QDomElement *parent, const QString &tag, const QColor &t);
0148 
0149 /**
0150  * Save a value of a scalar type into an XML tree. A child for \p parent
0151  * is created and assigned a tag \p tag.  The corresponding value can
0152  * be fetched from the XML using loadValue() later.
0153  *
0154  * \see loadValue()
0155  */
0156 template <typename T>
0157 void saveValue(QDomElement *parent, const QString &tag, T value)
0158 {
0159     QDomDocument doc = parent->ownerDocument();
0160     QDomElement e = doc.createElement(tag);
0161     parent->appendChild(e);
0162 
0163     e.setAttribute("type", "value");
0164     e.setAttribute("value", toString(value));
0165 }
0166 
0167 /**
0168  * Save a vector of values into an XML tree. A child for \p parent is
0169  * created and assigned a tag \p tag.  The values in the array should
0170  * have a type supported by saveValue() overrides. The corresponding
0171  * vector can be fetched from the XML using loadValue() later.
0172  *
0173  * \see loadValue()
0174  */
0175 template <template <class...> class Container, typename T, typename ...Args>
0176 typename std::enable_if<KritaUtils::is_container<Container<T, Args...>>::value, void>::type
0177 saveValue(QDomElement *parent, const QString &tag, const Container<T, Args...> &array)
0178 {
0179     QDomDocument doc = parent->ownerDocument();
0180     QDomElement e = doc.createElement(tag);
0181     parent->appendChild(e);
0182 
0183     e.setAttribute("type", "array");
0184 
0185     int i = 0;
0186     Q_FOREACH (const T &v, array) {
0187         saveValue(&e, QString("item_%1").arg(i++), v);
0188     }
0189 }
0190 
0191 /**
0192  * Find an element with tag \p tag which is a child of \p parent. The element should
0193  * be the only element with the provided tag in this parent.
0194  *
0195  * \return true is the element with \p tag is found and it is unique
0196  */
0197 bool KRITAGLOBAL_EXPORT findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages = 0);
0198 
0199 
0200 /**
0201  * Load an object from an XML element, which is a child of \p parent and has
0202  * a tag \p tag.
0203  *
0204  * \return true if the object is successfully loaded and is unique
0205  *
0206  * \see saveValue()
0207  */
0208 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, float *v);
0209 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, double *v);
0210 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QSize *size);
0211 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QRect *rc);
0212 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QRectF *rc);
0213 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QPoint *pt);
0214 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QPointF *pt);
0215 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QVector3D *pt);
0216 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QTransform *t);
0217 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QString *value);
0218 bool KRITAGLOBAL_EXPORT loadValue(const QDomElement &e, QColor *value);
0219 
0220 
0221 namespace Private {
0222     bool KRITAGLOBAL_EXPORT checkType(const QDomElement &e, const QString &expectedType);
0223 }
0224 
0225 
0226 /**
0227  * Load a scalar value from an XML element, which is a child of \p parent
0228  * and has a tag \p tag.
0229  *
0230  * \return true if the object is successfully loaded and is unique
0231  *
0232  * \see saveValue()
0233  */
0234 template <typename T>
0235     typename std::enable_if<std::is_arithmetic<T>::value, bool>::type
0236 loadValue(const QDomElement &e, T *value)
0237 {
0238     if (!Private::checkType(e, "value")) return false;
0239 
0240     QVariant v(e.attribute("value", "no-value"));
0241     *value = v.value<T>();
0242     return true;
0243 }
0244 
0245 /**
0246  * A special adapter method that makes vector- and tag-based methods
0247  * work with environment parameter uniformly.
0248  */
0249 template <typename T, typename E>
0250     typename std::enable_if<std::is_empty<E>::value, bool>::type
0251 loadValue(const QDomElement &parent, T *value, const E &/*env*/) {
0252     return loadValue(parent, value);
0253 }
0254 
0255 /**
0256  * Load an array from an XML element, which is a child of \p parent
0257  * and has a tag \p tag.
0258  *
0259  * \return true if the object is successfully loaded and is unique
0260  *
0261  * \see saveValue()
0262  */
0263 
0264 template <template <class ...> class Container, typename T, typename E, typename ...Args>
0265 typename std::enable_if<KritaUtils::is_appendable_container<Container<T, Args...>>::value, bool>::type
0266 loadValue(const QDomElement &e, Container<T, Args...> *array, const E &env = std::tuple<>())
0267 {
0268     if (!Private::checkType(e, "array")) return false;
0269 
0270     QDomElement child = e.firstChildElement();
0271     while (!child.isNull()) {
0272         T value;
0273         if (!loadValue(child, &value, env)) return false;
0274         array->push_back(value);
0275         child = child.nextSiblingElement();
0276     }
0277     return true;
0278 }
0279 
0280 template <template <class ...> class Container, typename T, typename E, typename F, typename ...Args>
0281 typename std::enable_if<KritaUtils::is_appendable_container<Container<T, Args...>>::value, bool>::type
0282 loadValue(const QDomElement &e, Container<T, Args...> *array, const E &env1, const F &env2)
0283 {
0284     if (!Private::checkType(e, "array")) return false;
0285 
0286     QDomElement child = e.firstChildElement();
0287     while (!child.isNull()) {
0288         T value;
0289         if (!loadValue(child, &value, env1, env2)) return false;
0290         array->push_back(value);
0291         child = child.nextSiblingElement();
0292     }
0293     return true;
0294 }
0295 
0296 template <typename T, typename E = std::tuple<>>
0297     bool loadValue(const QDomElement &parent, const QString &tag, T *value, const E &env = E())
0298 {
0299     QDomElement e;
0300     if (!findOnlyElement(parent, tag, &e)) return false;
0301 
0302     return loadValue(e, value, env);
0303 }
0304 
0305 template <typename T, typename E, typename F>
0306     bool loadValue(const QDomElement &parent, const QString &tag, T *value, const E &env1, const F &env2)
0307 {
0308     QDomElement e;
0309     if (!findOnlyElement(parent, tag, &e)) return false;
0310 
0311     return loadValue(e, value, env1, env2);
0312 }
0313 
0314 
0315 KRITAGLOBAL_EXPORT QDomElement findElementByAttibute(QDomNode parent,
0316                                                     const QString &tag,
0317                                                     const QString &attribute,
0318                                                     const QString &key);
0319 
0320 KRITAGLOBAL_EXPORT bool removeElements(QDomElement &parent, const QString &tag);
0321 
0322 }
0323 
0324 #endif /* __KIS_DOM_UTILS_H */