1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2008-11-21
7  * Description : Batch Queue Manager GUI
8  *
9  * Copyright (C) 2008-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  *
11  * This program is free software; you can redistribute it
12  * and/or modify it under the terms of the GNU General
13  * Public License as published by the Free Software Foundation;
14  * either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "queuemgrwindow_p.h"
25 
26 namespace Digikam
27 {
28 
29 QueueMgrWindow* QueueMgrWindow::m_instance = nullptr;
30 
queueManagerWindow()31 QueueMgrWindow* QueueMgrWindow::queueManagerWindow()
32 {
33     if (!m_instance)
34     {
35         new QueueMgrWindow();
36     }
37 
38     return m_instance;
39 }
40 
queueManagerWindowCreated()41 bool QueueMgrWindow::queueManagerWindowCreated()
42 {
43     return m_instance;
44 }
45 
QueueMgrWindow()46 QueueMgrWindow::QueueMgrWindow()
47     : DXmlGuiWindow(nullptr),
48       d            (new Private)
49 {
50     setConfigGroupName(QLatin1String("Batch Queue Manager Settings"));
51     setXMLFile(QLatin1String("queuemgrwindowui5.rc"));
52 
53     qRegisterMetaType<BatchToolSettings>("BatchToolSettings");
54     qRegisterMetaType<BatchToolSet>("BatchToolSet");
55 
56     m_instance = this;
57     BatchToolsFactory::instance();           // Create first instance here
58     WorkflowManager::instance();             // Create first instance here
59     d->thread  = new ActionThread(this);
60 
61     setWindowFlags(Qt::Window);
62     setCaption(i18n("Batch Queue Manager"));
63 
64     // We don't want to be deleted on close
65 
66     setAttribute(Qt::WA_DeleteOnClose, false);
67     setFullScreenOptions(FS_NONE);
68 
69     // -- Build the GUI -------------------------------
70 
71     setupUserArea();
72     setupStatusBar();
73     setupActions();
74 
75     // Make signals/slots connections
76 
77     setupConnections();
78 
79     //-------------------------------------------------------------
80 
81     readSettings();
82     applySettings();
83     setAutoSaveSettings(configGroupName(), true);
84 
85     populateToolsList();
86     slotQueueContentsChanged();
87 }
88 
~QueueMgrWindow()89 QueueMgrWindow::~QueueMgrWindow()
90 {
91     m_instance = nullptr;
92     delete d;
93 }
94 
queuesMap() const95 QMap<int, QString> QueueMgrWindow::queuesMap() const
96 {
97     if (d->queuePool)
98     {
99         return d->queuePool->queuesMap();
100     }
101 
102     return QMap<int, QString>();
103 }
104 
isBusy() const105 bool QueueMgrWindow::isBusy() const
106 {
107     return d->busy;
108 }
109 
closeEvent(QCloseEvent * e)110 void QueueMgrWindow::closeEvent(QCloseEvent* e)
111 {
112     if (!e)
113     {
114         return;
115     }
116 
117     writeSettings();
118 
119     DXmlGuiWindow::closeEvent(e);
120 }
121 
setupUserArea()122 void QueueMgrWindow::setupUserArea()
123 {
124     QWidget* const mainW          = new QWidget(this);
125     QVBoxLayout* const mainLayout = new QVBoxLayout(mainW);
126 
127     // ------------------------------------------------------------------------------
128 
129     QGroupBox* const queuesBox = new QGroupBox(i18n("Queues"), mainW);
130     QVBoxLayout* const vlay1   = new QVBoxLayout(queuesBox);
131     d->queuePool               = new QueuePool(queuesBox);
132     vlay1->addWidget(d->queuePool);
133     vlay1->setContentsMargins(QMargins());
134     vlay1->setSpacing(0);
135 
136     // ------------------------------------------------------------------------------
137 
138     QGroupBox* const queueSettingsBox = new QGroupBox(i18n("Queue Settings"), mainW);
139     QVBoxLayout* const vlay2          = new QVBoxLayout(queueSettingsBox);
140     d->queueSettingsView              = new QueueSettingsView(queueSettingsBox);
141     vlay2->addWidget(d->queueSettingsView);
142     vlay2->setContentsMargins(QMargins());
143     vlay2->setSpacing(0);
144 
145     // ------------------------------------------------------------------------------
146 
147     QGroupBox* const toolsBox = new QGroupBox(i18n("Control Panel"), mainW);
148     QVBoxLayout* const vlay3  = new QVBoxLayout(toolsBox);
149     d->toolsView              = new ToolsView(toolsBox);
150     vlay3->addWidget(d->toolsView);
151     vlay3->setContentsMargins(QMargins());
152     vlay3->setSpacing(0);
153 
154     // ------------------------------------------------------------------------------
155 
156     QGroupBox* const assignBox = new QGroupBox(i18n("Assigned Tools"), mainW);
157     QVBoxLayout* const vlay4   = new QVBoxLayout(assignBox);
158     d->assignedList            = new AssignedListView(assignBox);
159     vlay4->addWidget(d->assignedList);
160     vlay4->setContentsMargins(QMargins());
161     vlay4->setSpacing(0);
162 
163     // ------------------------------------------------------------------------------
164 
165     QGroupBox* const toolSettingsBox = new QGroupBox(i18n("Tool Settings"), mainW);
166     QVBoxLayout* const vlay5         = new QVBoxLayout(toolSettingsBox);
167     d->toolSettings                  = new ToolSettingsView(toolSettingsBox);
168     vlay5->addWidget(d->toolSettings);
169     vlay5->setContentsMargins(QMargins());
170     vlay5->setSpacing(0);
171 
172     // ------------------------------------------------------------------------------
173 
174     d->topSplitter = new SidebarSplitter(mainW);
175     d->topSplitter->addWidget(queuesBox);
176     d->topSplitter->addWidget(assignBox);
177     d->topSplitter->addWidget(toolSettingsBox);
178 
179     d->bottomSplitter = new SidebarSplitter(mainW);
180     d->bottomSplitter->addWidget(queueSettingsBox);
181     d->bottomSplitter->addWidget(toolsBox);
182 
183     d->verticalSplitter = new SidebarSplitter(Qt::Vertical, mainW);
184     d->verticalSplitter->addWidget(d->topSplitter);
185     d->verticalSplitter->addWidget(d->bottomSplitter);
186 
187     mainLayout->addWidget(d->verticalSplitter);
188 
189     setCentralWidget(mainW);
190 }
191 
setupStatusBar()192 void QueueMgrWindow::setupStatusBar()
193 {
194     d->statusProgressBar = new StatusProgressBar(statusBar());
195     d->statusProgressBar->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
196     d->statusProgressBar->setMaximumHeight(fontMetrics().height() + 2);
197     d->statusProgressBar->setNotify(true);
198     d->statusProgressBar->setNotificationTitle(i18n("Batch Queue Manager"), QIcon::fromTheme(QLatin1String("run-build")));
199     statusBar()->addWidget(d->statusProgressBar, 60);
200 
201     d->statusLabel = new QLabel(statusBar());
202     d->statusLabel->setAlignment(Qt::AlignCenter);
203     d->statusLabel->setMaximumHeight(fontMetrics().height() + 2);
204     statusBar()->addWidget(d->statusLabel, 40);
205 }
206 
setupConnections()207 void QueueMgrWindow::setupConnections()
208 {
209     // -- Assigned tools list connections -----------------------------------
210 
211     connect(d->assignedList, SIGNAL(signalToolSelected(BatchToolSet)),
212             d->toolSettings, SLOT(slotToolSelected(BatchToolSet)));
213 
214     connect(d->assignedList, SIGNAL(signalAssignedToolsChanged(AssignedBatchTools)),
215             d->queuePool, SLOT(slotAssignedToolsChanged(AssignedBatchTools)));
216 
217     connect(d->toolSettings, SIGNAL(signalSettingsChanged(BatchToolSet)),
218             d->assignedList, SLOT(slotSettingsChanged(BatchToolSet)));
219 
220     connect(d->assignedList, SIGNAL(signalAssignedToolsChanged(AssignedBatchTools)),
221             this, SLOT(slotAssignedToolsChanged(AssignedBatchTools)));
222 
223     connect(d->toolsView, SIGNAL(signalAssignTools(QMultiMap<int,QString>)),
224             d->assignedList, SLOT(slotAssignTools(QMultiMap<int,QString>)));
225 
226     // -- Queued Items list connections -------------------------------------
227 
228     connect(d->queuePool, SIGNAL(signalQueueSelected(int,QueueSettings,AssignedBatchTools)),
229             d->queueSettingsView, SLOT(slotQueueSelected(int,QueueSettings,AssignedBatchTools)));
230 
231     connect(d->queuePool, SIGNAL(signalQueueSelected(int,QueueSettings,AssignedBatchTools)),
232             d->assignedList, SLOT(slotQueueSelected(int,QueueSettings,AssignedBatchTools)));
233 
234     connect(d->queueSettingsView, SIGNAL(signalSettingsChanged(QueueSettings)),
235             d->queuePool, SLOT(slotSettingsChanged(QueueSettings)));
236 
237     connect(d->queueSettingsView, SIGNAL(signalSettingsChanged(QueueSettings)),
238             this, SLOT(slotQueueContentsChanged()));
239 
240     connect(d->queuePool, SIGNAL(signalQueueSelected(int,QueueSettings,AssignedBatchTools)),
241             this, SLOT(slotQueueContentsChanged()));
242 
243     connect(d->queuePool, SIGNAL(signalQueuePoolChanged()),
244             this, SLOT(slotQueueContentsChanged()));
245 
246     connect(d->queuePool, SIGNAL(signalQueueContentsChanged()),
247             this, SLOT(slotQueueContentsChanged()));
248 
249     connect(d->queuePool, SIGNAL(signalItemSelectionChanged()),
250             this, SLOT(slotItemSelectionChanged()));
251 
252     // -- Multithreaded interface connections -------------------------------
253 
254     connect(d->thread, SIGNAL(signalStarting(Digikam::ActionData)),
255             this, SLOT(slotAction(Digikam::ActionData)));
256 
257     connect(d->thread, SIGNAL(signalFinished(Digikam::ActionData)),
258             this, SLOT(slotAction(Digikam::ActionData)));
259 
260     connect(d->thread, SIGNAL(signalQueueProcessed()),
261             this, SLOT(slotQueueProcessed()));
262 
263     // -- GUI connections ---------------------------------------------------
264 
265     connect(d->toolsView, SIGNAL(signalHistoryEntryClicked(int,qlonglong)),
266             this, SLOT(slotHistoryEntryClicked(int,qlonglong)));
267 
268     connect(d->toolsView, SIGNAL(signalAssignQueueSettings(QString)),
269             this, SLOT(slotAssignQueueSettings(QString)));
270 }
271 
setupActions()272 void QueueMgrWindow::setupActions()
273 {
274     // -- Standard 'File' menu actions ---------------------------------------------
275 
276     KActionCollection* const ac = actionCollection();
277 
278     d->runAction = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")),
279                                i18n("Run"), this);
280     d->runAction->setEnabled(false);
281     connect(d->runAction, SIGNAL(triggered()), this, SLOT(slotRun()));
282     ac->addAction(QLatin1String("queuemgr_run"), d->runAction);
283     ac->setDefaultShortcut(d->runAction, Qt::CTRL + Qt::Key_P);
284 
285     d->runAllAction = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")),
286                                i18n("Run all"), this);
287     d->runAllAction->setEnabled(false);
288     connect(d->runAllAction, SIGNAL(triggered()), this, SLOT(slotRunAll()));
289     ac->addAction(QLatin1String("queuemgr_run_all"), d->runAllAction);
290     ac->setDefaultShortcut(d->runAllAction, Qt::CTRL + Qt::ALT + Qt::Key_P);
291 
292     d->stopAction = new QAction(QIcon::fromTheme(QLatin1String("media-playback-stop")), i18n("Stop"), this);
293     d->stopAction->setEnabled(false);
294     connect(d->stopAction, SIGNAL(triggered()), this, SLOT(slotStop()));
295     ac->addAction(QLatin1String("queuemgr_stop"), d->stopAction);
296     ac->setDefaultShortcut(d->stopAction, Qt::CTRL + Qt::Key_S);
297 
298     d->newQueueAction = new QAction(QIcon::fromTheme(QLatin1String("list-add")), i18n("New Queue"), this);
299     connect(d->newQueueAction, SIGNAL(triggered()), d->queuePool, SLOT(slotAddQueue()));
300     ac->addAction(QLatin1String("queuemgr_newqueue"), d->newQueueAction);
301 
302     d->removeQueueAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Remove Queue"), this);
303     connect(d->removeQueueAction, SIGNAL(triggered()), d->queuePool, SLOT(slotRemoveCurrentQueue()));
304     ac->addAction(QLatin1String("queuemgr_removequeue"), d->removeQueueAction);
305 
306     // TODO rename action to saveWorkflowAction to avoid confusion?
307 
308     d->saveQueueAction = new QAction(QIcon::fromTheme(QLatin1String("document-save")), i18n("Save Workflow"), this);
309     connect(d->saveQueueAction, SIGNAL(triggered()), this, SLOT(slotSaveWorkflow()));
310     ac->addAction(QLatin1String("queuemgr_savequeue"), d->saveQueueAction);
311 
312     d->removeItemsSelAction = new QAction(QIcon::fromTheme(QLatin1String("list-remove")), i18n("Remove items"), this);
313     d->removeItemsSelAction->setEnabled(false);
314     connect(d->removeItemsSelAction, SIGNAL(triggered()), d->queuePool, SLOT(slotRemoveSelectedItems()));
315     ac->addAction(QLatin1String("queuemgr_removeitemssel"), d->removeItemsSelAction);
316     ac->setDefaultShortcut(d->removeItemsSelAction, Qt::CTRL + Qt::Key_K);
317 
318     d->removeItemsDoneAction = new QAction(i18n("Remove processed items"), this);
319     d->removeItemsDoneAction->setEnabled(false);
320     connect(d->removeItemsDoneAction, SIGNAL(triggered()), d->queuePool, SLOT(slotRemoveItemsDone()));
321     ac->addAction(QLatin1String("queuemgr_removeitemsdone"), d->removeItemsDoneAction);
322 
323     d->clearQueueAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear")), i18n("Clear Queue"), this);
324     d->clearQueueAction->setEnabled(false);
325     connect(d->clearQueueAction, SIGNAL(triggered()), d->queuePool, SLOT(slotClearList()));
326     ac->addAction(QLatin1String("queuemgr_clearlist"), d->clearQueueAction);
327     ac->setDefaultShortcut(d->clearQueueAction, Qt::CTRL + Qt::SHIFT + Qt::Key_K);
328 
329     QAction* const close = buildStdAction(StdCloseAction, this, SLOT(close()), this);
330     ac->addAction(QLatin1String("queuemgr_close"), close);
331 
332     // -- 'Tools' menu actions -----------------------------------------------------
333 
334     d->moveUpToolAction = new QAction(QIcon::fromTheme(QLatin1String("go-up")), i18n("Move up"), this);
335     connect(d->moveUpToolAction, SIGNAL(triggered()), d->assignedList, SLOT(slotMoveCurrentToolUp()));
336     ac->addAction(QLatin1String("queuemgr_toolup"), d->moveUpToolAction);
337 
338     d->moveDownToolAction = new QAction(QIcon::fromTheme(QLatin1String("go-down")), i18n("Move down"), this);
339     connect(d->moveDownToolAction, SIGNAL(triggered()), d->assignedList, SLOT(slotMoveCurrentToolDown()));
340     ac->addAction(QLatin1String("queuemgr_tooldown"), d->moveDownToolAction);
341 
342     d->removeToolAction = new QAction(QIcon::fromTheme(QLatin1String("list-remove")), i18n("Remove tool"), this);
343     connect(d->removeToolAction, SIGNAL(triggered()), d->assignedList, SLOT(slotRemoveCurrentTool()));
344     ac->addAction(QLatin1String("queuemgr_toolremove"), d->removeToolAction);
345 
346     d->clearToolsAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear")), i18n("Clear List"), this);
347     connect(d->clearToolsAction, SIGNAL(triggered()), d->assignedList, SLOT(slotClearToolsList()));
348     ac->addAction(QLatin1String("queuemgr_toolsclear"), d->clearToolsAction);
349 
350     // -- Standard 'View' menu actions ---------------------------------------------
351 
352     createFullScreenAction(QLatin1String("queuemgr_fullscreen"));
353 
354     // ---------------------------------------------------------------------------------
355 
356     ThemeManager::instance()->registerThemeActions(this);
357 
358     // Standard 'Help' menu actions
359 
360     createHelpActions();
361 
362     // Provides a menu entry that allows showing/hiding the toolbar(s)
363 
364     setStandardToolBarMenuEnabled(true);
365 
366     // Provides a menu entry that allows showing/hiding the statusbar
367 
368     createStandardStatusBarAction();
369 
370     // Standard 'Configure' menu actions
371 
372     createSettingsActions();
373 
374     // ---------------------------------------------------------------------------------
375 
376     createGUI(xmlFile());
377     cleanupActions();
378 
379     showMenuBarAction()->setChecked(!menuBar()->isHidden());  // NOTE: workaround for bug #171080
380 }
381 
refreshView()382 void QueueMgrWindow::refreshView()
383 {
384     // NOTE: method called when something is changed from Database (tags, rating, etc...).
385     //       There is nothing to do for the moment.
386 }
387 
readSettings()388 void QueueMgrWindow::readSettings()
389 {
390     KSharedConfig::Ptr config = KSharedConfig::openConfig();
391     KConfigGroup group        = config->group(configGroupName());
392 
393     d->verticalSplitter->restoreState(group, d->VERTICAL_SPLITTER_CONFIG_KEY);
394     d->bottomSplitter->restoreState(group,   d->BOTTOM_SPLITTER_CONFIG_KEY);
395     d->topSplitter->restoreState(group,      d->TOP_SPLITTER_CONFIG_KEY);
396 
397     readFullScreenSettings(group);
398 }
399 
writeSettings()400 void QueueMgrWindow::writeSettings()
401 {
402     KSharedConfig::Ptr config = KSharedConfig::openConfig();
403     KConfigGroup group        = config->group(configGroupName());
404 
405     d->topSplitter->saveState(group,      d->TOP_SPLITTER_CONFIG_KEY);
406     d->bottomSplitter->saveState(group,   d->BOTTOM_SPLITTER_CONFIG_KEY);
407     d->verticalSplitter->saveState(group, d->VERTICAL_SPLITTER_CONFIG_KEY);
408 
409     config->sync();
410 }
411 
applySettings()412 void QueueMgrWindow::applySettings()
413 {
414     // Do not apply general settings from config panel if BQM is busy.
415 
416     if (d->busy)
417     {
418         return;
419     }
420 
421     d->queuePool->applySettings();
422 
423     KSharedConfig::Ptr config = KSharedConfig::openConfig();
424     KConfigGroup group        = config->group(configGroupName());
425     readFullScreenSettings(group);
426 }
427 
refreshStatusBar()428 void QueueMgrWindow::refreshStatusBar()
429 {
430     int items        = d->queuePool->currentQueue()->itemsCount();
431     int pendingItems = d->queuePool->currentQueue()->pendingItemsCount();
432     int tasks        = d->queuePool->currentQueue()->pendingTasksCount();
433     int totalItems   = d->queuePool->totalPendingItems();
434     int totalTasks   = d->queuePool->totalPendingTasks();
435     QString message  = i18n("Current Queue: ");
436 
437     switch (pendingItems)
438     {
439         case 0:
440             message.append(i18n("No items"));
441             break;
442 
443         default:
444             message.append(i18np("1 item", "%1 items", pendingItems));
445             break;
446     }
447 
448     message.append(QLatin1String(" / "));
449 
450     switch (tasks)
451     {
452         case 0:
453             message.append(i18n("No tasks"));
454             break;
455 
456         default:
457             message.append(i18np("1 task", "%1 tasks", tasks));
458             break;
459     }
460 
461     message.append(QLatin1String(" - ") + i18nc("#info: total number of items to process", "Total: "));
462 
463     switch (totalItems)
464     {
465         case 0:
466             message.append(i18n("No items"));
467             break;
468 
469         default:
470             message.append(i18np("1 item", "%1 items", totalItems));
471             break;
472     }
473 
474     message.append(QLatin1String(" / "));
475 
476     switch (totalTasks)
477     {
478         case 0:
479             message.append(i18n("No tasks"));
480             break;
481 
482         default:
483             message.append(i18np("1 task", "%1 tasks", totalTasks));
484             break;
485     }
486 
487     d->statusLabel->setText(message);
488 
489     if (!d->busy)
490     {
491         d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode, i18nc("batch queue manager is ready to use", "Ready"));
492         d->removeItemsSelAction->setEnabled(items > 0);
493         d->removeItemsDoneAction->setEnabled((items - pendingItems) > 0);
494         d->clearQueueAction->setEnabled(items > 0);
495         d->runAction->setEnabled((tasks > 0) && (pendingItems > 0));
496         d->runAllAction->setEnabled((totalTasks > 0) && (totalItems > 0));
497     }
498 }
499 
slotSetup()500 void QueueMgrWindow::slotSetup()
501 {
502     setup(Setup::LastPageUsed);
503 }
504 
setup(Setup::Page page)505 void QueueMgrWindow::setup(Setup::Page page)
506 {
507     Setup::execDialog(this, page);
508 }
509 
slotComponentsInfo()510 void QueueMgrWindow::slotComponentsInfo()
511 {
512     showDigikamComponentsInfo();
513 }
514 
slotDBStat()515 void QueueMgrWindow::slotDBStat()
516 {
517     showDigikamDatabaseStat();
518 }
519 
slotOnlineVersionCheck()520 void QueueMgrWindow::slotOnlineVersionCheck()
521 {
522     Setup::onlineVersionCheck();
523 }
524 
queryClose()525 bool QueueMgrWindow::queryClose()
526 {
527     if (isBusy())
528     {
529         int result = QMessageBox::warning(this, i18n("Processing under progress"),
530                                           i18n("Batch Queue Manager is running. Do you want to cancel current job?"),
531                                           QMessageBox::Yes | QMessageBox::No);
532 
533         if      (result == QMessageBox::Yes)
534         {
535             slotStop();
536         }
537         else if (result == QMessageBox::No)
538         {
539             return false;
540         }
541     }
542 
543     return true;
544 }
545 
addNewQueue()546 void QueueMgrWindow::addNewQueue()
547 {
548     d->queuePool->slotAddQueue();
549 }
550 
currentQueueId() const551 int QueueMgrWindow::currentQueueId() const
552 {
553     return (d->queuePool->currentIndex());
554 }
555 
loadItemInfos(const ItemInfoList & list,int queueId)556 void QueueMgrWindow::loadItemInfos(const ItemInfoList& list, int queueId)
557 {
558     QueueListView* const queue = d->queuePool->findQueueByIndex(queueId);
559 
560     if (queue)
561     {
562         queue->slotAddItems(list);
563     }
564 }
565 
loadItemInfosToCurrentQueue(const ItemInfoList & list)566 void QueueMgrWindow::loadItemInfosToCurrentQueue(const ItemInfoList& list)
567 {
568     if (!d->queuePool->currentQueue())
569     {
570         addNewQueue();
571     }
572 
573     d->queuePool->currentQueue()->slotAddItems(list);
574 }
575 
loadItemInfosToNewQueue(const ItemInfoList & list)576 void QueueMgrWindow::loadItemInfosToNewQueue(const ItemInfoList& list)
577 {
578     QueueListView* const queue = d->queuePool->currentQueue();
579 
580     if (!queue || queue->itemsCount())
581     {
582         addNewQueue();
583     }
584 
585     d->queuePool->currentQueue()->slotAddItems(list);
586 }
587 
slotQueueContentsChanged()588 void QueueMgrWindow::slotQueueContentsChanged()
589 {
590     if (d->busy)
591     {
592         refreshStatusBar();
593     }
594     else
595     {
596         // refreshStatusBar() and actions in tools view
597 
598         slotAssignedToolsChanged(d->assignedList->assignedList());
599     }
600 }
601 
slotItemSelectionChanged()602 void QueueMgrWindow::slotItemSelectionChanged()
603 {
604     if (!d->busy)
605     {
606         int count = d->queuePool->currentQueue()->selectedItems().count();
607         d->removeItemsSelAction->setEnabled((count != 0) ? true : false);
608     }
609 }
610 
populateToolsList()611 void QueueMgrWindow::populateToolsList()
612 {
613     BatchToolsList list = BatchToolsFactory::instance()->toolsList();
614 
615     foreach (BatchTool* const tool, list)
616     {
617         d->toolsView->addTool(tool);
618     }
619 }
620 
slotRun()621 void QueueMgrWindow::slotRun()
622 {
623     d->currentQueueToProcess   = d->queuePool->currentIndex();
624     QueueListView* const queue = d->queuePool->currentQueue();
625     QString msg;
626 
627     if      (!queue)
628     {
629         msg = i18n("There is no queue to be run.");
630     }
631     else if (queue->pendingItemsCount() == 0)
632     {
633         msg = i18n("There is no item to process in the current queue (%1).",
634                    d->queuePool->currentTitle());
635     }
636     else if ((queue->settings().renamingRule == QueueSettings::CUSTOMIZE) &&
637              queue->settings().renamingParser.isEmpty())
638     {
639         msg = i18n("Custom renaming rule is invalid for current queue (%1). "
640                    "Please fix it.", d->queuePool->currentTitle());
641     }
642     else if (queue->assignedTools().m_toolsList.isEmpty())
643     {
644         msg = i18n("Assigned batch tools list is empty for current queue (%1). "
645                    "Please assign tools.", d->queuePool->currentTitle());
646     }
647 
648     if (!msg.isEmpty())
649     {
650         QMessageBox::critical(this, qApp->applicationName(), msg);
651         processingAborted();
652 
653         return;
654     }
655 
656     // Take a look if general settings are changed, as we cannot do it when BQM is busy.
657 
658     applySettings();
659 
660     d->statusProgressBar->setProgressTotalSteps(queue ? queue->pendingTasksCount() : 0);
661     d->statusProgressBar->setProgressValue(0);
662     d->statusProgressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode);
663     d->toolsView->showTab(ToolsView::HISTORY);
664     busy(true);
665 
666     d->processingAllQueues = false;
667     processOneQueue();
668 }
669 
slotRunAll()670 void QueueMgrWindow::slotRunAll()
671 {
672     d->currentQueueToProcess = 0;
673 
674     if (!d->queuePool->totalPendingItems())
675     {
676         QMessageBox::critical(this, qApp->applicationName(), i18n("There are no items to process in the queues."));
677         processingAborted();
678 
679         return;
680     }
681 
682     if (!d->queuePool->customRenamingRulesAreValid())
683     {
684         processingAborted();
685 
686         return;
687     }
688 
689     if (!d->queuePool->assignedBatchToolsListsAreValid())
690     {
691         processingAborted();
692 
693         return;
694     }
695 
696     // Take a look if general settings are changed, as we cannot do it when BQM is busy.
697 
698     applySettings();
699 
700     d->statusProgressBar->setProgressTotalSteps(d->queuePool->totalPendingTasks());
701     d->statusProgressBar->setProgressValue(0);
702     d->statusProgressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode);
703     d->toolsView->showTab(ToolsView::HISTORY);
704     busy(true);
705 
706     d->processingAllQueues = true;
707     processOneQueue();
708 }
709 
processingAborted()710 void QueueMgrWindow::processingAborted()
711 {
712     d->statusProgressBar->setProgressValue(0);
713     d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode);
714     busy(false);
715     refreshStatusBar();
716 }
717 
processOneQueue()718 void QueueMgrWindow::processOneQueue()
719 {
720     d->assignedList->reset();
721 
722     d->queuePool->setCurrentIndex(d->currentQueueToProcess);
723     QueuePoolItemsList itemsList = d->queuePool->queueItemsList(d->currentQueueToProcess);
724     QueueSettings settings       = d->queuePool->currentQueue()->settings();
725 
726     if (!checkTargetAlbum(d->currentQueueToProcess))
727     {
728         processingAborted();
729         return;
730     }
731 
732     QList<AssignedBatchTools> tools4Items;
733 
734     foreach (const ItemInfoSet& item, itemsList)
735     {
736         AssignedBatchTools one         = d->queuePool->currentQueue()->assignedTools();
737         one.m_itemUrl                  = item.info.fileUrl();
738         QueueListViewItem* const cItem = d->queuePool->currentQueue()->findItemByUrl(one.m_itemUrl);
739         one.m_destFileName             = cItem->destFileName();
740         tools4Items.append(one);
741     }
742 
743     d->thread->setSettings(settings);
744     d->thread->processQueueItems(tools4Items);
745 
746     if (!d->thread->isRunning())
747     {
748         d->thread->start();
749     }
750 }
751 
busy(bool busy)752 void QueueMgrWindow::busy(bool busy)
753 {
754     d->busy = busy;
755     d->runAction->setEnabled(!d->busy);
756     d->runAllAction->setEnabled(!d->busy);
757     d->newQueueAction->setEnabled(!d->busy);
758     d->saveQueueAction->setEnabled(!d->busy);
759     d->removeQueueAction->setEnabled(!d->busy);
760     d->removeItemsSelAction->setEnabled(!d->busy);
761     d->removeItemsDoneAction->setEnabled(!d->busy);
762     d->clearQueueAction->setEnabled(!d->busy);
763     d->stopAction->setEnabled(d->busy);
764 
765     d->queuePool->setBusy(d->busy);
766     d->queueSettingsView->setBusy(d->busy);
767     d->toolsView->setBusy(d->busy);
768     d->assignedList->setBusy(d->busy);
769     d->toolSettings->setBusy(d->busy);
770 
771     // To update status of Tools actions.
772 
773     slotAssignedToolsChanged(d->assignedList->assignedList());
774 
775     // To update status of Queue items actions.
776 
777     slotItemSelectionChanged();
778 
779     d->busy ? d->queuePool->setCursor(Qt::WaitCursor) : d->queuePool->unsetCursor();
780     d->busy ? m_animLogo->start() : m_animLogo->stop();
781 
782     emit signalBqmIsBusy(d->busy);
783 }
784 
slotAssignedToolsChanged(const AssignedBatchTools & tools)785 void QueueMgrWindow::slotAssignedToolsChanged(const AssignedBatchTools& tools)
786 {
787     if (d->busy)
788     {
789         d->moveUpToolAction->setEnabled(false);
790         d->moveDownToolAction->setEnabled(false);
791         d->removeToolAction->setEnabled(false);
792         d->clearToolsAction->setEnabled(false);
793 
794         return;
795     }
796 
797     switch (tools.m_toolsList.count())
798     {
799         case 0:
800         {
801             d->moveUpToolAction->setEnabled(false);
802             d->moveDownToolAction->setEnabled(false);
803             d->removeToolAction->setEnabled(false);
804             d->clearToolsAction->setEnabled(false);
805             break;
806         }
807 
808         case 1:
809         {
810             d->moveUpToolAction->setEnabled(false);
811             d->moveDownToolAction->setEnabled(false);
812             d->removeToolAction->setEnabled(true);
813             d->clearToolsAction->setEnabled(true);
814             break;
815         }
816 
817         default:
818         {
819             d->moveUpToolAction->setEnabled(true);
820             d->moveDownToolAction->setEnabled(true);
821             d->removeToolAction->setEnabled(true);
822             d->clearToolsAction->setEnabled(true);
823             break;
824         }
825     }
826 
827     refreshStatusBar();
828 }
829 
checkTargetAlbum(int queueId)830 bool QueueMgrWindow::checkTargetAlbum(int queueId)
831 {
832     QueueListView* const queue = d->queuePool->findQueueByIndex(queueId);
833 
834     if (!queue)
835     {
836         return false;
837     }
838 
839     if (!queue->settings().useOrgAlbum)
840     {
841         QString queueName              = d->queuePool->queueTitle(queueId);
842         QUrl    processedItemsAlbumUrl = queue->settings().workingUrl;
843         qCDebug(DIGIKAM_GENERAL_LOG) << "Target album for queue " << queueName << " is: " << processedItemsAlbumUrl.toLocalFile();
844 
845         if (processedItemsAlbumUrl.isEmpty())
846         {
847             QMessageBox::critical(this, i18n("Processed items album settings"),
848                                   i18n("Album to host processed items from queue \"%1\" is not set. "
849                                        "Please select one from Queue Settings panel.", queueName));
850             return false;
851         }
852 
853         QFileInfo dir(processedItemsAlbumUrl.toLocalFile());
854 
855         if (!dir.exists() || !dir.isWritable())
856         {
857             QMessageBox::critical(this, i18n("Processed items album settings"),
858                                   i18n("Album to host processed items from queue \"%1\" "
859                                        "is not available or not writable. "
860                                        "Please set another one from Queue Settings panel.", queueName));
861             return false;
862         }
863     }
864 
865     return true;
866 }
867 
moveEvent(QMoveEvent * e)868 void QueueMgrWindow::moveEvent(QMoveEvent* e)
869 {
870     Q_UNUSED(e)
871     emit signalWindowHasMoved();
872 }
873 
slotHistoryEntryClicked(int queueId,qlonglong itemId)874 void QueueMgrWindow::slotHistoryEntryClicked(int queueId, qlonglong itemId)
875 {
876     if (d->busy)
877     {
878         return;
879     }
880 
881     QueueListView* const view = d->queuePool->findQueueByIndex(queueId);
882 
883     if (view)
884     {
885         QueueListViewItem* const item = view->findItemById(itemId);
886 
887         if (item)
888         {
889             d->queuePool->setCurrentIndex(queueId);
890             view->scrollToItem(item);
891             view->setCurrentItem(item);
892             item->setSelected(true);
893         }
894     }
895 }
896 
slotAction(const ActionData & ad)897 void QueueMgrWindow::slotAction(const ActionData& ad)
898 {
899     QueueListViewItem* const cItem = d->queuePool->currentQueue()->findItemByUrl(ad.fileUrl);
900 
901     switch (ad.status)
902     {
903         case ActionData::BatchStarted:
904         {
905             if (cItem)
906             {
907                 cItem->reset();
908                 d->queuePool->currentQueue()->setCurrentItem(cItem);
909                 d->queuePool->currentQueue()->scrollToItem(cItem);
910                 d->queuePool->setItemBusy(cItem->info().id());
911                 addHistoryMessage(cItem, i18n("Processing..."), DHistoryView::StartingEntry);
912             }
913 
914             break;
915         }
916 
917         case ActionData::BatchDone:
918         case ActionData::BatchSkipped:
919         {
920             if (cItem)
921             {
922                 cItem->setDestFileName(ad.destUrl.fileName());
923                 cItem->setDone();
924                 addHistoryMessage(cItem, ad.message, DHistoryView::SuccessEntry);
925                 d->statusProgressBar->setProgressValue(d->statusProgressBar->progressValue() + 1);
926             }
927 
928             break;
929         }
930 
931         case ActionData::BatchFailed:
932         {
933             if (cItem)
934             {
935                 cItem->setFailed();
936                 addHistoryMessage(cItem, i18n("Failed to process item..."), DHistoryView::ErrorEntry);
937                 addHistoryMessage(cItem, ad.message, DHistoryView::ErrorEntry);
938                 d->statusProgressBar->setProgressValue(d->statusProgressBar->progressValue() + 1);
939             }
940 
941             break;
942         }
943 
944         case ActionData::BatchCanceled:
945         {
946             if (cItem)
947             {
948                 cItem->setCanceled();
949                 addHistoryMessage(cItem, i18n("Process Cancelled..."), DHistoryView::CancelEntry);
950                 d->statusProgressBar->setProgressValue(d->statusProgressBar->progressValue() + 1);
951             }
952 
953             break;
954         }
955 
956         default:         // NONE
957         {
958             break;
959         }
960     }
961 }
962 
addHistoryMessage(QueueListViewItem * const cItem,const QString & msg,DHistoryView::EntryType type)963 void QueueMgrWindow::addHistoryMessage(QueueListViewItem* const cItem, const QString& msg, DHistoryView::EntryType type)
964 {
965     if (cItem)
966     {
967         int itemId   = cItem->info().id();
968         int queueId  = d->queuePool->currentIndex();
969         QString text = i18n("Item \"%1\" from queue \"%2\": %3", cItem->info().name(),
970                             d->queuePool->queueTitle(queueId), msg);
971         d->toolsView->addHistoryEntry(text, type, queueId, itemId);
972     }
973     else
974     {
975         d->toolsView->addHistoryEntry(msg, type);
976     }
977 }
978 
slotStop()979 void QueueMgrWindow::slotStop()
980 {
981     d->thread->cancel();
982     d->queuePool->currentQueue()->cancelItems();
983     processingAborted();
984 }
985 
slotQueueProcessed()986 void QueueMgrWindow::slotQueueProcessed()
987 {
988     if (!d->busy)
989     {
990         return;
991     }
992 
993     d->currentQueueToProcess++;
994     QString msg;
995 
996     if      (!d->processingAllQueues)
997     {
998         msg = i18n("Batch queue finished");
999     }
1000     else if (d->currentQueueToProcess == d->queuePool->count())
1001     {
1002         msg = i18n("All batch queues finished");
1003     }
1004     else
1005     {
1006         // We will process next queue from the pool.
1007 
1008         processOneQueue();
1009 
1010         return;
1011     }
1012 
1013     DNotificationWrapper(QLatin1String("batchqueuecompleted"), msg, this,
1014                          windowTitle());
1015     processingAborted();
1016 }
1017 
slotAssignQueueSettings(const QString & title)1018 void QueueMgrWindow::slotAssignQueueSettings(const QString& title)
1019 {
1020     if (!title.isEmpty())
1021     {
1022         Workflow q                 = WorkflowManager::instance()->findByTitle(title);
1023         QueueListView* const queue = d->queuePool->currentQueue();
1024 
1025         if (queue)
1026         {
1027             queue->setSettings(q.qSettings);
1028             AssignedBatchTools tools;
1029             tools.m_toolsList = q.aTools;
1030 
1031             //qCDebug(DIGIKAM_GENERAL_LOG) << tools.m_toolsList;
1032 
1033             queue->setAssignedTools(tools);
1034             d->queuePool->slotQueueSelected(d->queuePool->currentIndex());
1035         }
1036     }
1037 }
1038 
slotSaveWorkflow()1039 void QueueMgrWindow::slotSaveWorkflow()
1040 {
1041     if (d->queuePool->saveWorkflow())
1042     {
1043         d->toolsView->showTab(ToolsView::WORKFLOW);
1044     }
1045 }
1046 
customizedFullScreenMode(bool set)1047 void QueueMgrWindow::customizedFullScreenMode(bool set)
1048 {
1049     showStatusBarAction()->setEnabled(!set);
1050     toolBarMenuAction()->setEnabled(!set);
1051     showMenuBarAction()->setEnabled(!set);
1052 }
1053 
1054 } // namespace Digikam
1055