1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2009-02-13
7 * Description : tabbed queue items list.
8 *
9 * Copyright (C) 2009-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 "queuepool.h"
25
26 // Qt includes
27
28 #include <QTabBar>
29 #include <QApplication>
30 #include <QIcon>
31
32 // KDE includes
33
34 #include <klocalizedstring.h>
35
36 // Local includes
37
38 #include "digikam_debug.h"
39 #include "dmessagebox.h"
40 #include "applicationsettings.h"
41 #include "iccsettings.h"
42 #include "metaenginesettings.h"
43 #include "ddragobjects.h"
44 #include "queuelist.h"
45 #include "workflowmanager.h"
46 #include "workflowdlg.h"
47 #include "queuesettings.h"
48 #include "loadingcacheinterface.h"
49
50 namespace Digikam
51 {
52
QueuePoolBar(QWidget * const parent)53 QueuePoolBar::QueuePoolBar(QWidget* const parent)
54 : QTabBar(parent)
55 {
56 setAcceptDrops(true);
57 setMouseTracking(true);
58 }
59
~QueuePoolBar()60 QueuePoolBar::~QueuePoolBar()
61 {
62 }
63
dragEnterEvent(QDragEnterEvent * e)64 void QueuePoolBar::dragEnterEvent(QDragEnterEvent* e)
65 {
66 int tab = tabAt(e->pos());
67
68 if (tab != -1)
69 {
70 bool accept = false;
71
72 // The receivers of the testCanDecode() signal has to adjust 'accept' accordingly.
73
74 emit signalTestCanDecode(e, accept);
75
76 e->setAccepted(accept);
77
78 return;
79 }
80
81 QTabBar::dragEnterEvent(e);
82 }
83
dragMoveEvent(QDragMoveEvent * e)84 void QueuePoolBar::dragMoveEvent(QDragMoveEvent* e)
85 {
86 int tab = tabAt(e->pos());
87
88 if (tab != -1)
89 {
90 bool accept = false;
91
92 // The receivers of the testCanDecode() signal has to adjust 'accept' accordingly.
93
94 emit signalTestCanDecode(e, accept);
95
96 e->setAccepted(accept);
97
98 return;
99 }
100
101 QTabBar::dragMoveEvent(e);
102 }
103
104 // --------------------------------------------------------------------------------------------
105
QueuePool(QWidget * const parent)106 QueuePool::QueuePool(QWidget* const parent)
107 : QTabWidget(parent)
108 {
109 setTabBar(new QueuePoolBar(this));
110 setTabsClosable(false);
111 setAcceptDrops(true);
112 slotAddQueue();
113
114 connect(this, SIGNAL(currentChanged(int)),
115 this, SLOT(slotQueueSelected(int)));
116
117 connect(this, SIGNAL(tabCloseRequested(int)),
118 this, SLOT(slotCloseQueueRequest(int)));
119
120 connect(tabBar(), SIGNAL(signalTestCanDecode(const QDragMoveEvent*,bool&)),
121 this, SLOT(slotTestCanDecode(const QDragMoveEvent*,bool&)));
122
123 // -- FileWatch connections ------------------------------
124
125 LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString)));
126 }
127
~QueuePool()128 QueuePool::~QueuePool()
129 {
130 }
131
keyPressEvent(QKeyEvent * event)132 void QueuePool::keyPressEvent(QKeyEvent* event)
133 {
134 if (event->key() == Qt::Key_Delete)
135 {
136 slotRemoveSelectedItems();
137 }
138 else
139 {
140 QTabWidget::keyPressEvent(event);
141 }
142 }
143
setBusy(bool b)144 void QueuePool::setBusy(bool b)
145 {
146 tabBar()->setEnabled(!b);
147
148 for (int i = 0 ; i < count() ; ++i)
149 {
150 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
151
152 if (queue)
153 {
154 queue->viewport()->setEnabled(!b);
155 }
156 }
157 }
158
currentQueue() const159 QueueListView* QueuePool::currentQueue() const
160 {
161 return (dynamic_cast<QueueListView*>(currentWidget()));
162 }
163
currentTitle() const164 QString QueuePool::currentTitle() const
165 {
166 return queueTitle(currentIndex());
167 }
168
findQueueByItemId(qlonglong id) const169 QueueListView* QueuePool::findQueueByItemId(qlonglong id) const
170 {
171 for (int i = 0 ; i < count() ; ++i)
172 {
173 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
174
175 if (queue && queue->findItemById(id))
176 {
177 return queue;
178 }
179 }
180
181 return nullptr;
182 }
183
setItemBusy(qlonglong id)184 void QueuePool::setItemBusy(qlonglong id)
185 {
186 QueueListView* const queue = findQueueByItemId(id);
187
188 if (queue)
189 {
190 queue->setItemBusy(id);
191 }
192 }
193
findQueueByIndex(int index) const194 QueueListView* QueuePool::findQueueByIndex(int index) const
195 {
196 return (dynamic_cast<QueueListView*>(widget(index)));
197 }
198
queuesMap() const199 QMap<int, QString> QueuePool::queuesMap() const
200 {
201 QMap<int, QString> map;
202
203 for (int i = 0 ; i < count() ; ++i)
204 {
205 map.insert(i, queueTitle(i));
206 }
207
208 return map;
209 }
210
queueTitle(int index) const211 QString QueuePool::queueTitle(int index) const
212 {
213 // NOTE: clean up tab title. With QTabWidget, it sound like mistake is added, as '&' and space.
214 // NOTE update, & is an usability helper to allow keyboard access -teemu
215
216 return (tabText(index).remove(QLatin1Char('&')).remove(QLatin1Char(' ')));
217 }
218
slotAddQueue()219 void QueuePool::slotAddQueue()
220 {
221 QueueListView* const queue = new QueueListView(this);
222
223 if (!queue)
224 {
225 return;
226 }
227
228 int index = addTab(queue, QIcon::fromTheme(QLatin1String("run-build")), QString::fromUtf8("#%1").arg(count() + 1));
229
230 connect(queue, SIGNAL(signalQueueContentsChanged()),
231 this, SIGNAL(signalQueueContentsChanged()));
232
233 connect(queue, SIGNAL(itemSelectionChanged()),
234 this, SIGNAL(signalItemSelectionChanged()));
235
236 emit signalQueuePoolChanged();
237
238 setCurrentIndex(index);
239 }
240
queueItemsList(int index) const241 QueuePoolItemsList QueuePool::queueItemsList(int index) const
242 {
243 QueuePoolItemsList qpool;
244
245 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(index));
246
247 if (queue)
248 {
249 ItemInfoList list = queue->pendingItemsList();
250
251 for (ItemInfoList::const_iterator it = list.constBegin() ; it != list.constEnd() ; ++it)
252 {
253 ItemInfo info = *it;
254 ItemInfoSet set(index, info);
255 qpool.append(set);
256 }
257 }
258
259 return qpool;
260 }
261
totalPendingItems() const262 int QueuePool::totalPendingItems() const
263 {
264 int items = 0;
265
266 for (int i = 0 ; i < count() ; ++i)
267 {
268 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
269
270 if (queue)
271 {
272 items += queue->pendingItemsCount();
273 }
274 }
275
276 return items;
277 }
278
totalPendingTasks() const279 int QueuePool::totalPendingTasks() const
280 {
281 int tasks = 0;
282
283 for (int i = 0 ; i < count() ; ++i)
284 {
285 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
286
287 if (queue)
288 {
289 tasks += queue->pendingTasksCount();
290 }
291 }
292
293 return tasks;
294 }
295
slotRemoveCurrentQueue()296 void QueuePool::slotRemoveCurrentQueue()
297 {
298 QueueListView* const queue = currentQueue();
299
300 if (!queue)
301 {
302 return;
303 }
304
305 removeTab(indexOf(queue));
306
307 if (count() == 0)
308 {
309 slotAddQueue();
310 }
311 else
312 {
313 for (int i = 0; i < count(); ++i)
314 {
315 setTabText(i, QString::fromUtf8("#%1").arg(i + 1));
316 }
317 }
318
319 emit signalQueuePoolChanged();
320 }
321
saveWorkflow() const322 bool QueuePool::saveWorkflow() const
323 {
324 QueueListView* const queue = currentQueue();
325
326 if (queue)
327 {
328 WorkflowManager* const mngr = WorkflowManager::instance();
329 Workflow wf;
330 wf.qSettings = queue->settings();
331 wf.aTools = queue->assignedTools().m_toolsList;
332
333 if (WorkflowDlg::createNew(wf))
334 {
335 mngr->insert(wf);
336 mngr->save();
337
338 return true;
339 }
340 }
341
342 return false;
343 }
344
slotClearList()345 void QueuePool::slotClearList()
346 {
347 QueueListView* const queue = currentQueue();
348
349 if (queue)
350 {
351 queue->slotClearList();
352 }
353 }
354
slotRemoveSelectedItems()355 void QueuePool::slotRemoveSelectedItems()
356 {
357 QueueListView* const queue = currentQueue();
358
359 if (queue)
360 {
361 queue->slotRemoveSelectedItems();
362 }
363 }
364
slotRemoveItemsDone()365 void QueuePool::slotRemoveItemsDone()
366 {
367 QueueListView* const queue = currentQueue();
368
369 if (queue)
370 {
371 queue->slotRemoveItemsDone();
372 }
373 }
374
slotAddItems(const ItemInfoList & list,int queueId)375 void QueuePool::slotAddItems(const ItemInfoList& list, int queueId)
376 {
377 QueueListView* const queue = findQueueByIndex(queueId);
378
379 if (queue)
380 {
381 queue->slotAddItems(list);
382 }
383 }
384
slotAssignedToolsChanged(const AssignedBatchTools & tools4Item)385 void QueuePool::slotAssignedToolsChanged(const AssignedBatchTools& tools4Item)
386 {
387 QueueListView* const queue = currentQueue();
388
389 if (queue)
390 {
391 queue->slotAssignedToolsChanged(tools4Item);
392 }
393 }
394
slotQueueSelected(int index)395 void QueuePool::slotQueueSelected(int index)
396 {
397 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(index));
398
399 if (queue)
400 {
401 emit signalItemSelectionChanged();
402 emit signalQueueSelected(index, queue->settings(), queue->assignedTools());
403 }
404 }
405
slotCloseQueueRequest(int index)406 void QueuePool::slotCloseQueueRequest(int index)
407 {
408 removeTab(index);
409
410 if (count() == 0)
411 {
412 slotAddQueue();
413 }
414
415 emit signalQueuePoolChanged();
416 }
417
removeTab(int index)418 void QueuePool::removeTab(int index)
419 {
420 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(index));
421
422 if (!queue)
423 {
424 return;
425 }
426
427 int count = queue->pendingItemsCount();
428
429 if (count > 0)
430 {
431 int ret = QMessageBox::question(this, qApp->applicationName(),
432 i18np("There is still 1 unprocessed item in \"%2\".\nDo you want to close this queue?",
433 "There are still %1 unprocessed items in \"%2\".\nDo you want to close this queue?",
434 count, queueTitle(index)),
435 QMessageBox::Yes | QMessageBox::No);
436
437 if (ret == QMessageBox::No)
438 {
439 return;
440 }
441 }
442
443 QTabWidget::removeTab(index);
444 }
445
slotTestCanDecode(const QDragMoveEvent * e,bool & accept)446 void QueuePool::slotTestCanDecode(const QDragMoveEvent* e, bool& accept)
447 {
448 int albumID;
449 QList<int> albumIDs;
450 QList<qlonglong> imageIDs;
451 QList<QUrl> urls;
452
453 if (DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs) ||
454 DAlbumDrag::decode(e->mimeData(), urls, albumID) ||
455 DTagListDrag::canDecode(e->mimeData()))
456 {
457 accept = true;
458
459 return;
460 }
461
462 accept = false;
463 }
464
slotSettingsChanged(const QueueSettings & settings)465 void QueuePool::slotSettingsChanged(const QueueSettings& settings)
466 {
467 QueueListView* const queue = currentQueue();
468
469 if (queue)
470 {
471 queue->setSettings(settings);
472 }
473 }
474
customRenamingRulesAreValid() const475 bool QueuePool::customRenamingRulesAreValid() const
476 {
477 QStringList list;
478
479 for (int i = 0 ; i < count() ; ++i)
480 {
481 QueueListView* const queue = findQueueByIndex(i);
482
483 if (queue)
484 {
485 if (queue->settings().renamingRule == QueueSettings::CUSTOMIZE &&
486 queue->settings().renamingParser.isEmpty())
487 {
488 list.append(queueTitle(i));
489 }
490 }
491 }
492
493 if (!list.isEmpty())
494 {
495 DMessageBox::showInformationList(QMessageBox::Critical,
496 qApp->activeWindow(),
497 qApp->applicationName(),
498 i18n("Custom renaming rules are invalid for Queues listed below. "
499 "Please fix them."),
500 list);
501 return false;
502 }
503
504 return true;
505 }
506
assignedBatchToolsListsAreValid() const507 bool QueuePool::assignedBatchToolsListsAreValid() const
508 {
509 QStringList list;
510
511 for (int i = 0 ; i < count() ; ++i)
512 {
513 QueueListView* const queue = findQueueByIndex(i);
514
515 if (queue)
516 {
517 if (queue->assignedTools().m_toolsList.isEmpty())
518 {
519 list.append(queueTitle(i));
520 }
521 }
522 }
523
524 if (!list.isEmpty())
525 {
526 DMessageBox::showInformationList(QMessageBox::Critical,
527 qApp->activeWindow(),
528 qApp->applicationName(),
529 i18n("Assigned batch tools list is empty for Queues listed below. "
530 "Please assign tools."),
531 list);
532 return false;
533 }
534
535 return true;
536 }
537
slotFileChanged(const QString & filePath)538 void QueuePool::slotFileChanged(const QString& filePath)
539 {
540 for (int i = 0 ; i < count() ; ++i)
541 {
542 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
543
544 if (queue)
545 {
546 queue->reloadThumbs(QUrl::fromLocalFile(filePath));
547 }
548 }
549 }
550
applySettings()551 void QueuePool::applySettings()
552 {
553 for (int i = 0 ; i < count() ; ++i)
554 {
555 QueueListView* const queue = dynamic_cast<QueueListView*>(widget(i));
556
557 if (queue)
558 {
559 // Show/hide tool-tips settings.
560
561 queue->setEnableToolTips(ApplicationSettings::instance()->getShowToolTips());
562
563 // Reset Exif Orientation settings.
564
565 QueueSettings prm = queue->settings();
566 prm.exifSetOrientation = MetaEngineSettings::instance()->settings().exifRotate;
567
568 // Apply Color Management rules to RAW images decoding settings
569
570 // If digiKam Color Management is enable, no need to correct color of decoded RAW image,
571 // else, sRGB color workspace will be used.
572
573 ICCSettingsContainer ICCSettings = IccSettings::instance()->settings();
574
575 if (ICCSettings.enableCM)
576 {
577 if ((ICCSettings.defaultUncalibratedBehavior & ICCSettingsContainer::AskUser) ||
578 (ICCSettings.defaultUncalibratedBehavior & ICCSettingsContainer::AutomaticColors))
579 {
580 prm.rawDecodingSettings.outputColorSpace = DRawDecoderSettings::CUSTOMOUTPUTCS;
581 prm.rawDecodingSettings.outputProfile = ICCSettings.workspaceProfile;
582 }
583 else
584 {
585 prm.rawDecodingSettings.outputColorSpace = DRawDecoderSettings::RAWCOLOR;
586 }
587 }
588 else
589 {
590 prm.rawDecodingSettings.outputColorSpace = DRawDecoderSettings::SRGB;
591 }
592
593 queue->setSettings(prm);
594 }
595 }
596 }
597
598 } // namespace Digikam
599