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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-02-13
0007  * Description : tabbed queue items list.
0008  *
0009  * SPDX-FileCopyrightText: 2009-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 "queuepool.h"
0016 
0017 // Qt includes
0018 
0019 #include <QApplication>
0020 #include <QIcon>
0021 
0022 // KDE includes
0023 
0024 #include <klocalizedstring.h>
0025 
0026 // Local includes
0027 
0028 #include "digikam_debug.h"
0029 #include "dmessagebox.h"
0030 #include "applicationsettings.h"
0031 #include "iccsettings.h"
0032 #include "metaenginesettings.h"
0033 #include "ddragobjects.h"
0034 #include "queuelist.h"
0035 #include "workflowmanager.h"
0036 #include "workflowdlg.h"
0037 #include "queuesettings.h"
0038 #include "loadingcacheinterface.h"
0039 
0040 namespace Digikam
0041 {
0042 
0043 QueuePoolBar::QueuePoolBar(QWidget* const parent)
0044     : QTabBar(parent)
0045 {
0046   setAcceptDrops(true);
0047   setMouseTracking(true);
0048 }
0049 
0050 QueuePoolBar::~QueuePoolBar()
0051 {
0052 }
0053 
0054 void QueuePoolBar::dragEnterEvent(QDragEnterEvent* e)
0055 {
0056 
0057 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0058 
0059     int tab = tabAt(e->position().toPoint());
0060 
0061 #else
0062 
0063     int tab = tabAt(e->pos());
0064 
0065 #endif
0066 
0067     if (tab != -1)
0068     {
0069         bool accept = false;
0070 
0071         // The receivers of the testCanDecode() signal has to adjust 'accept' accordingly.
0072 
0073         Q_EMIT signalTestCanDecode(e, accept);
0074 
0075         e->setAccepted(accept);
0076 
0077         return;
0078     }
0079 
0080     QTabBar::dragEnterEvent(e);
0081 }
0082 
0083 void QueuePoolBar::dragMoveEvent(QDragMoveEvent* e)
0084 {
0085 
0086 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0087 
0088     int tab = tabAt(e->position().toPoint());
0089 
0090 #else
0091 
0092     int tab = tabAt(e->pos());
0093 
0094 #endif
0095 
0096     if (tab != -1)
0097     {
0098         bool accept = false;
0099 
0100         // The receivers of the testCanDecode() signal has to adjust 'accept' accordingly.
0101 
0102         Q_EMIT signalTestCanDecode(e, accept);
0103 
0104         e->setAccepted(accept);
0105 
0106         return;
0107     }
0108 
0109     QTabBar::dragMoveEvent(e);
0110 }
0111 
0112 // --------------------------------------------------------------------------------------------
0113 
0114 QueuePool::QueuePool(QWidget* const parent)
0115     : QTabWidget(parent)
0116 {
0117     setTabBar(new QueuePoolBar(this));
0118     setTabsClosable(false);
0119     setAcceptDrops(true);
0120     slotAddQueue();
0121 
0122     connect(this, SIGNAL(currentChanged(int)),
0123             this, SLOT(slotQueueSelected(int)));
0124 
0125     connect(this, SIGNAL(tabCloseRequested(int)),
0126             this, SLOT(slotCloseQueueRequest(int)));
0127 
0128     connect(tabBar(), SIGNAL(signalTestCanDecode(const QDragMoveEvent*,bool&)),
0129             this, SLOT(slotTestCanDecode(const QDragMoveEvent*,bool&)));
0130 
0131     // -- FileWatch connections ------------------------------
0132 
0133     LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString)));
0134 }
0135 
0136 QueuePool::~QueuePool()
0137 {
0138 }
0139 
0140 void QueuePool::keyPressEvent(QKeyEvent* event)
0141 {
0142     if (event->key() == Qt::Key_Delete)
0143     {
0144         slotRemoveSelectedItems();
0145     }
0146     else
0147     {
0148         QTabWidget::keyPressEvent(event);
0149     }
0150 }
0151 
0152 void QueuePool::setBusy(bool b)
0153 {
0154     tabBar()->setEnabled(!b);
0155 
0156     for (int i = 0 ; i < count() ; ++i)
0157     {
0158         QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
0159 
0160         if (queue)
0161         {
0162             queue->viewport()->setEnabled(!b);
0163         }
0164     }
0165 }
0166 
0167 QueueListView* QueuePool::currentQueue() const
0168 {
0169     return (dynamic_cast<QueueListView*>(currentWidget()));
0170 }
0171 
0172 QString QueuePool::currentTitle() const
0173 {
0174     return queueTitle(currentIndex());
0175 }
0176 
0177 QueueListView* QueuePool::findQueueByItemId(qlonglong id) const
0178 {
0179     for (int i = 0 ; i < count() ; ++i)
0180     {
0181         QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
0182 
0183         if (queue && queue->findItemById(id))
0184         {
0185             return queue;
0186         }
0187     }
0188 
0189     return nullptr;
0190 }
0191 
0192 void QueuePool::setItemBusy(qlonglong id)
0193 {
0194     QueueListView* const queue = findQueueByItemId(id);
0195 
0196     if (queue)
0197     {
0198         queue->setItemBusy(id);
0199     }
0200 }
0201 
0202 QueueListView* QueuePool::findQueueByIndex(int index) const
0203 {
0204     return (dynamic_cast<QueueListView*>(widget(index)));
0205 }
0206 
0207 QMap<int, QString> QueuePool::queuesMap() const
0208 {
0209     QMap<int, QString> map;
0210 
0211     for (int i = 0 ; i < count() ; ++i)
0212     {
0213         map.insert(i, queueTitle(i));
0214     }
0215 
0216     return map;
0217 }
0218 
0219 QString QueuePool::queueTitle(int index) const
0220 {
0221     // NOTE: clean up tab title. With QTabWidget, it sound like mistake is added, as '&' and space.
0222     // NOTE update, & is an usability helper to allow keyboard access -teemu
0223 
0224     return (tabText(index).remove(QLatin1Char('&')).remove(QLatin1Char(' ')));
0225 }
0226 
0227 void QueuePool::slotAddQueue()
0228 {
0229     QueueListView* const queue = new QueueListView(this);
0230 
0231     if (!queue)
0232     {
0233         return;
0234     }
0235 
0236     int index = addTab(queue, QIcon::fromTheme(QLatin1String("run-build")), QString::fromUtf8("#%1").arg(count() + 1));
0237 
0238     connect(queue, SIGNAL(signalQueueContentsChanged()),
0239             this, SIGNAL(signalQueueContentsChanged()));
0240 
0241     connect(queue, SIGNAL(itemSelectionChanged()),
0242             this, SIGNAL(signalItemSelectionChanged()));
0243 
0244     Q_EMIT signalQueuePoolChanged();
0245 
0246     setCurrentIndex(index);
0247 }
0248 
0249 QueuePoolItemsList QueuePool::queueItemsList(int index) const
0250 {
0251     QueuePoolItemsList qpool;
0252 
0253     QueueListView* const queue = dynamic_cast<QueueListView*>(widget(index));
0254 
0255     if (queue)
0256     {
0257         ItemInfoList list = queue->pendingItemsList();
0258 
0259         for (ItemInfoList::const_iterator it = list.constBegin() ; it != list.constEnd() ; ++it)
0260         {
0261             ItemInfo info = *it;
0262             ItemInfoSet set(index, info);
0263             qpool.append(set);
0264         }
0265     }
0266 
0267     return qpool;
0268 }
0269 
0270 int QueuePool::totalPendingItems() const
0271 {
0272     int items = 0;
0273 
0274     for (int i = 0 ; i < count() ; ++i)
0275     {
0276         QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
0277 
0278         if (queue)
0279         {
0280             items += queue->pendingItemsCount();
0281         }
0282     }
0283 
0284     return items;
0285 }
0286 
0287 int QueuePool::totalPendingTasks() const
0288 {
0289     int tasks = 0;
0290 
0291     for (int i = 0 ; i < count() ; ++i)
0292     {
0293         QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
0294 
0295         if (queue)
0296         {
0297             tasks += queue->pendingTasksCount();
0298         }
0299     }
0300 
0301     return tasks;
0302 }
0303 
0304 void QueuePool::slotRemoveCurrentQueue()
0305 {
0306     QueueListView* const queue = currentQueue();
0307 
0308     if (!queue)
0309     {
0310         return;
0311     }
0312 
0313     removeTab(indexOf(queue));
0314 
0315     if (count() == 0)
0316     {
0317         slotAddQueue();
0318     }
0319     else
0320     {
0321         for (int i = 0; i < count(); ++i)
0322         {
0323             setTabText(i, QString::fromUtf8("#%1").arg(i + 1));
0324         }
0325     }
0326 
0327     Q_EMIT signalQueuePoolChanged();
0328 }
0329 
0330 bool QueuePool::saveWorkflow() const
0331 {
0332     QueueListView* const queue = currentQueue();
0333 
0334     if (queue)
0335     {
0336         WorkflowManager* const mngr = WorkflowManager::instance();
0337         Workflow wf;
0338         wf.qSettings = queue->settings();
0339         wf.aTools    = queue->assignedTools().m_toolsList;
0340 
0341         if (WorkflowDlg::createNew(wf))
0342         {
0343             mngr->insert(wf);
0344             mngr->save();
0345 
0346             return true;
0347         }
0348     }
0349 
0350     return false;
0351 }
0352 
0353 void QueuePool::slotClearList()
0354 {
0355     QueueListView* const queue = currentQueue();
0356 
0357     if (queue)
0358     {
0359         queue->slotClearList();
0360     }
0361 }
0362 
0363 void QueuePool::slotRemoveSelectedItems()
0364 {
0365     QueueListView* const queue = currentQueue();
0366 
0367     if (queue)
0368     {
0369         queue->slotRemoveSelectedItems();
0370     }
0371 }
0372 
0373 void QueuePool::slotRemoveItemsDone()
0374 {
0375     QueueListView* const queue = currentQueue();
0376 
0377     if (queue)
0378     {
0379         queue->slotRemoveItemsDone();
0380     }
0381 }
0382 
0383 void QueuePool::slotAddItems(const ItemInfoList& list, int queueId)
0384 {
0385     QueueListView* const queue = findQueueByIndex(queueId);
0386 
0387     if (queue)
0388     {
0389         queue->slotAddItems(list);
0390     }
0391 }
0392 
0393 void QueuePool::slotAssignedToolsChanged(const AssignedBatchTools& tools4Item)
0394 {
0395     QueueListView* const queue = currentQueue();
0396 
0397     if (queue)
0398     {
0399         queue->slotAssignedToolsChanged(tools4Item);
0400     }
0401 }
0402 
0403 void QueuePool::slotQueueSelected(int index)
0404 {
0405     QueueListView* const queue = dynamic_cast<QueueListView*>(widget(index));
0406 
0407     if (queue)
0408     {
0409         Q_EMIT signalItemSelectionChanged();
0410         Q_EMIT signalQueueSelected(index, queue->settings(), queue->assignedTools());
0411     }
0412 }
0413 
0414 void QueuePool::slotCloseQueueRequest(int index)
0415 {
0416     removeTab(index);
0417 
0418     if (count() == 0)
0419     {
0420         slotAddQueue();
0421     }
0422 
0423     Q_EMIT signalQueuePoolChanged();
0424 }
0425 
0426 void QueuePool::removeTab(int index)
0427 {
0428     QueueListView* const queue = dynamic_cast<QueueListView*>(widget(index));
0429 
0430     if (!queue)
0431     {
0432         return;
0433     }
0434 
0435     int count = queue->pendingItemsCount();
0436 
0437     if (count > 0)
0438     {
0439         int ret = QMessageBox::question(this, qApp->applicationName(),
0440                                         i18np("There is still 1 unprocessed item in \"%2\".\nDo you want to close this queue?",
0441                                               "There are still %1 unprocessed items in \"%2\".\nDo you want to close this queue?",
0442                                               count, queueTitle(index)),
0443                                         QMessageBox::Yes | QMessageBox::No);
0444 
0445         if (ret == QMessageBox::No)
0446         {
0447             return;
0448         }
0449     }
0450 
0451     QTabWidget::removeTab(index);
0452 }
0453 
0454 void QueuePool::slotTestCanDecode(const QDragMoveEvent* e, bool& accept)
0455 {
0456     int              albumID;
0457     QList<int>       albumIDs;
0458     QList<qlonglong> imageIDs;
0459     QList<QUrl>      urls;
0460 
0461     if (DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs) ||
0462         DAlbumDrag::decode(e->mimeData(), urls, albumID)           ||
0463         DTagListDrag::canDecode(e->mimeData()))
0464     {
0465         accept = true;
0466 
0467         return;
0468     }
0469 
0470     accept = false;
0471 }
0472 
0473 void QueuePool::slotSettingsChanged(const QueueSettings& settings)
0474 {
0475     QueueListView* const queue = currentQueue();
0476 
0477     if (queue)
0478     {
0479         queue->setSettings(settings);
0480     }
0481 }
0482 
0483 bool QueuePool::customRenamingRulesAreValid() const
0484 {
0485     QStringList list;
0486 
0487     for (int i = 0 ; i < count() ; ++i)
0488     {
0489         QueueListView* const queue = findQueueByIndex(i);
0490 
0491         if (queue)
0492         {
0493             if (queue->settings().renamingRule == QueueSettings::CUSTOMIZE &&
0494                 queue->settings().renamingParser.isEmpty())
0495             {
0496                 list.append(queueTitle(i));
0497             }
0498         }
0499     }
0500 
0501     if (!list.isEmpty())
0502     {
0503         DMessageBox::showInformationList(QMessageBox::Critical,
0504                                          qApp->activeWindow(),
0505                                          qApp->applicationName(),
0506                                          i18n("Custom renaming rules are invalid for Queues listed below. "
0507                                               "Please fix them."),
0508                                          list);
0509         return false;
0510     }
0511 
0512     return true;
0513 }
0514 
0515 bool QueuePool::assignedBatchToolsListsAreValid() const
0516 {
0517     QStringList list;
0518 
0519     for (int i = 0 ; i < count() ; ++i)
0520     {
0521         QueueListView* const queue = findQueueByIndex(i);
0522 
0523         if (queue)
0524         {
0525             if (queue->assignedTools().m_toolsList.isEmpty())
0526             {
0527                 list.append(queueTitle(i));
0528             }
0529         }
0530     }
0531 
0532     if (!list.isEmpty())
0533     {
0534         DMessageBox::showInformationList(QMessageBox::Critical,
0535                                          qApp->activeWindow(),
0536                                          qApp->applicationName(),
0537                                          i18n("Assigned batch tools list is empty for Queues listed below. "
0538                                               "Please assign tools."),
0539                                          list);
0540         return false;
0541     }
0542 
0543     return true;
0544 }
0545 
0546 void QueuePool::slotFileChanged(const QString& filePath)
0547 {
0548     for (int i = 0 ; i < count() ; ++i)
0549     {
0550         QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
0551 
0552         if (queue)
0553         {
0554             queue->reloadThumbs(QUrl::fromLocalFile(filePath));
0555         }
0556     }
0557 }
0558 
0559 void QueuePool::applySettings()
0560 {
0561     for (int i = 0 ; i < count() ; ++i)
0562     {
0563         QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
0564 
0565         if (queue)
0566         {
0567             // Show/hide tool-tips settings.
0568 
0569             queue->setEnableToolTips(ApplicationSettings::instance()->getShowToolTips());
0570 
0571             // Reset Exif Orientation settings.
0572 
0573             QueueSettings prm = queue->settings();
0574             prm.exifSetOrientation = MetaEngineSettings::instance()->settings().exifRotate;
0575 
0576             // Apply Color Management rules to RAW images decoding settings
0577 
0578             // If digiKam Color Management is enable, no need to correct color of decoded RAW image,
0579             // else, sRGB color workspace will be used.
0580 
0581             ICCSettingsContainer ICCSettings = IccSettings::instance()->settings();
0582 
0583             if (ICCSettings.enableCM)
0584             {
0585                 if ((ICCSettings.defaultUncalibratedBehavior & ICCSettingsContainer::AskUser) ||
0586                     (ICCSettings.defaultUncalibratedBehavior & ICCSettingsContainer::AutomaticColors))
0587                 {
0588                     prm.rawDecodingSettings.outputColorSpace = DRawDecoderSettings::CUSTOMOUTPUTCS;
0589                     prm.rawDecodingSettings.outputProfile    = ICCSettings.workspaceProfile;
0590                 }
0591                 else
0592                 {
0593                     prm.rawDecodingSettings.outputColorSpace = DRawDecoderSettings::RAWCOLOR;
0594                 }
0595             }
0596             else
0597             {
0598                 prm.rawDecodingSettings.outputColorSpace = DRawDecoderSettings::SRGB;
0599             }
0600 
0601             queue->setSettings(prm);
0602         }
0603     }
0604 }
0605 
0606 } // namespace Digikam
0607 
0608 #include "moc_queuepool.cpp"