File indexing completed on 2024-05-12 15:58:38
0001 /* 0002 * SPDX-FileCopyrightText: 2006 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2007, 2010 Cyrille Berger <cberger@cberger.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kis_properties_configuration.h" 0009 0010 0011 #include <kis_debug.h> 0012 #include <QDomDocument> 0013 #include <QString> 0014 0015 #include "kis_image.h" 0016 #include "kis_transaction.h" 0017 #include "kis_undo_adapter.h" 0018 #include "kis_painter.h" 0019 #include "kis_selection.h" 0020 #include "KoID.h" 0021 #include "kis_types.h" 0022 #include <KoColor.h> 0023 #include <KoColorModelStandardIds.h> 0024 #include <KoColorSpaceRegistry.h> 0025 0026 struct Q_DECL_HIDDEN KisPropertiesConfiguration::Private { 0027 QMap<QString, QVariant> properties; 0028 QStringList notSavedProperties; 0029 }; 0030 0031 KisPropertiesConfiguration::KisPropertiesConfiguration() : d(new Private) 0032 { 0033 } 0034 0035 KisPropertiesConfiguration::~KisPropertiesConfiguration() 0036 { 0037 delete d; 0038 } 0039 0040 KisPropertiesConfiguration::KisPropertiesConfiguration(const KisPropertiesConfiguration& rhs) 0041 : KisSerializableConfiguration(rhs) 0042 , d(new Private(*rhs.d)) 0043 { 0044 } 0045 0046 KisPropertiesConfiguration &KisPropertiesConfiguration::operator=(const KisPropertiesConfiguration &rhs) 0047 { 0048 if (&rhs != this) { 0049 *d = *rhs.d; 0050 } 0051 0052 return *this; 0053 } 0054 0055 bool KisPropertiesConfiguration::fromXML(const QString & xml, bool clear) 0056 { 0057 if (clear) { 0058 clearProperties(); 0059 } 0060 0061 QDomDocument doc; 0062 bool retval = doc.setContent(xml); 0063 if (retval) { 0064 QDomElement e = doc.documentElement(); 0065 fromXML(e); 0066 } 0067 return retval; 0068 } 0069 0070 void KisPropertiesConfiguration::fromXML(const QDomElement& e) 0071 { 0072 QDomNode n = e.firstChild(); 0073 0074 while (!n.isNull()) { 0075 // We don't nest elements in filter configuration. For now... 0076 QDomElement e = n.toElement(); 0077 if (!e.isNull()) { 0078 if (e.tagName() == "param") { 0079 // If the file contains the new type parameter introduced in Krita act on it 0080 // Else invoke old behaviour 0081 if(e.attributes().contains("type")) 0082 { 0083 QString type = e.attribute("type"); 0084 QString name = e.attribute("name"); 0085 QString value = e.text(); 0086 0087 if (type == "bytearray") { 0088 d->properties[name] = QVariant(QByteArray::fromBase64(value.toLatin1())); 0089 } 0090 else { 0091 d->properties[name] = value; 0092 } 0093 } 0094 else { 0095 d->properties[e.attribute("name")] = QVariant(e.text()); 0096 } 0097 } 0098 } 0099 n = n.nextSibling(); 0100 } 0101 //dump(); 0102 } 0103 0104 void KisPropertiesConfiguration::toXML(QDomDocument& doc, QDomElement& root) const 0105 { 0106 QMap<QString, QVariant>::ConstIterator it; 0107 for (it = d->properties.constBegin(); it != d->properties.constEnd(); ++it) { 0108 if (d->notSavedProperties.contains(it.key())) { 0109 continue; 0110 } 0111 0112 QDomElement e = doc.createElement("param"); 0113 e.setAttribute("name", QString(it.key().toLatin1())); 0114 QString type = "string"; 0115 QVariant v = it.value(); 0116 QDomText text; 0117 if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId<KisCubicCurve>()) { 0118 text = doc.createCDATASection(v.value<KisCubicCurve>().toString()); 0119 } else if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId<KoColor>()) { 0120 QDomDocument cdataDoc = QDomDocument("color"); 0121 QDomElement cdataRoot = cdataDoc.createElement("color"); 0122 cdataDoc.appendChild(cdataRoot); 0123 v.value<KoColor>().toXML(cdataDoc, cdataRoot); 0124 text = cdataDoc.createCDATASection(cdataDoc.toString()); 0125 type = "color"; 0126 } else if(v.type() == QVariant::String ) { 0127 text = doc.createCDATASection(v.toString()); // XXX: Unittest this! 0128 type = "string"; 0129 } else if(v.type() == QVariant::ByteArray ) { 0130 text = doc.createTextNode(QString::fromLatin1(v.toByteArray().toBase64())); // Arbitrary Data 0131 type = "bytearray"; 0132 } else { 0133 text = doc.createTextNode(v.toString()); 0134 type = "internal"; 0135 } 0136 e.setAttribute("type", type); 0137 e.appendChild(text); 0138 root.appendChild(e); 0139 } 0140 } 0141 0142 QString KisPropertiesConfiguration::toXML() const 0143 { 0144 QDomDocument doc = QDomDocument("params"); 0145 QDomElement root = doc.createElement("params"); 0146 doc.appendChild(root); 0147 toXML(doc, root); 0148 return doc.toString(); 0149 } 0150 0151 0152 bool KisPropertiesConfiguration::hasProperty(const QString& name) const 0153 { 0154 return d->properties.contains(name); 0155 } 0156 0157 void KisPropertiesConfiguration::setProperty(const QString & name, const QVariant & value) 0158 { 0159 if (d->properties.find(name) == d->properties.end()) { 0160 d->properties.insert(name, value); 0161 } else { 0162 d->properties[name] = value; 0163 } 0164 } 0165 0166 bool KisPropertiesConfiguration::getProperty(const QString & name, QVariant & value) const 0167 { 0168 if (d->properties.constFind(name) == d->properties.constEnd()) { 0169 return false; 0170 } else { 0171 value = d->properties.value(name); 0172 return true; 0173 } 0174 } 0175 0176 QVariant KisPropertiesConfiguration::getProperty(const QString & name) const 0177 { 0178 return d->properties.value(name, QVariant()); 0179 } 0180 0181 0182 int KisPropertiesConfiguration::getInt(const QString & name, int def) const 0183 { 0184 QVariant v = getProperty(name); 0185 if (v.isValid()) 0186 return v.toInt(); 0187 else 0188 return def; 0189 0190 } 0191 0192 double KisPropertiesConfiguration::getDouble(const QString & name, double def) const 0193 { 0194 QVariant v = getProperty(name); 0195 if (v.isValid()) 0196 return v.toDouble(); 0197 else 0198 return def; 0199 } 0200 0201 float KisPropertiesConfiguration::getFloat(const QString & name, float def) const 0202 { 0203 QVariant v = getProperty(name); 0204 if (v.isValid()) 0205 return (float)v.toDouble(); 0206 else 0207 return def; 0208 } 0209 0210 0211 bool KisPropertiesConfiguration::getBool(const QString & name, bool def) const 0212 { 0213 QVariant v = getProperty(name); 0214 if (v.isValid()) 0215 return v.toBool(); 0216 else 0217 return def; 0218 } 0219 0220 QString KisPropertiesConfiguration::getString(const QString & name, const QString & def) const 0221 { 0222 QVariant v = getProperty(name); 0223 if (v.isValid()) 0224 return v.toString(); 0225 else 0226 return def; 0227 } 0228 0229 KisCubicCurve KisPropertiesConfiguration::getCubicCurve(const QString & name, const KisCubicCurve & curve) const 0230 { 0231 QVariant v = getProperty(name); 0232 if (v.isValid()) { 0233 if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId<KisCubicCurve>()) { 0234 return v.value<KisCubicCurve>(); 0235 } else { 0236 KisCubicCurve c; 0237 c.fromString(v.toString()); 0238 return c; 0239 } 0240 } else 0241 return curve; 0242 } 0243 0244 KoColor KisPropertiesConfiguration::getColor(const QString& name, const KoColor& color) const 0245 { 0246 QVariant v = getProperty(name); 0247 0248 if (v.isValid()) { 0249 switch(v.type()) { 0250 case QVariant::UserType: 0251 { 0252 if (v.userType() == qMetaTypeId<KoColor>()) { 0253 return v.value<KoColor>(); 0254 } 0255 break; 0256 } 0257 case QVariant::String: 0258 { 0259 QDomDocument doc; 0260 if (doc.setContent(v.toString())) { 0261 QDomElement e = doc.documentElement().firstChild().toElement(); 0262 bool ok; 0263 KoColor c = KoColor::fromXML(e, Integer16BitsColorDepthID.id(), &ok); 0264 if (ok) { 0265 return c; 0266 } 0267 } 0268 else { 0269 QColor c(v.toString()); 0270 if (c.isValid()) { 0271 KoColor kc(c, KoColorSpaceRegistry::instance()->rgb8()); 0272 return kc; 0273 } 0274 } 0275 break; 0276 } 0277 case QVariant::Color: 0278 { 0279 QColor c = v.value<QColor>(); 0280 KoColor kc(c, KoColorSpaceRegistry::instance()->rgb8()); 0281 return kc; 0282 } 0283 case QVariant::Int: 0284 { 0285 QColor c(v.toInt()); 0286 if (c.isValid()) { 0287 KoColor kc(c, KoColorSpaceRegistry::instance()->rgb8()); 0288 return kc; 0289 } 0290 break; 0291 } 0292 default: 0293 ; 0294 } 0295 } 0296 return color; 0297 } 0298 0299 void KisPropertiesConfiguration::dump() const 0300 { 0301 QMap<QString, QVariant>::ConstIterator it; 0302 for (it = d->properties.constBegin(); it != d->properties.constEnd(); ++it) { 0303 if (it->type() == QVariant::ByteArray) { 0304 QByteArray ba = it->toByteArray(); 0305 0306 if (ba.size() > 32) { 0307 qDebug() << it.key() << " = " << QString("...skipped total %1 bytes...").arg(ba.size()) << it.value().typeName(); 0308 } else { 0309 qDebug() << it.key() << " = " << it.value() << it.value().typeName(); 0310 } 0311 } else { 0312 qDebug() << it.key() << " = " << it.value() << it.value().typeName(); 0313 } 0314 } 0315 0316 } 0317 0318 void KisPropertiesConfiguration::clearProperties() 0319 { 0320 d->properties.clear(); 0321 } 0322 0323 void KisPropertiesConfiguration::setPropertyNotSaved(const QString& name) 0324 { 0325 d->notSavedProperties.append(name); 0326 } 0327 0328 QMap<QString, QVariant> KisPropertiesConfiguration::getProperties() const 0329 { 0330 return d->properties; 0331 } 0332 0333 void KisPropertiesConfiguration::removeProperty(const QString & name) 0334 { 0335 d->properties.remove(name); 0336 } 0337 0338 QList<QString> KisPropertiesConfiguration::getPropertiesKeys() const 0339 { 0340 return d->properties.keys(); 0341 } 0342 0343 void KisPropertiesConfiguration::getPrefixedProperties(const QString &prefix, KisPropertiesConfiguration *config) const 0344 { 0345 const int prefixSize = prefix.size(); 0346 0347 const QList<QString> keys = getPropertiesKeys(); 0348 Q_FOREACH (const QString &key, keys) { 0349 if (key.startsWith(prefix)) { 0350 config->setProperty(key.mid(prefixSize), getProperty(key)); 0351 } 0352 } 0353 } 0354 0355 void KisPropertiesConfiguration::getPrefixedProperties(const QString &prefix, KisPropertiesConfigurationSP config) const 0356 { 0357 getPrefixedProperties(prefix, config.data()); 0358 } 0359 0360 void KisPropertiesConfiguration::setPrefixedProperties(const QString &prefix, const KisPropertiesConfiguration *config) 0361 { 0362 const QList<QString> keys = config->getPropertiesKeys(); 0363 Q_FOREACH (const QString &key, keys) { 0364 this->setProperty(prefix + key, config->getProperty(key)); 0365 } 0366 } 0367 0368 void KisPropertiesConfiguration::setPrefixedProperties(const QString &prefix, const KisPropertiesConfigurationSP config) 0369 { 0370 setPrefixedProperties(prefix, config.data()); 0371 } 0372 0373 QString KisPropertiesConfiguration::escapeString(const QString &string) 0374 { 0375 QString result = string; 0376 result.replace(";", "\\;"); 0377 result.replace("]", "\\]"); 0378 result.replace(">", "\\>"); 0379 return result; 0380 } 0381 0382 QString KisPropertiesConfiguration::unescapeString(const QString &string) 0383 { 0384 QString result = string; 0385 result.replace("\\;", ";"); 0386 result.replace("\\]", "]"); 0387 result.replace("\\>", ">"); 0388 return result; 0389 } 0390 0391 void KisPropertiesConfiguration::setProperty(const QString &name, const QStringList &value) 0392 { 0393 QStringList escapedList; 0394 escapedList.reserve(value.size()); 0395 0396 Q_FOREACH (const QString &str, value) { 0397 escapedList << escapeString(str); 0398 } 0399 0400 setProperty(name, escapedList.join(';')); 0401 } 0402 0403 QStringList KisPropertiesConfiguration::getStringList(const QString &name, const QStringList &defaultValue) const 0404 { 0405 if (!hasProperty(name)) return defaultValue; 0406 0407 const QString joined = getString(name); 0408 0409 QStringList result; 0410 0411 int afterLastMatch = -1; 0412 for (int i = 0; i < joined.size(); i++) { 0413 const bool lastChunk = i == joined.size() - 1; 0414 const bool matchedSplitter = joined[i] == ';' && (i == 0 || joined[i - 1] != '\\'); 0415 0416 if (lastChunk || matchedSplitter) { 0417 result << unescapeString(joined.mid(afterLastMatch, i - afterLastMatch + int(lastChunk && !matchedSplitter))); 0418 afterLastMatch = i + 1; 0419 } 0420 0421 if (lastChunk && matchedSplitter) { 0422 result << QString(); 0423 } 0424 } 0425 0426 return result; 0427 } 0428 0429 QStringList KisPropertiesConfiguration::getPropertyLazy(const QString &name, const QStringList &defaultValue) const 0430 { 0431 return getStringList(name, defaultValue); 0432 } 0433 0434 bool KisPropertiesConfiguration::compareTo(const KisPropertiesConfiguration* rhs) const 0435 { 0436 if (rhs == nullptr) 0437 return false; 0438 0439 for(const auto& propertyName: getPropertiesKeys()) { 0440 if (getProperty(propertyName) != rhs->getProperty(propertyName)) 0441 return false; 0442 } 0443 0444 return true; 0445 } 0446 0447 // --- factory --- 0448 0449 struct Q_DECL_HIDDEN KisPropertiesConfigurationFactory::Private { 0450 }; 0451 0452 KisPropertiesConfigurationFactory::KisPropertiesConfigurationFactory() : d(new Private) 0453 { 0454 } 0455 0456 KisPropertiesConfigurationFactory::~KisPropertiesConfigurationFactory() 0457 { 0458 delete d; 0459 } 0460 0461 KisSerializableConfigurationSP KisPropertiesConfigurationFactory::createDefault() 0462 { 0463 return new KisPropertiesConfiguration(); 0464 } 0465 0466 KisSerializableConfigurationSP KisPropertiesConfigurationFactory::create(const QDomElement& e) 0467 { 0468 KisPropertiesConfigurationSP pc = new KisPropertiesConfiguration(); 0469 pc->fromXML(e); 0470 return pc; 0471 } 0472