File indexing completed on 2025-01-05 03:57:18

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-06-20
0007  * Description : metadata template manager.
0008  *
0009  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2009-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "templatemanager.h"
0017 
0018 // Qt includes
0019 
0020 #include <QFile>
0021 #include <QDomDocument>
0022 #include <QDomElement>
0023 #include <QMutex>
0024 #include <QTextStream>
0025 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0026 #include <QTextCodec>
0027 #endif
0028 #include <QStandardPaths>
0029 
0030 // Local includes
0031 
0032 #include "template.h"
0033 
0034 namespace Digikam
0035 {
0036 
0037 class Q_DECL_HIDDEN TemplateManager::Private
0038 {
0039 public:
0040 
0041     explicit Private()
0042         : modified(false),
0043           mutex   ()
0044     {
0045     }
0046 
0047     bool            modified;
0048 
0049     QList<Template> pList;
0050     QString         file;
0051 
0052     QMutex          mutex;
0053 };
0054 
0055 class Q_DECL_HIDDEN TemplateManagerCreator
0056 {
0057 public:
0058 
0059     TemplateManager object;
0060 };
0061 
0062 Q_GLOBAL_STATIC(TemplateManagerCreator, creator)
0063 
0064 TemplateManager* TemplateManager::defaultManager()
0065 {
0066     return &creator->object;
0067 }
0068 
0069 TemplateManager::TemplateManager()
0070     : d(new Private)
0071 {
0072     d->file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/template.xml");
0073 
0074     load();
0075 }
0076 
0077 TemplateManager::~TemplateManager()
0078 {
0079     save();
0080     clear();
0081     delete d;
0082 }
0083 
0084 bool TemplateManager::load()
0085 {
0086     d->modified = false;
0087 
0088     QFile file(d->file);
0089 
0090     if (!file.open(QIODevice::ReadOnly))
0091     {
0092         return false;
0093     }
0094 
0095     QDomDocument doc(QLatin1String("templatelist"));
0096 
0097     if (!doc.setContent(&file))
0098     {
0099         return false;
0100     }
0101 
0102     QDomElement docElem = doc.documentElement();
0103 
0104     if (docElem.tagName() != QLatin1String("templatelist"))
0105     {
0106         return false;
0107     }
0108 
0109     for (QDomNode n = docElem.firstChild() ; !n.isNull() ; n = n.nextSibling())
0110     {
0111         QDomElement e = n.toElement();
0112 
0113         if (e.isNull())
0114         {
0115             continue;
0116         }
0117 
0118         if (e.tagName() != QLatin1String("template"))
0119         {
0120             continue;
0121         }
0122 
0123         Template             t;
0124         IptcCoreLocationInfo locationInf;
0125         IptcCoreContactInfo  contactInf;
0126 
0127         for (QDomNode n2 = e.firstChild() ; !n2.isNull() ; n2 = n2.nextSibling())
0128         {
0129             QDomElement e2 = n2.toElement();
0130 
0131             if (e2.isNull())
0132             {
0133                 continue;
0134             }
0135 
0136             QString name2  = e2.tagName();
0137             QString val2   = e2.attribute(QLatin1String("value"));
0138 
0139             if      (name2 == QLatin1String("templatetitle"))
0140             {
0141                 t.setTemplateTitle(val2);
0142             }
0143             else if (name2 == QLatin1String("authorsposition"))
0144             {
0145                 t.setAuthorsPosition(val2);
0146             }
0147             else if (name2 == QLatin1String("credit"))
0148             {
0149                 t.setCredit(val2);
0150             }
0151             else if (name2 == QLatin1String("source"))
0152             {
0153                 t.setSource(val2);
0154             }
0155             else if (name2 == QLatin1String("instructions"))
0156             {
0157                 t.setInstructions(val2);
0158             }
0159             else if (name2 == QLatin1String("authors"))
0160             {
0161                 QStringList list;
0162 
0163                 for (QDomNode n3 = e2.firstChild() ; !n3.isNull() ; n3 = n3.nextSibling())
0164                 {
0165                     QDomElement e3 = n3.toElement();
0166                     QString key    = e3.tagName();
0167                     QString val    = e3.attribute(QLatin1String("value"));
0168 
0169                     if (key == QLatin1String("name"))
0170                     {
0171                         if (val.isEmpty())
0172                         {
0173                             continue;
0174                         }
0175 
0176                         list.append(val);
0177                     }
0178                 }
0179 
0180                 t.setAuthors(list);
0181             }
0182             else if (name2 == QLatin1String("copyright"))
0183             {
0184                 MetaEngine::AltLangMap copyrights;
0185 
0186                 for (QDomNode n3 = e2.firstChild() ; !n3.isNull() ; n3 = n3.nextSibling())
0187                 {
0188                     QDomElement e3 = n3.toElement();
0189                     QString key    = e3.tagName();
0190                     QString val    = e3.attribute(QLatin1String("value"));
0191                     copyrights.insert(key, val);
0192                 }
0193 
0194                 t.setCopyright(copyrights);
0195             }
0196             else if (name2 == QLatin1String("rightusageterms"))
0197             {
0198                 MetaEngine::AltLangMap usages;
0199 
0200                 for (QDomNode n3 = e2.firstChild() ; !n3.isNull() ; n3 = n3.nextSibling())
0201                 {
0202                     QDomElement e3 = n3.toElement();
0203                     QString key    = e3.tagName();
0204                     QString val    = e3.attribute(QLatin1String("value"));
0205                     usages.insert(key, val);
0206                 }
0207 
0208                 t.setRightUsageTerms(usages);
0209             }
0210             else if (name2 == QLatin1String("locationcountry"))
0211             {
0212                 locationInf.country = val2;
0213             }
0214             else if (name2 == QLatin1String("locationcountrycode"))
0215             {
0216                 locationInf.countryCode = val2;
0217             }
0218             else if (name2 == QLatin1String("locationprovincestate"))
0219             {
0220                 locationInf.provinceState = val2;
0221             }
0222             else if (name2 == QLatin1String("locationcity"))
0223             {
0224                 locationInf.city = val2;
0225             }
0226             else if (name2 == QLatin1String("locationlocation"))
0227             {
0228                 locationInf.location = val2;
0229             }
0230             else if (name2 == QLatin1String("contactcity"))
0231             {
0232                 contactInf.city = val2;
0233             }
0234             else if (name2 == QLatin1String("contactcountry"))
0235             {
0236                 contactInf.country = val2;
0237             }
0238             else if (name2 == QLatin1String("contactaddress"))
0239             {
0240                 contactInf.address = val2;
0241             }
0242             else if (name2 == QLatin1String("contactpostalcode"))
0243             {
0244                 contactInf.postalCode = val2;
0245             }
0246             else if (name2 == QLatin1String("contactprovincestate"))
0247             {
0248                 contactInf.provinceState = val2;
0249             }
0250             else if (name2 == QLatin1String("contactemail"))
0251             {
0252                 contactInf.email = val2;
0253             }
0254             else if (name2 == QLatin1String("contactphone"))
0255             {
0256                 contactInf.phone = val2;
0257             }
0258             else if (name2 == QLatin1String("contactweburl"))
0259             {
0260                 contactInf.webUrl = val2;
0261             }
0262             else if (name2 == QLatin1String("subjects"))
0263             {
0264                 QStringList list;
0265 
0266                 for (QDomNode n3 = e2.firstChild() ; !n3.isNull() ; n3 = n3.nextSibling())
0267                 {
0268                     QDomElement e3 = n3.toElement();
0269                     QString key    = e3.tagName();
0270                     QString val    = e3.attribute(QLatin1String("value"));
0271 
0272                     if (key == QLatin1String("subject"))
0273                     {
0274                         if (val.isEmpty())
0275                         {
0276                             continue;
0277                         }
0278 
0279                         list.append(val);
0280                     }
0281                 }
0282 
0283                 t.setIptcSubjects(list);
0284             }
0285         }
0286 
0287         t.setLocationInfo(locationInf);
0288         t.setContactInfo(contactInf);
0289         insertPrivate(t);
0290     }
0291 
0292     return true;
0293 }
0294 
0295 bool TemplateManager::save()
0296 {
0297     // If not modified don't save the file
0298 
0299     if (!d->modified)
0300     {
0301         return true;
0302     }
0303 
0304     QDomDocument doc(QLatin1String("templatelist"));
0305     doc.setContent(QLatin1String("<!DOCTYPE XMLTemplateList><templatelist version=\"2.0\" client=\"digikam\" encoding=\"UTF-8\"/>"));
0306     QDomElement docElem = doc.documentElement();
0307 
0308     {
0309         QMutexLocker lock(&d->mutex);
0310 
0311         Q_FOREACH (const Template& t, d->pList)
0312         {
0313             QDomElement elem = doc.createElement(QLatin1String("template"));
0314 
0315             QDomElement templatetitle = doc.createElement(QLatin1String("templatetitle"));
0316             templatetitle.setAttribute(QLatin1String("value"), t.templateTitle());
0317             elem.appendChild(templatetitle);
0318 
0319             QDomElement authorsposition = doc.createElement(QLatin1String("authorsposition"));
0320             authorsposition.setAttribute(QLatin1String("value"), t.authorsPosition());
0321             elem.appendChild(authorsposition);
0322 
0323             QDomElement credit = doc.createElement(QLatin1String("credit"));
0324             credit.setAttribute(QLatin1String("value"), t.credit());
0325             elem.appendChild(credit);
0326 
0327             QDomElement source = doc.createElement(QLatin1String("source"));
0328             source.setAttribute(QLatin1String("value"), t.source());
0329             elem.appendChild(source);
0330 
0331             QDomElement instructions = doc.createElement(QLatin1String("instructions"));
0332             instructions.setAttribute(QLatin1String("value"), t.instructions());
0333             elem.appendChild(instructions);
0334 
0335             QDomElement authors = doc.createElement(QLatin1String("authors"));
0336             elem.appendChild(authors);
0337 
0338             Q_FOREACH (const QString& name, t.authors())
0339             {
0340                 QDomElement e = doc.createElement(QLatin1String("name"));
0341                 e.setAttribute(QLatin1String("value"), name);
0342                 authors.appendChild(e);
0343             }
0344 
0345             QDomElement copyright = doc.createElement(QLatin1String("copyright"));
0346             elem.appendChild(copyright);
0347             MetaEngine::AltLangMap rights = t.copyright();
0348             MetaEngine::AltLangMap::const_iterator it;
0349 
0350             for (it = rights.constBegin() ; it != rights.constEnd() ; ++it)
0351             {
0352                 QDomElement e = doc.createElement(it.key());
0353                 e.setAttribute(QLatin1String("value"), it.value());
0354                 copyright.appendChild(e);
0355             }
0356 
0357             QDomElement rightusageterms = doc.createElement(QLatin1String("rightusageterms"));
0358             elem.appendChild(rightusageterms);
0359             MetaEngine::AltLangMap usages = t.rightUsageTerms();
0360             MetaEngine::AltLangMap::const_iterator it2;
0361 
0362             for (it2 = usages.constBegin() ; it2 != usages.constEnd() ; ++it2)
0363             {
0364                 QDomElement e = doc.createElement(it2.key());
0365                 e.setAttribute(QLatin1String("value"), it2.value());
0366                 rightusageterms.appendChild(e);
0367             }
0368 
0369             docElem.appendChild(elem);
0370 
0371             QDomElement locationcountry = doc.createElement(QLatin1String("locationcountry"));
0372             locationcountry.setAttribute(QLatin1String("value"), t.locationInfo().country);
0373             elem.appendChild(locationcountry);
0374 
0375             QDomElement locationcountrycode = doc.createElement(QLatin1String("locationcountrycode"));
0376             locationcountrycode.setAttribute(QLatin1String("value"), t.locationInfo().countryCode);
0377             elem.appendChild(locationcountrycode);
0378 
0379             QDomElement locationprovincestate = doc.createElement(QLatin1String("locationprovincestate"));
0380             locationprovincestate.setAttribute(QLatin1String("value"), t.locationInfo().provinceState);
0381             elem.appendChild(locationprovincestate);
0382 
0383             QDomElement locationcity = doc.createElement(QLatin1String("locationcity"));
0384             locationcity.setAttribute(QLatin1String("value"), t.locationInfo().city);
0385             elem.appendChild(locationcity);
0386 
0387             QDomElement locationlocation = doc.createElement(QLatin1String("locationlocation"));
0388             locationlocation.setAttribute(QLatin1String("value"), t.locationInfo().location);
0389             elem.appendChild(locationlocation);
0390 
0391             QDomElement contactcity = doc.createElement(QLatin1String("contactcity"));
0392             contactcity.setAttribute(QLatin1String("value"), t.contactInfo().city);
0393             elem.appendChild(contactcity);
0394 
0395             QDomElement contactcountry = doc.createElement(QLatin1String("contactcountry"));
0396             contactcountry.setAttribute(QLatin1String("value"), t.contactInfo().country);
0397             elem.appendChild(contactcountry);
0398 
0399             QDomElement contactaddress = doc.createElement(QLatin1String("contactaddress"));
0400             contactaddress.setAttribute(QLatin1String("value"), t.contactInfo().address);
0401             elem.appendChild(contactaddress);
0402 
0403             QDomElement contactpostalcode = doc.createElement(QLatin1String("contactpostalcode"));
0404             contactpostalcode.setAttribute(QLatin1String("value"), t.contactInfo().postalCode);
0405             elem.appendChild(contactpostalcode);
0406 
0407             QDomElement contactprovincestate = doc.createElement(QLatin1String("contactprovincestate"));
0408             contactprovincestate.setAttribute(QLatin1String("value"), t.contactInfo().provinceState);
0409             elem.appendChild(contactprovincestate);
0410 
0411             QDomElement contactemail = doc.createElement(QLatin1String("contactemail"));
0412             contactemail.setAttribute(QLatin1String("value"), t.contactInfo().email);
0413             elem.appendChild(contactemail);
0414 
0415             QDomElement contactphone = doc.createElement(QLatin1String("contactphone"));
0416             contactphone.setAttribute(QLatin1String("value"), t.contactInfo().phone);
0417             elem.appendChild(contactphone);
0418 
0419             QDomElement contactweburl = doc.createElement(QLatin1String("contactweburl"));
0420             contactweburl.setAttribute(QLatin1String("value"), t.contactInfo().webUrl);
0421             elem.appendChild(contactweburl);
0422 
0423             QDomElement subjects = doc.createElement(QLatin1String("subjects"));
0424             elem.appendChild(subjects);
0425 
0426             Q_FOREACH (const QString& subject, t.IptcSubjects())
0427             {
0428                 QDomElement e = doc.createElement(QLatin1String("subject"));
0429                 e.setAttribute(QLatin1String("value"), subject);
0430                 subjects.appendChild(e);
0431             }
0432         }
0433     }
0434 
0435     QFile file(d->file);
0436 
0437     if (!file.open(QIODevice::WriteOnly))
0438     {
0439         return false;
0440     }
0441 
0442     QTextStream stream(&file);
0443 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0444     // In Qt5 only. Qt6 uses UTF-8 by default.
0445     stream.setCodec(QTextCodec::codecForName("UTF-8"));
0446 #endif
0447     stream.setAutoDetectUnicode(true);
0448     stream << doc.toString();
0449     file.close();
0450 
0451     return true;
0452 }
0453 
0454 void TemplateManager::insert(const Template& t)
0455 {
0456     if (t.isNull())
0457     {
0458         return;
0459     }
0460 
0461     d->modified = true;
0462     insertPrivate(t);
0463 }
0464 
0465 void TemplateManager::remove(const Template& t)
0466 {
0467     if (t.isNull())
0468     {
0469         return;
0470     }
0471 
0472     d->modified = true;
0473     removePrivate(t);
0474 }
0475 
0476 void TemplateManager::insertPrivate(const Template& t)
0477 {
0478     if (t.isNull())
0479     {
0480         return;
0481     }
0482 
0483     {
0484         QMutexLocker lock(&d->mutex);
0485         d->pList.append(t);
0486     }
0487 
0488     Q_EMIT signalTemplateAdded(t);
0489 }
0490 
0491 void TemplateManager::removePrivate(const Template& t)
0492 {
0493     if (t.isNull())
0494     {
0495         return;
0496     }
0497 
0498     {
0499         QMutexLocker lock(&d->mutex);
0500 
0501         for (QList<Template>::iterator it = d->pList.begin() ; it != d->pList.end() ; ++it)
0502         {
0503             if (it->templateTitle() == t.templateTitle())
0504             {
0505                 it = d->pList.erase(it);
0506                 break;
0507             }
0508         }
0509     }
0510 
0511     Q_EMIT signalTemplateRemoved(t);
0512 }
0513 
0514 void TemplateManager::clear()
0515 {
0516     QList<Template> oldTemplates = d->pList;
0517 
0518     {
0519         QMutexLocker lock(&d->mutex);
0520         d->pList.clear();
0521     }
0522 
0523     Q_FOREACH (const Template& t, d->pList)
0524     {
0525         Q_EMIT signalTemplateRemoved(t);
0526     }
0527 }
0528 
0529 QList<Template> TemplateManager::templateList() const
0530 {
0531     return d->pList;
0532 }
0533 
0534 Template TemplateManager::findByTitle(const QString& title) const
0535 {
0536     QMutexLocker lock(&d->mutex);
0537 
0538     Q_FOREACH (const Template& t, d->pList)
0539     {
0540         if (t.templateTitle() == title)
0541         {    // cppcheck-suppress useStlAlgorithm
0542             return t;
0543         }
0544     }
0545 
0546     return Template();
0547 }
0548 
0549 Template TemplateManager::findByContents(const Template& tref) const
0550 {
0551     QMutexLocker lock(&d->mutex);
0552 
0553     Q_FOREACH (const Template& t, d->pList)
0554     {
0555         if (t == tref)
0556         {   // cppcheck-suppress useStlAlgorithm
0557             return t;
0558         }
0559     }
0560 
0561     return Template();
0562 }
0563 
0564 Template TemplateManager::fromIndex(int index) const
0565 {
0566     QMutexLocker lock(&d->mutex);
0567 
0568     if ((index >= 0) && (index < d->pList.size()))
0569     {
0570         return d->pList.at(index);
0571     }
0572 
0573     return Template();
0574 }
0575 
0576 } // namespace Digikam
0577 
0578 #include "moc_templatemanager.cpp"