File indexing completed on 2025-03-09 03:58:51

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-12-17
0007  * Description : workflow manager.
0008  *
0009  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "workflowmanager.h"
0016 
0017 // Qt includes
0018 
0019 #include <QMutex>
0020 #include <QFile>
0021 #include <QDomDocument>
0022 #include <QDomElement>
0023 #include <QTextStream>
0024 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0025 #include <QTextCodec>
0026 #endif
0027 #include <QStandardPaths>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "batchtoolsfactory.h"
0033 
0034 namespace Digikam
0035 {
0036 
0037 class Q_DECL_HIDDEN WorkflowManager::Private
0038 {
0039 public:
0040 
0041     explicit Private()
0042         : modified(false),
0043           mutex   ()
0044     {
0045     }
0046 
0047     bool            modified;
0048 
0049     QList<Workflow> qList;
0050     QString         file;
0051 
0052     QMutex          mutex;
0053 };
0054 
0055 // ------------------------------------------------------------------------------------------
0056 
0057 class Q_DECL_HIDDEN WorkflowManagerCreator
0058 {
0059 public:
0060 
0061     WorkflowManager object;
0062 };
0063 
0064 Q_GLOBAL_STATIC(WorkflowManagerCreator, creator)
0065 
0066 WorkflowManager* WorkflowManager::instance()
0067 {
0068     return &creator->object;
0069 }
0070 
0071 // ------------------------------------------------------------------------------------------
0072 
0073 WorkflowManager::WorkflowManager()
0074     : d(new Private)
0075 {
0076     d->file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/queue.xml");
0077 }
0078 
0079 WorkflowManager::~WorkflowManager()
0080 {
0081     save();
0082     clear();
0083     delete d;
0084 }
0085 
0086 void WorkflowManager::insert(const Workflow& q)
0087 {
0088     if (q.title.isNull())
0089     {
0090         return;
0091     }
0092 
0093     d->modified = true;
0094     insertPrivate(q);
0095 }
0096 
0097 void WorkflowManager::remove(const Workflow& q)
0098 {
0099     if (q.title.isNull())
0100     {
0101         return;
0102     }
0103 
0104     d->modified = true;
0105     removePrivate(q);
0106 }
0107 
0108 void WorkflowManager::insertPrivate(const Workflow& q)
0109 {
0110     if (q.title.isNull())
0111     {
0112         return;
0113     }
0114 
0115     {
0116         QMutexLocker lock(&d->mutex);
0117         d->qList.append(q);
0118         qCDebug(DIGIKAM_GENERAL_LOG) << "add : " << q.title;
0119     }
0120 
0121     Q_EMIT signalQueueSettingsAdded(q.title);
0122 }
0123 
0124 void WorkflowManager::removePrivate(const Workflow& q)
0125 {
0126     if (q.title.isNull())
0127     {
0128         return;
0129     }
0130 
0131     {
0132         QMutexLocker lock(&d->mutex);
0133 
0134         for (QList<Workflow>::iterator it = d->qList.begin() ; it != d->qList.end() ; ++it)
0135         {
0136             if (it->title == q.title)
0137             {
0138                 qCDebug(DIGIKAM_GENERAL_LOG) << "Remove " << it->title << " from Workflow list";
0139                 it = d->qList.erase(it);
0140                 break;
0141             }
0142         }
0143     }
0144 
0145     Q_EMIT signalQueueSettingsRemoved(q.title);
0146 }
0147 
0148 Workflow WorkflowManager::findByTitle(const QString& title) const
0149 {
0150     QMutexLocker lock(&d->mutex);
0151 
0152     Q_FOREACH (const Workflow& q, d->qList)
0153     {
0154         if (q.title == title)
0155         {    // cppcheck-suppress useStlAlgorithm
0156             return q;
0157         }
0158     }
0159 
0160     return Workflow();
0161 }
0162 
0163 void WorkflowManager::clear()
0164 {
0165     QList<Workflow> oldQueues = d->qList;
0166 
0167     {
0168         QMutexLocker lock(&d->mutex);
0169         d->qList.clear();
0170     }
0171 
0172     Q_FOREACH (const Workflow& q, d->qList)
0173     {
0174         Q_EMIT signalQueueSettingsRemoved(q.title);
0175     }
0176 }
0177 
0178 QList<Workflow> WorkflowManager::queueSettingsList() const
0179 {
0180     return d->qList;
0181 }
0182 
0183 bool WorkflowManager::save()
0184 {
0185     // If not modified don't save the file
0186 
0187     if (!d->modified)
0188     {
0189         return true;
0190     }
0191 
0192     QDomDocument doc(QLatin1String("queuelist"));
0193     doc.setContent(QString::fromUtf8("<!DOCTYPE XMLQueueList><queuelist version=\"2.0\" client=\"digikam\" encoding=\"UTF-8\"/>"));
0194     QDomElement docElem = doc.documentElement();
0195 
0196     {
0197         QMutexLocker lock(&d->mutex);
0198 
0199         Q_FOREACH (const Workflow& q, d->qList)
0200         {
0201             QDomElement elm = doc.createElement(QLatin1String("queue"));
0202             QDomElement data;
0203 
0204             data = doc.createElement(QLatin1String("queuetitle"));
0205             data.setAttribute(QLatin1String("value"), q.title);
0206             elm.appendChild(data);
0207 
0208             data = doc.createElement(QLatin1String("queuedesc"));
0209             data.setAttribute(QLatin1String("value"), q.desc);
0210             elm.appendChild(data);
0211 
0212             data = doc.createElement(QLatin1String("renamingparser"));
0213             data.setAttribute(QLatin1String("value"), q.qSettings.renamingParser);
0214             elm.appendChild(data);
0215 
0216             data = doc.createElement(QLatin1String("useoriginalalbum"));
0217             data.setAttribute(QLatin1String("value"), q.qSettings.useOrgAlbum);
0218             elm.appendChild(data);
0219 
0220             data = doc.createElement(QLatin1String("saveasnewversion"));
0221             data.setAttribute(QLatin1String("value"), q.qSettings.saveAsNewVersion);
0222             elm.appendChild(data);
0223 
0224             data = doc.createElement(QLatin1String("usemulticorecpu"));
0225             data.setAttribute(QLatin1String("value"), q.qSettings.useMultiCoreCPU);
0226             elm.appendChild(data);
0227 
0228             data = doc.createElement(QLatin1String("workingurl"));
0229             data.setAttribute(QLatin1String("value"), q.qSettings.workingUrl.toLocalFile());
0230             elm.appendChild(data);
0231 
0232             data = doc.createElement(QLatin1String("conflictrule"));
0233             data.setAttribute(QLatin1String("value"), q.qSettings.conflictRule);
0234             elm.appendChild(data);
0235 
0236             data = doc.createElement(QLatin1String("renamingrule"));
0237             data.setAttribute(QLatin1String("value"), q.qSettings.renamingRule);
0238             elm.appendChild(data);
0239 
0240             data = doc.createElement(QLatin1String("rawloadingrule"));
0241             data.setAttribute(QLatin1String("value"), q.qSettings.rawLoadingRule);
0242             elm.appendChild(data);
0243 
0244             data = doc.createElement(QLatin1String("jpegcompression"));
0245             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.JPEGCompression);
0246             elm.appendChild(data);
0247 
0248             data = doc.createElement(QLatin1String("jpegsubsampling"));
0249             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.JPEGSubSampling);
0250             elm.appendChild(data);
0251 
0252             data = doc.createElement(QLatin1String("pngcompression"));
0253             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.PNGCompression);
0254             elm.appendChild(data);
0255 
0256             data = doc.createElement(QLatin1String("tiffcompression"));
0257             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.TIFFCompression);
0258             elm.appendChild(data);
0259 
0260             data = doc.createElement(QLatin1String("jpeg2000lossless"));
0261             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.JPEG2000LossLess);
0262             elm.appendChild(data);
0263 
0264             data = doc.createElement(QLatin1String("jpeg2000compression"));
0265             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.JPEG2000Compression);
0266             elm.appendChild(data);
0267 
0268             data = doc.createElement(QLatin1String("pgflossless"));
0269             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.PGFLossLess);
0270             elm.appendChild(data);
0271 
0272             data = doc.createElement(QLatin1String("pgfcompression"));
0273             data.setAttribute(QLatin1String("value"), q.qSettings.ioFileSettings.PGFCompression);
0274             elm.appendChild(data);
0275 
0276             // ----------------------
0277 
0278             QDomElement rawdecodingsettings = doc.createElement(QLatin1String("rawdecodingsettings"));
0279             elm.appendChild(rawdecodingsettings);
0280             DRawDecoding::decodingSettingsToXml(q.qSettings.rawDecodingSettings, rawdecodingsettings);
0281 
0282             // ----------------------
0283 
0284             Q_FOREACH (const BatchToolSet& set, q.aTools)
0285             {
0286                 QDomElement batchtool = doc.createElement(QLatin1String("tool"));
0287                 elm.appendChild(batchtool);
0288 
0289                 data = doc.createElement(QLatin1String("toolname"));
0290                 data.setAttribute(QLatin1String("value"), set.name);
0291                 batchtool.appendChild(data);
0292 
0293                 data = doc.createElement(QLatin1String("toolgroup"));
0294                 data.setAttribute(QLatin1String("value"), set.group);
0295                 batchtool.appendChild(data);
0296 
0297                 data = doc.createElement(QLatin1String("index"));
0298                 data.setAttribute(QLatin1String("value"), set.index);
0299                 batchtool.appendChild(data);
0300 
0301                 data = doc.createElement(QLatin1String("version"));
0302                 data.setAttribute(QLatin1String("value"), set.version);
0303                 batchtool.appendChild(data);
0304 
0305                 for (BatchToolSettings::const_iterator it = set.settings.constBegin() ; it != set.settings.constEnd() ; ++it)
0306                 {
0307                     data = doc.createElement(QLatin1String("parameter"));
0308                     data.setAttribute(QLatin1String("name"),  it.key());
0309                     data.setAttribute(QLatin1String("type"),  QString::fromUtf8(it.value().typeName()));
0310                     data.setAttribute(QLatin1String("value"), it.value().toString());
0311                     batchtool.appendChild(data);
0312                 }
0313             }
0314 
0315             docElem.appendChild(elm);
0316         }
0317     }
0318 
0319     QFile file(d->file);
0320 
0321     if (!file.open(QIODevice::WriteOnly))
0322     {
0323         qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot open XML file to store Workflow";
0324         return false;
0325     }
0326 
0327     QTextStream stream(&file);
0328 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0329     // In Qt5 only. Qt6 uses UTF-8 by default.
0330     stream.setCodec(QTextCodec::codecForName("UTF-8"));
0331 #endif
0332     stream.setAutoDetectUnicode(true);
0333     stream << doc.toString(4);
0334     file.close();
0335 
0336     return true;
0337 }
0338 
0339 bool WorkflowManager::load(QStringList& failed)
0340 {
0341     d->modified = false;
0342 
0343     QFile file(d->file);
0344 
0345     if (file.exists())
0346     {
0347         if (!file.open(QIODevice::ReadOnly))
0348         {
0349             qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot open XML file to load Workflow";
0350             return false;
0351         }
0352 
0353         QDomDocument doc(QLatin1String("queuelist"));
0354 
0355         if (!doc.setContent(&file))
0356         {
0357             qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot load Workflow XML file";
0358             file.close();
0359             return false;
0360         }
0361 
0362         QDomElement docElem = doc.documentElement();
0363 
0364         if (docElem.tagName() != QLatin1String("queuelist"))
0365         {
0366             qCDebug(DIGIKAM_GENERAL_LOG) << "Workflow XML file do not content Queue List data";
0367             file.close();
0368             return false;
0369         }
0370 
0371         for (QDomNode n = docElem.firstChild() ; !n.isNull() ; n = n.nextSibling())
0372         {
0373             QDomElement e = n.toElement();
0374 
0375             if (e.isNull())
0376             {
0377                 continue;
0378             }
0379 
0380             if (e.tagName() != QLatin1String("queue"))
0381             {
0382                 continue;
0383             }
0384 
0385             Workflow q;
0386             bool     versionOk = true;
0387 
0388             for (QDomNode n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling())
0389             {
0390                 QDomElement e2 = n2.toElement();
0391 
0392                 if (e2.isNull())
0393                 {
0394                     continue;
0395                 }
0396 
0397                 QString name2 = e2.tagName();
0398                 QString val2  = e2.attribute(QLatin1String("value"));
0399                 bool ok       = true;
0400 
0401                 if      (name2 == QLatin1String("queuetitle"))
0402                 {
0403                     q.title = val2;
0404                 }
0405                 else if (name2 == QLatin1String("queuedesc"))
0406                 {
0407                     q.desc = val2;
0408                 }
0409                 else if (name2 == QLatin1String("renamingparser"))
0410                 {
0411                     q.qSettings.renamingParser = val2;
0412                 }
0413                 else if (name2 == QLatin1String("useoriginalalbum"))
0414                 {
0415                     q.qSettings.useOrgAlbum = (bool)val2.toUInt(&ok);
0416                 }
0417                 else if (name2 == QLatin1String("saveasnewversion"))
0418                 {
0419                     q.qSettings.saveAsNewVersion = (bool)val2.toUInt(&ok);
0420                 }
0421                 else if (name2 == QLatin1String("usemulticorecpu"))
0422                 {
0423                     q.qSettings.useMultiCoreCPU = (bool)val2.toUInt(&ok);
0424                 }
0425                 else if (name2 == QLatin1String("workingurl"))
0426                 {
0427                     q.qSettings.workingUrl = QUrl::fromLocalFile(val2);
0428                 }
0429                 else if (name2 == QLatin1String("conflictrule"))
0430                 {
0431                     q.qSettings.conflictRule = (FileSaveConflictBox::ConflictRule)val2.toUInt(&ok);
0432                 }
0433                 else if (name2 == QLatin1String("renamingrule"))
0434                 {
0435                     q.qSettings.renamingRule = (QueueSettings::RenamingRule)val2.toUInt(&ok);
0436                 }
0437                 else if (name2 == QLatin1String("rawloadingrule"))
0438                 {
0439                     q.qSettings.rawLoadingRule = (QueueSettings::RawLoadingRule)val2.toUInt(&ok);
0440                 }
0441                 else if (name2 == QLatin1String("jpegcompression"))
0442                 {
0443                     q.qSettings.ioFileSettings.JPEGCompression = val2.toUInt(&ok);
0444                 }
0445                 else if (name2 == QLatin1String("jpegsubsampling"))
0446                 {
0447                     q.qSettings.ioFileSettings.JPEGSubSampling = val2.toUInt(&ok);
0448                 }
0449                 else if (name2 == QLatin1String("pngcompression"))
0450                 {
0451                     q.qSettings.ioFileSettings.PNGCompression = val2.toUInt(&ok);
0452                 }
0453                 else if (name2 == QLatin1String("tiffcompression"))
0454                 {
0455                     q.qSettings.ioFileSettings.TIFFCompression = (bool)val2.toUInt(&ok);
0456                 }
0457                 else if (name2 == QLatin1String("jpeg2000lossless"))
0458                 {
0459                     q.qSettings.ioFileSettings.JPEG2000LossLess = (bool)val2.toUInt(&ok);
0460                 }
0461                 else if (name2 == QLatin1String("jpeg2000compression"))
0462                 {
0463                     q.qSettings.ioFileSettings.JPEG2000Compression = val2.toUInt(&ok);
0464                 }
0465                 else if (name2 == QLatin1String("pgflossless"))
0466                 {
0467                     q.qSettings.ioFileSettings.PGFLossLess = (bool)val2.toUInt(&ok);
0468                 }
0469                 else if (name2 == QLatin1String("pgfcompression"))
0470                 {
0471                     q.qSettings.ioFileSettings.PGFCompression = val2.toUInt(&ok);
0472                 }
0473                 else if (name2 == QLatin1String("rawdecodingsettings"))
0474                 {
0475                     DRawDecoding::decodingSettingsFromXml(e2, q.qSettings.rawDecodingSettings);
0476                 }
0477                 else if (name2 == QLatin1String("tool"))
0478                 {
0479                     BatchToolSet set;
0480 
0481                     for (QDomNode n3 = e2.firstChild() ; !n3.isNull() ; n3 = n3.nextSibling())
0482                     {
0483                         QDomElement e3 = n3.toElement();
0484 
0485                         if (e3.isNull())
0486                         {
0487                             continue;
0488                         }
0489 
0490                         QString name3  = e3.tagName();
0491                         QString val3   = e3.attribute(QLatin1String("value"));
0492 
0493                         if      (name3 == QLatin1String("toolname"))
0494                         {
0495                             set.name = val3;
0496                         }
0497                         else if (name3 == QLatin1String("toolgroup"))
0498                         {
0499                             set.group = (BatchTool::BatchToolGroup)val3.toInt(&ok);
0500                         }
0501                         else if (name3 == QLatin1String("index"))
0502                         {
0503                             set.index = val3.toInt(&ok);
0504                         }
0505                         else if (name3 == QLatin1String("version"))
0506                         {
0507                             set.version = val3.toInt(&ok);
0508                         }
0509                         else if (name3 == QLatin1String("parameter"))
0510                         {
0511                             QString pname = e3.attribute(QLatin1String("name"));
0512                             QString type  = e3.attribute(QLatin1String("type"));
0513                             QVariant var(val3);
0514 
0515 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0516 
0517                             var.convert(QMetaType().fromName(type.toLatin1().constData()));
0518 
0519 #else
0520 
0521                             var.convert(QVariant::nameToType(type.toLatin1().constData()));
0522 
0523 #endif
0524 
0525 /*
0526                             qCDebug(DIGIKAM_GENERAL_LOG) << "name=" << pname << " :: "
0527                                                          << "type=" << type << " :: "
0528                                                          << "value=" << val3 << " :: "
0529                                                          << "QVariant=" << var;
0530 */
0531                             set.settings.insert(pname, var);
0532                         }
0533                     }
0534 
0535                     BatchTool* const tool = BatchToolsFactory::instance()->findTool(set.name, set.group);
0536 
0537                     if (tool)
0538                     {
0539                         if (set.version == tool->toolVersion())
0540                         {
0541                             q.aTools.append(set);
0542                         }
0543                     }
0544                     else
0545                     {
0546                         versionOk   = false;
0547                         d->modified = true;
0548                     }
0549                 }
0550             }
0551 
0552             if (versionOk)
0553             {
0554                 // We only insert workflow if all tools version are compatible
0555 
0556                 insertPrivate(q);
0557             }
0558             else
0559             {
0560                 failed.append(QString::fromUtf8("%1 [%2]").arg(q.title).arg(q.desc));
0561             }
0562         }
0563 
0564         file.close();
0565         return true;
0566     }
0567     else
0568     {
0569         return false;
0570     }
0571 }
0572 
0573 } // namespace Digikam
0574 
0575 #include "moc_workflowmanager.cpp"