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 items list.
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 #define ICONSIZE 32
25
26 #include "assignedlist.h"
27
28 // Qt includes
29
30 #include <QDragEnterEvent>
31 #include <QFileInfo>
32 #include <QHeaderView>
33 #include <QPainter>
34 #include <QUrl>
35 #include <QMimeData>
36 #include <QAction>
37 #include <QMenu>
38
39 // KDE includes
40
41 #include <kactioncollection.h>
42 #include <klocalizedstring.h>
43
44 // Local includes
45
46 #include "digikam_debug.h"
47 #include "queuemgrwindow.h"
48 #include "queuesettingsview.h"
49 #include "toolslistview.h"
50 #include "batchtoolsfactory.h"
51
52 namespace Digikam
53 {
54
AssignedListViewItem(QTreeWidget * const parent)55 AssignedListViewItem::AssignedListViewItem(QTreeWidget* const parent)
56 : QTreeWidgetItem(parent)
57 {
58 setFlags(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | flags());
59 }
60
AssignedListViewItem(QTreeWidget * const parent,QTreeWidgetItem * const preceding)61 AssignedListViewItem::AssignedListViewItem(QTreeWidget* const parent, QTreeWidgetItem* const preceding)
62 : QTreeWidgetItem(parent, preceding)
63 {
64 setFlags(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | flags());
65 }
66
~AssignedListViewItem()67 AssignedListViewItem::~AssignedListViewItem()
68 {
69 }
70
setToolSet(const BatchToolSet & set)71 void AssignedListViewItem::setToolSet(const BatchToolSet& set)
72 {
73 m_set = set;
74 setIndex(m_set.index);
75
76 BatchTool* const tool = BatchToolsFactory::instance()->findTool(m_set.name, m_set.group);
77
78 if (tool)
79 {
80 setIcon(1, tool->toolIcon());
81 setText(1, tool->toolTitle());
82 }
83 }
84
toolSet()85 BatchToolSet AssignedListViewItem::toolSet()
86 {
87 return m_set;
88 }
89
setIndex(int index)90 void AssignedListViewItem::setIndex(int index)
91 {
92 m_set.index = index;
93 setText(0, QString::fromUtf8("%1").arg(m_set.index + 1));
94 }
95
96 // ---------------------------------------------------------------------------
97
AssignedListView(QWidget * const parent)98 AssignedListView::AssignedListView(QWidget* const parent)
99 : QTreeWidget(parent)
100 {
101 setSelectionMode(QAbstractItemView::SingleSelection);
102 setWhatsThis(i18n("This is the list of batch tools assigned."));
103 setIconSize(QSize(ICONSIZE, ICONSIZE));
104
105 setDragEnabled(true);
106 setAcceptDrops(true);
107 viewport()->setAcceptDrops(true);
108 setDropIndicatorShown(true);
109
110 setContextMenuPolicy(Qt::CustomContextMenu);
111 setSortingEnabled(false);
112 setAllColumnsShowFocus(true);
113 setRootIsDecorated(false);
114 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
115 setColumnCount(2);
116 setHeaderHidden(true);
117 header()->setSectionResizeMode(QHeaderView::ResizeToContents);
118
119 connect(this, SIGNAL(itemSelectionChanged()),
120 this, SLOT(slotSelectionChanged()));
121
122 connect(this, SIGNAL(customContextMenuRequested(QPoint)),
123 this, SLOT(slotContextMenu()));
124 }
125
~AssignedListView()126 AssignedListView::~AssignedListView()
127 {
128 }
129
keyPressEvent(QKeyEvent * e)130 void AssignedListView::keyPressEvent(QKeyEvent* e)
131 {
132 if (e->key() == Qt::Key_Delete)
133 {
134 slotRemoveCurrentTool();
135 }
136 else
137 {
138 QTreeWidget::keyPressEvent(e);
139 }
140 }
141
setBusy(bool b)142 void AssignedListView::setBusy(bool b)
143 {
144 viewport()->setEnabled(!b);
145 }
146
assignedList()147 AssignedBatchTools AssignedListView::assignedList()
148 {
149 BatchSetList list;
150 QTreeWidgetItemIterator it(this);
151
152 while (*it)
153 {
154 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(*it);
155
156 if (item)
157 {
158 list.append(item->toolSet());
159 }
160
161 ++it;
162 }
163
164 AssignedBatchTools tools;
165 tools.m_toolsList = list;
166
167 return tools;
168 }
169
assignedCount()170 int AssignedListView::assignedCount()
171 {
172 return assignedList().m_toolsList.count();
173 }
174
slotRemoveCurrentTool()175 void AssignedListView::slotRemoveCurrentTool()
176 {
177 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(currentItem());
178
179 if (item)
180 {
181 delete item;
182 refreshIndex();
183 emit signalAssignedToolsChanged(assignedList());
184 }
185
186 if (assignedCount() == 0)
187 {
188 emit signalToolSelected(BatchToolSet());
189 }
190 }
191
slotClearToolsList()192 void AssignedListView::slotClearToolsList()
193 {
194 clear();
195 emit signalAssignedToolsChanged(assignedList());
196 emit signalToolSelected(BatchToolSet());
197 }
198
slotMoveCurrentToolUp()199 void AssignedListView::slotMoveCurrentToolUp()
200 {
201 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(currentItem());
202
203 if (item)
204 {
205 AssignedListViewItem* const iabove = dynamic_cast<AssignedListViewItem*>(itemAbove(item));
206
207 if (iabove)
208 {
209 moveTool(item, iabove->toolSet());
210 setCurrentItem(item);
211 }
212 }
213 }
214
slotMoveCurrentToolDown()215 void AssignedListView::slotMoveCurrentToolDown()
216 {
217 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(currentItem());
218
219 if (item)
220 {
221 AssignedListViewItem* const ibelow = dynamic_cast<AssignedListViewItem*>(itemBelow(item));
222
223 if (ibelow)
224 {
225 AssignedListViewItem* const nitem = moveTool(ibelow, item->toolSet());
226 setCurrentItem(nitem);
227 }
228 }
229 }
230
moveTool(AssignedListViewItem * const preceding,const BatchToolSet & set)231 AssignedListViewItem* AssignedListView::moveTool(AssignedListViewItem* const preceding, const BatchToolSet& set)
232 {
233 BatchTool* const tool = BatchToolsFactory::instance()->findTool(set.name, set.group);
234
235 if (!tool)
236 {
237 return nullptr;
238 }
239
240 removeTool(set);
241 AssignedListViewItem* const item = insertTool(preceding, set);
242 refreshIndex();
243
244 emit signalAssignedToolsChanged(assignedList());
245
246 return item;
247 }
248
insertTool(AssignedListViewItem * const preceding,const BatchToolSet & set)249 AssignedListViewItem* AssignedListView::insertTool(AssignedListViewItem* const preceding, const BatchToolSet& set)
250 {
251 AssignedListViewItem* item = nullptr;
252
253 if (preceding)
254 {
255 item = new AssignedListViewItem(this, preceding);
256 }
257 else
258 {
259 item = new AssignedListViewItem(this);
260 }
261
262 BatchTool* const tool = BatchToolsFactory::instance()->findTool(set.name, set.group);
263
264 // NOTE: Only now create the settings widget when needed.
265
266 if (tool && !tool->settingsWidget())
267 {
268 tool->registerSettingsWidget();
269 }
270
271 item->setToolSet(set);
272 refreshIndex();
273
274 emit signalAssignedToolsChanged(assignedList());
275
276 return item;
277 }
278
addTool(const BatchToolSet & set)279 AssignedListViewItem* AssignedListView::addTool(const BatchToolSet& set)
280 {
281 return insertTool(nullptr, set);
282 }
283
removeTool(const BatchToolSet & set)284 bool AssignedListView::removeTool(const BatchToolSet& set)
285 {
286 QTreeWidgetItemIterator it(this);
287
288 while (*it)
289 {
290 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(*it);
291
292 if (item && (item->toolSet() == set))
293 {
294 delete item;
295 refreshIndex();
296 return true;
297 }
298
299 ++it;
300 }
301
302 return false;
303 }
304
findTool(const BatchToolSet & set)305 AssignedListViewItem* AssignedListView::findTool(const BatchToolSet& set)
306 {
307 QTreeWidgetItemIterator it(this);
308
309 while (*it)
310 {
311 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(*it);
312
313 if (item && (item->toolSet() == set))
314 {
315 return item;
316 }
317
318 ++it;
319 }
320
321 return nullptr;
322 }
323
supportedDropActions() const324 Qt::DropActions AssignedListView::supportedDropActions() const
325 {
326 return (Qt::CopyAction | Qt::MoveAction);
327 }
328
mimeTypes() const329 QStringList AssignedListView::mimeTypes() const
330 {
331 QStringList types;
332 types << QLatin1String("digikam/assignedbatchtool");
333
334 return types;
335 }
336
337 // cppcheck-suppress passedByValue
mimeData(const QList<QTreeWidgetItem * > items) const338 QMimeData* AssignedListView::mimeData(const QList<QTreeWidgetItem*> items) const // clazy:exclude=function-args-by-ref
339 {
340 QMimeData* const mimeData = new QMimeData();
341 QByteArray encodedData;
342
343 QDataStream stream(&encodedData, QIODevice::WriteOnly);
344 stream << items.count();
345
346 foreach (QTreeWidgetItem* const itm, items)
347 {
348 AssignedListViewItem* const alwi = dynamic_cast<AssignedListViewItem*>(itm);
349
350 if (alwi)
351 {
352 stream << (int)(alwi->toolSet().group);
353 stream << alwi->toolSet().name;
354 stream << alwi->toolSet().index;
355 stream << alwi->toolSet().version;
356 stream << alwi->toolSet().settings;
357 }
358 }
359
360 mimeData->setData(QLatin1String("digikam/assignedbatchtool"), encodedData);
361
362 return mimeData;
363 }
364
dragEnterEvent(QDragEnterEvent * e)365 void AssignedListView::dragEnterEvent(QDragEnterEvent* e)
366 {
367 QTreeWidget::dragEnterEvent(e);
368 e->accept();
369 }
370
dragMoveEvent(QDragMoveEvent * e)371 void AssignedListView::dragMoveEvent(QDragMoveEvent* e)
372 {
373 if (e->mimeData()->formats().contains(QLatin1String("digikam/batchtoolslist")) ||
374 e->mimeData()->formats().contains(QLatin1String("digikam/assignedbatchtool")))
375 {
376 QTreeWidget::dragMoveEvent(e);
377 e->accept();
378
379 return;
380 }
381
382 e->ignore();
383 }
384
dropEvent(QDropEvent * e)385 void AssignedListView::dropEvent(QDropEvent* e)
386 {
387 if (e->mimeData()->formats().contains(QLatin1String("digikam/batchtoolslist")))
388 {
389 QByteArray ba = e->mimeData()->data(QLatin1String("digikam/batchtoolslist"));
390
391 if (ba.size())
392 {
393 QDataStream ds(ba);
394 QMultiMap<int, QString> map;
395 ds >> map;
396
397 AssignedListViewItem* const preceding = dynamic_cast<AssignedListViewItem*>(itemAt(e->pos()));
398 assignTools(map, preceding);
399 }
400
401 e->acceptProposedAction();
402 }
403 else if (e->mimeData()->formats().contains(QLatin1String("digikam/assignedbatchtool")))
404 {
405 QByteArray ba = e->mimeData()->data(QLatin1String("digikam/assignedbatchtool"));
406
407 if (ba.size())
408 {
409 int count;
410 QDataStream ds(ba);
411 ds >> count;
412
413 for (int i = 0 ; i < count ; ++i)
414 {
415 int group, index, version;
416 QString name;
417 BatchToolSettings settings;
418
419 ds >> group;
420 ds >> name;
421 ds >> index;
422 ds >> version;
423 ds >> settings;
424
425 AssignedListViewItem* const preceding = dynamic_cast<AssignedListViewItem*>(itemAt(e->pos()));
426
427 BatchToolSet set;
428 set.name = name;
429 set.group = (BatchTool::BatchToolGroup)group;
430 set.index = index;
431 set.version = version;
432 set.settings = settings;
433 AssignedListViewItem* const item = moveTool(preceding, set);
434 setCurrentItem(item);
435 }
436 }
437
438 e->acceptProposedAction();
439 }
440 else
441 {
442 e->ignore();
443 }
444 }
445
slotSelectionChanged()446 void AssignedListView::slotSelectionChanged()
447 {
448 QList<QTreeWidgetItem*> list = selectedItems();
449
450 if (list.isEmpty())
451 {
452 return;
453 }
454
455 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(list.first());
456
457 if (item)
458 {
459 BatchToolSet set = item->toolSet();
460 emit signalToolSelected(set);
461 }
462 else
463 {
464 emit signalToolSelected(BatchToolSet());
465 }
466 }
467
slotQueueSelected(int,const QueueSettings &,const AssignedBatchTools & tools)468 void AssignedListView::slotQueueSelected(int, const QueueSettings&, const AssignedBatchTools& tools)
469 {
470 // Clear assigned tools list and tool settings view.
471
472 clear();
473 emit signalToolSelected(BatchToolSet());
474
475 if (!tools.m_toolsList.isEmpty())
476 {
477 blockSignals(true);
478
479 foreach (const BatchToolSet& set, tools.m_toolsList)
480 {
481 addTool(set);
482 }
483
484 blockSignals(false);
485 }
486 }
487
slotSettingsChanged(const BatchToolSet & set)488 void AssignedListView::slotSettingsChanged(const BatchToolSet& set)
489 {
490 AssignedListViewItem* const item = findTool(set);
491
492 if (item)
493 {
494 item->setToolSet(set);
495 emit signalAssignedToolsChanged(assignedList());
496 }
497 }
498
slotAssignTools(const QMultiMap<int,QString> & map)499 void AssignedListView::slotAssignTools(const QMultiMap<int, QString>& map)
500 {
501 if (map.isEmpty())
502 {
503 return;
504 }
505
506 assignTools(map, nullptr);
507 }
508
assignTools(const QMultiMap<int,QString> & map,AssignedListViewItem * const preceding)509 void AssignedListView::assignTools(const QMultiMap<int, QString>& map, AssignedListViewItem* const preceding)
510 {
511 // We pop all items in reverse order to have same order than selection from Batch Tools list.
512
513 QMapIterator<int, QString> it(map);
514 it.toBack();
515
516 while (it.hasPrevious())
517 {
518 it.previous();
519 BatchTool::BatchToolGroup group = (BatchTool::BatchToolGroup)(it.key());
520 QString name = it.value();
521 BatchTool* const tool = BatchToolsFactory::instance()->findTool(name, group);
522
523 if (tool)
524 {
525 // NOTE: Only now create the settings widget when needed.
526
527 if (!tool->settingsWidget())
528 {
529 tool->registerSettingsWidget();
530 }
531
532 BatchToolSet set;
533 set.name = tool->objectName();
534 set.group = tool->toolGroup();
535 set.version = tool->toolVersion();
536 set.settings = tool->defaultSettings();
537 AssignedListViewItem* const item = insertTool(preceding, set);
538 setCurrentItem(item);
539 }
540 }
541 }
542
slotContextMenu()543 void AssignedListView::slotContextMenu()
544 {
545 if (!viewport()->isEnabled())
546 {
547 return;
548 }
549
550 KActionCollection* const acol = QueueMgrWindow::queueManagerWindow()->actionCollection();
551 QMenu popmenu(this);
552 popmenu.addAction(acol->action(QLatin1String("queuemgr_toolup")));
553 popmenu.addAction(acol->action(QLatin1String("queuemgr_tooldown")));
554 popmenu.addAction(acol->action(QLatin1String("queuemgr_toolremove")));
555 popmenu.addSeparator();
556 popmenu.addAction(acol->action(QLatin1String("queuemgr_savequeue")));
557 popmenu.addAction(acol->action(QLatin1String("queuemgr_toolsclear")));
558 popmenu.exec(QCursor::pos());
559 }
560
refreshIndex()561 void AssignedListView::refreshIndex()
562 {
563 QTreeWidgetItemIterator it(this);
564
565 while (*it)
566 {
567 AssignedListViewItem* const item = dynamic_cast<AssignedListViewItem*>(*it);
568
569 if (item)
570 {
571 item->setIndex(indexOfTopLevelItem(item));
572 }
573
574 ++it;
575 }
576 }
577
578 } // namespace Digikam
579