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