File indexing completed on 2024-05-12 15:59:52

0001 /*
0002  * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KisTag.h"
0008 
0009 #include <QIODevice>
0010 #include <QLocale>
0011 #include <QBuffer>
0012 #include <QByteArray>
0013 #include <QStandardPaths>
0014 #include <QFile>
0015 #include <QTextCodec>
0016 #include <QTextStream>
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <kis_debug.h>
0021 
0022 const QString KisTag::s_group {"Desktop Entry"};
0023 const QString KisTag::s_type {"Type"};
0024 const QString KisTag::s_tag {"Tag"};
0025 const QString KisTag::s_name {"Name"};
0026 const QString KisTag::s_resourceType {"ResourceType"};
0027 const QString KisTag::s_url {"URL"};
0028 const QString KisTag::s_comment {"Comment"};
0029 const QString KisTag::s_defaultResources {"Default Resources"};
0030 const QString KisTag::s_desktop {"[Desktop Entry]"};
0031 
0032 class KisTag::Private {
0033 public:
0034     bool valid {false};
0035     QString url; // This is the actual tag
0036     QString name;
0037     QString comment;
0038     QMap<QString, QString> names; // The translated tag names
0039     QMap<QString, QString> comments; // The translated tag comments
0040     QStringList defaultResources; // The list of resources as defined in the tag file
0041     QString resourceType; // The resource type this tag can be applied to
0042     QString filename; // the original filename for the tag
0043     int id {-1};
0044     bool active{true};
0045 };
0046 
0047 KisTag::KisTag()
0048     : d(new Private)
0049 {
0050 }
0051 
0052 KisTag::~KisTag()
0053 {
0054 }
0055 
0056 KisTag::KisTag(const KisTag &rhs)
0057     : d(new Private)
0058 {
0059     *this = rhs;
0060 }
0061 
0062 KisTag &KisTag::operator=(const KisTag &rhs)
0063 {
0064     if (this != &rhs) {
0065         d->valid = rhs.d->valid;
0066         d->url = rhs.d->url;
0067         d->name = rhs.d->name;
0068         d->comment = rhs.d->comment;
0069         d->names = rhs.d->names;
0070         d->comments = rhs.d->comments;
0071         d->defaultResources = rhs.d->defaultResources;        d->resourceType = rhs.d->resourceType;
0072         d->filename = rhs.d->filename;
0073         d->id = rhs.d->id;
0074         d->active = rhs.d->active;
0075     }
0076     return *this;
0077 }
0078 
0079 KisTagSP KisTag::clone() const
0080 {
0081     return KisTagSP(new KisTag(*this));
0082 }
0083 
0084 QString KisTag::currentLocale()
0085 {
0086     const QStringList languages = KLocalizedString::languages();
0087     QString locale;
0088     if (languages.isEmpty()) {
0089         locale = QLocale().name();
0090     }
0091     else {
0092         locale = languages.first();
0093     }
0094     return locale;
0095 }
0096 
0097 bool KisTag::valid() const
0098 {
0099     return d->valid;
0100 }
0101 
0102 int KisTag::id() const
0103 {
0104     return d->id;
0105 }
0106 
0107 bool KisTag::active() const
0108 {
0109     return d->active;
0110 }
0111 
0112 QString KisTag::filename()
0113 {
0114     return d->filename;
0115 }
0116 
0117 void KisTag::setFilename(const QString &filename)
0118 {
0119     d->filename = filename;
0120 }
0121 
0122 QString KisTag::name(bool translated) const
0123 {
0124     if (translated && d->names.contains(currentLocale())) {
0125         return d->names[currentLocale()];
0126     }
0127     Q_ASSERT(!d->name.isEmpty());
0128     return d->name;
0129 }
0130 
0131 void KisTag::setName(const QString &name)
0132 {
0133     d->name = name;
0134 }
0135 
0136 QMap<QString, QString> KisTag::names() const
0137 {
0138     return d->names;
0139 }
0140 
0141 void KisTag::setNames(const QMap<QString, QString> &names)
0142 {
0143     d->names = names;
0144 }
0145 
0146 QString KisTag::comment(bool translated) const
0147 {
0148     if (translated && d->comments.contains(currentLocale())) {
0149         return d->comments[currentLocale()];
0150     }
0151     return d->comment;
0152 }
0153 
0154 void KisTag::setComment(const QString comment)
0155 {
0156     d->comment = comment;
0157 }
0158 
0159 QString KisTag::url() const
0160 {
0161     return d->url;
0162 }
0163 
0164 void KisTag::setUrl(const QString &url)
0165 {
0166     d->url = url;
0167 }
0168 
0169 
0170 QMap<QString, QString> KisTag::comments() const
0171 {
0172     return d->comments;
0173 }
0174 
0175 void KisTag::setComments(const QMap<QString, QString> &comments)
0176 {
0177     d->comments = comments;
0178 }
0179 
0180 QString KisTag::resourceType() const
0181 {
0182     return d->resourceType;
0183 }
0184 
0185 void KisTag::setResourceType(const QString &resourceType)
0186 {
0187     d->resourceType = resourceType;
0188 }
0189 
0190 QStringList KisTag::defaultResources() const
0191 {
0192     return d->defaultResources;
0193 }
0194 
0195 void KisTag::setDefaultResources(const QStringList &defaultResources)
0196 {
0197     d->defaultResources = defaultResources;
0198 }
0199 
0200 bool KisTag::load(QIODevice &io)
0201 {
0202     if (!io.isOpen()) {
0203         io.open(QIODevice::ReadOnly);
0204     }
0205     KIS_ASSERT(io.isOpen());
0206 
0207     setValid(false);
0208 
0209     QTextStream stream(&io);
0210     stream.setCodec("UTF-8");
0211     QStringList lines;
0212     QString line;
0213 
0214     while (stream.readLineInto(&line)) {
0215         lines << line;
0216     }
0217 
0218     if (lines.length() < 6 ) {
0219         qWarning()  << d->filename << ": Incomplete tag file" << lines.length();
0220         return false;
0221     }
0222     if (lines[0].toUpper() != s_desktop.toUpper()) {
0223         qWarning()  << d->filename << ":Invalid tag file" << lines[0];
0224         return false;
0225     }
0226 
0227     lines.removeFirst();
0228 
0229     Q_FOREACH(const QString line, lines) {
0230         if (line.isEmpty()) {
0231             continue;
0232         }
0233 
0234         if (!line.contains("=")) {
0235             qWarning() << "Found invalid line:" << line;
0236             continue;
0237         }
0238         int isPos = line.indexOf("=");
0239         QString key = line.left(isPos).trimmed();
0240         QString value = line.right(line.size() - (isPos + 1)).trimmed();
0241 
0242         if (key == s_url) {
0243             d->url = value;
0244         }
0245         else if (key == s_resourceType) {
0246             d->resourceType = value;
0247         }
0248         else if (key == s_defaultResources) {
0249             d->defaultResources = value.split(',', QString::SkipEmptyParts);
0250         }
0251         else if (key == s_name) {
0252             d->name = value;
0253         }
0254         else if (key == s_comment) {
0255             d->comment = value;
0256         }
0257         else if (key.startsWith(s_name + "[")) {
0258             int start = key.indexOf('[') + 1;
0259             int len = key.size() - (s_name.size() + 2);
0260             QString language = key.mid(start, len);
0261             d->names[language] = value;
0262         }
0263         else if (key.startsWith(s_comment + "[")) {
0264             int start = key.indexOf('[') + 1;
0265             int len = key.size() - (s_comment.size() + 2);
0266             QString language = key.mid(start, len);
0267             d->comments[language] = value;
0268         }
0269     }
0270 
0271     setValid(true);
0272 
0273     return true;
0274 }
0275 
0276 bool KisTag::save(QIODevice &io)
0277 {
0278     if (!io.isOpen()) {
0279         io.open(QIODevice::WriteOnly | QIODevice::Text);
0280     }
0281 
0282     QTextStream stream(&io);
0283     stream.setCodec("UTF-8");
0284     stream << s_desktop << '\n';
0285     stream << s_type << '=' << s_tag << '\n';
0286     stream << s_url << '=' << d->url << '\n';
0287     stream << s_resourceType << '=' << d->resourceType << '\n';
0288     stream << s_name << '=' << d->name << '\n';
0289     stream << s_comment << '=' << d->comment << '\n';
0290     stream << s_defaultResources << '=' << d->defaultResources.join(',') << '\n';
0291 
0292     Q_FOREACH(const QString &language, d->names) {
0293         stream << s_name << '[' << language << "]=" << d->names[language] << '\n';
0294     }
0295 
0296     Q_FOREACH(const QString &language, d->comments) {
0297         stream << s_comment << '[' << language << "]=" << d->comments[language] << '\n';
0298     }
0299 
0300     return true;
0301 }
0302 
0303 void KisTag::setId(int id)
0304 {
0305     d->id = id;
0306 }
0307 
0308 void KisTag::setActive(bool active)
0309 {
0310     d->active = active;
0311 }
0312 
0313 void KisTag::setValid(bool valid)
0314 {
0315     d->valid = valid;
0316 }
0317