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"