1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2009-04-24
7  * Description : Qt model-view for items
8  *
9  * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10  * Copyright (C) 2009-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11  * Copyright (C) 2011      by Andi Clemens <andi dot clemens at gmail dot com>
12  * Copyright (C) 2013      by Michael G. Hansen <mike at mghansen dot de>
13  * Copyright (C) 2014      by Mohamed_Anwer <m_dot_anwer at gmx dot com>
14  * Copyright (C) 2017      by Simon Frei <freisim93 at gmail dot com>
15  *
16  * This program is free software you can redistribute it
17  * and/or modify it under the terms of the GNU General
18  * Public License as published by the Free Software Foundation
19  * either version 2, or (at your option)
20  * any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * ============================================================ */
28 
29 #include "digikamitemview_p.h"
30 
31 // Qt includes
32 
33 #include <QApplication>
34 #include <QPointer>
35 #include <QAction>
36 #include <QMenu>
37 #include <QIcon>
38 #include <QUrl>
39 
40 // Local includes
41 
42 #include "digikam_debug.h"
43 #include "albummanager.h"
44 #include "coredb.h"
45 #include "coredboperationgroup.h"
46 #include "advancedrenamedialog.h"
47 #include "advancedrenameprocessdialog.h"
48 #include "applicationsettings.h"
49 #include "assignnameoverlay.h"
50 #include "contextmenuhelper.h"
51 #include "coredbaccess.h"
52 #include "ddragobjects.h"
53 #include "digikamapp.h"
54 #include "digikamitemdelegate.h"
55 #include "itemfacedelegate.h"
56 #include "dio.h"
57 #include "groupindicatoroverlay.h"
58 #include "itemalbumfiltermodel.h"
59 #include "itemalbummodel.h"
60 #include "itemdragdrop.h"
61 #include "itemratingoverlay.h"
62 #include "itemfullscreenoverlay.h"
63 #include "itemcoordinatesoverlay.h"
64 #include "tagslineeditoverlay.h"
65 #include "itemviewutilities.h"
66 #include "imagewindow.h"
67 #include "fileactionmngr.h"
68 #include "fileactionprogress.h"
69 #include "thumbnailloadthread.h"
70 #include "tagregion.h"
71 #include "addtagslineedit.h"
72 #include "facerejectionoverlay.h"
73 #include "facetagsiface.h"
74 
75 namespace Digikam
76 {
77 
DigikamItemView(QWidget * const parent)78 DigikamItemView::DigikamItemView(QWidget* const parent)
79     : ItemCategorizedView(parent),
80       d                  (new Private(this))
81 {
82     installDefaultModels();
83 
84     d->editPipeline.plugDatabaseEditor();
85     d->editPipeline.plugTrainer();
86     d->editPipeline.construct();
87 
88     connect(&d->editPipeline, SIGNAL(scheduled()),
89             this, SLOT(slotInitProgressIndicator()));
90 
91     d->normalDelegate = new DigikamItemDelegate(this);
92     d->faceDelegate   = new ItemFaceDelegate(this);
93 
94     setItemDelegate(d->normalDelegate);
95     setSpacing(10);
96 
97     ApplicationSettings* const settings = ApplicationSettings::instance();
98 
99     imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByAlbum);
100 
101     imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread());
102 
103     // Virtual method: use Dynamic binding.
104 
105     this->setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize()));
106 
107     imageAlbumModel()->setPreloadThumbnails(true);
108 
109     imageModel()->setDragDropHandler(new ItemDragDropHandler(imageModel()));
110     setDragEnabled(true);
111     setAcceptDrops(true);
112     setDropIndicatorShown(false);
113 
114     setToolTipEnabled(settings->showToolTipsIsValid());
115     imageFilterModel()->setStringTypeNatural(settings->isStringTypeNatural());
116     imageFilterModel()->setSortRole((ItemSortSettings::SortRole)settings->getImageSortOrder());
117     imageFilterModel()->setSortOrder((ItemSortSettings::SortOrder)settings->getImageSorting());
118     imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)settings->getImageSeparationMode());
119     imageFilterModel()->setCategorizationSortOrder((ItemSortSettings::SortOrder) settings->getImageSeparationSortOrder());
120 
121     // selection overlay
122 
123     addSelectionOverlay(d->normalDelegate);
124     addSelectionOverlay(d->faceDelegate);
125 
126     // rotation overlays
127 
128     d->rotateLeftOverlay  = ItemRotateOverlay::left(this);
129     d->rotateRightOverlay = ItemRotateOverlay::right(this);
130     d->fullscreenOverlay  = ItemFullScreenOverlay::instance(this);
131     d->updateOverlays();
132 
133     // rating overlay
134 
135     ItemRatingOverlay* const ratingOverlay = new ItemRatingOverlay(this);
136     addOverlay(ratingOverlay);
137 
138     // face overlays
139     // NOTE: order to plug this overlay is important, else rejection cant be suitable (see bug #324759).
140 
141     addAssignNameOverlay(d->faceDelegate);
142     addRejectionOverlay(d->faceDelegate);
143 
144     GroupIndicatorOverlay* const groupOverlay = new GroupIndicatorOverlay(this);
145     addOverlay(groupOverlay);
146 
147     addOverlay(new ItemCoordinatesOverlay(this));
148 
149     connect(ratingOverlay, SIGNAL(ratingEdited(QList<QModelIndex>,int)),
150             this, SLOT(assignRating(QList<QModelIndex>,int)));
151 
152     connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)),
153             this, SLOT(groupIndicatorClicked(QModelIndex)));
154 
155     connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)),
156             this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*)));
157 
158     d->utilities = new ItemViewUtilities(this);
159 
160     connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList<ItemInfo>,QList<int>)),
161             FileActionMngr::instance(), SLOT(assignTags(QList<ItemInfo>,QList<int>)));
162 
163     connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ItemInfo,QList<ItemInfo>)),
164             FileActionMngr::instance(), SLOT(addToGroup(ItemInfo,QList<ItemInfo>)));
165 
166     connect(imageModel()->dragDropHandler(), SIGNAL(dragDropSort(ItemInfo,QList<ItemInfo>)),
167             this, SLOT(dragDropSort(ItemInfo,QList<ItemInfo>)));
168 
169     connect(d->utilities, SIGNAL(editorCurrentUrlChanged(QUrl)),
170             this, SLOT(setCurrentUrlWhenAvailable(QUrl)));
171 
172     // --- NOTE: use dynamic binding as slotSetupChanged() is a virtual method which can be re-implemented in derived classes.
173 
174     connect(settings, &ApplicationSettings::setupChanged,
175             this, &DigikamItemView::slotSetupChanged);
176 
177     this->slotSetupChanged();
178 }
179 
~DigikamItemView()180 DigikamItemView::~DigikamItemView()
181 {
182     delete d;
183 }
184 
utilities() const185 ItemViewUtilities* DigikamItemView::utilities() const
186 {
187     return d->utilities;
188 }
189 
setThumbnailSize(const ThumbnailSize & size)190 void DigikamItemView::setThumbnailSize(const ThumbnailSize& size)
191 {
192     imageThumbnailModel()->setPreloadThumbnailSize(size);
193     ItemCategorizedView::setThumbnailSize(size);
194 }
195 
allItemInfos(bool grouping) const196 ItemInfoList DigikamItemView::allItemInfos(bool grouping) const
197 {
198     if (grouping)
199     {
200         return resolveGrouping(ItemCategorizedView::allItemInfos());
201     }
202 
203     return ItemCategorizedView::allItemInfos();
204 }
205 
selectedItemInfos(bool grouping) const206 ItemInfoList DigikamItemView::selectedItemInfos(bool grouping) const
207 {
208     if (grouping)
209     {
210         return resolveGrouping(ItemCategorizedView::selectedItemInfos());
211     }
212 
213     return ItemCategorizedView::selectedItemInfos();
214 }
215 
selectedItemInfosCurrentFirst(bool grouping) const216 ItemInfoList DigikamItemView::selectedItemInfosCurrentFirst(bool grouping) const
217 {
218     if (grouping)
219     {
220         return resolveGrouping(ItemCategorizedView::selectedItemInfosCurrentFirst());
221     }
222 
223     return ItemCategorizedView::selectedItemInfosCurrentFirst();
224 }
225 
dragDropSort(const ItemInfo & pick,const QList<ItemInfo> & infos)226 void DigikamItemView::dragDropSort(const ItemInfo& pick, const QList<ItemInfo>& infos)
227 {
228     if (pick.isNull() || infos.isEmpty())
229     {
230         return;
231     }
232 
233     ItemInfoList infoList = allItemInfos(false);
234     qlonglong counter     = pick.manualOrder();
235     bool order            = (ApplicationSettings::instance()->
236                                getImageSorting() == Qt::AscendingOrder);
237     bool found            = false;
238 
239     QApplication::setOverrideCursor(Qt::WaitCursor);
240 
241     CoreDbOperationGroup group;
242     group.setMaximumTime(200);
243 
244     foreach (ItemInfo info, infoList)
245     {
246         if      (!found && info.id() == pick.id())
247         {
248             foreach (ItemInfo dropInfo, infos)
249             {
250                 dropInfo.setManualOrder(counter);
251                 counter += (order ? 1 : -1);
252             }
253 
254             info.setManualOrder(counter);
255             found = true;
256         }
257         else if (found && !infos.contains(info))
258         {
259             if (( order && info.manualOrder() > counter) ||
260                 (!order && info.manualOrder() < counter))
261             {
262                 break;
263             }
264 
265             counter += (order ? 100 : -100);
266             info.setManualOrder(counter);
267         }
268 
269         group.allowLift();
270     }
271 
272     QApplication::restoreOverrideCursor();
273 
274     imageFilterModel()->invalidate();
275  }
276 
allNeedGroupResolving(const ApplicationSettings::OperationType type) const277 bool DigikamItemView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const
278 {
279     return needGroupResolving(type, allItemInfos());
280 }
281 
selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const282 bool DigikamItemView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const
283 {
284     return needGroupResolving(type, selectedItemInfos());
285 }
286 
fitToWidthIcons()287 int DigikamItemView::fitToWidthIcons()
288 {
289     return delegate()->calculatethumbSizeToFit(viewport()->size().width());
290 }
291 
slotSetupChanged()292 void DigikamItemView::slotSetupChanged()
293 {
294     imageFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural());
295     setToolTipEnabled(ApplicationSettings::instance()->showToolTipsIsValid());
296     setFont(ApplicationSettings::instance()->getIconViewFont());
297 
298     d->updateOverlays();
299 
300     ItemCategorizedView::slotSetupChanged();
301 }
302 
hasHiddenGroupedImages(const ItemInfo & info) const303 bool DigikamItemView::hasHiddenGroupedImages(const ItemInfo& info) const
304 {
305     return (info.hasGroupedImages()                &&
306             !imageFilterModel()->isAllGroupsOpen() &&
307             !imageFilterModel()->isGroupOpen(info.id()));
308 }
309 
imageInfos(const QList<QModelIndex> & indexes,ApplicationSettings::OperationType type) const310 ItemInfoList DigikamItemView::imageInfos(const QList<QModelIndex>& indexes,
311                                          ApplicationSettings::OperationType type) const
312 {
313     ItemInfoList infos = ItemCategorizedView::imageInfos(indexes);
314 
315     if (needGroupResolving(type, infos))
316     {
317         return resolveGrouping(infos);
318     }
319 
320     return infos;
321 }
322 
getFaceMode() const323 bool DigikamItemView::getFaceMode() const
324 {
325     return d->faceMode;
326 }
327 
setFaceMode(bool on)328 void DigikamItemView::setFaceMode(bool on)
329 {
330     d->faceMode = on;
331 
332     if (on)
333     {
334         // See ItemLister, which creates a search the implements listing tag in the ioslave
335 
336         imageAlbumModel()->setSpecialTagListing(QLatin1String("faces"));
337         setItemDelegate(d->faceDelegate);
338 
339         // grouping is not very much compatible with faces
340 
341         imageFilterModel()->setAllGroupsOpen(true);
342 
343         // by default, Face View is categorized by Faces.
344 
345         imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByFaces);
346 
347         emit signalSeparationModeChanged((int)ItemSortSettings::CategoryByFaces);
348     }
349     else
350     {
351         imageAlbumModel()->setSpecialTagListing(QString());
352         setItemDelegate(d->normalDelegate);
353 
354         bool open = ApplicationSettings::instance()->getAllGroupsOpen();
355         int separationMode = ApplicationSettings::instance()->getImageSeparationMode();
356 
357         imageFilterModel()->setAllGroupsOpen(open);
358         imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)separationMode);
359 
360         emit signalSeparationModeChanged((int)separationMode);
361     }
362 }
363 
addRejectionOverlay(ItemDelegate * delegate)364 void DigikamItemView::addRejectionOverlay(ItemDelegate* delegate)
365 {
366     FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this);
367 
368     connect(rejectionOverlay, SIGNAL(rejectFaces(QList<QModelIndex>)),
369             this, SLOT(removeFaces(QList<QModelIndex>)));
370 
371     addOverlay(rejectionOverlay, delegate);
372 }
373 
374 /*
375 void DigikamItemView::addTagEditOverlay(ItemDelegate* delegate)
376 {
377     TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this);
378 
379     connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)),
380             this, SLOT(assignTag(QModelIndex,QString)));
381 
382     addOverlay(tagOverlay, delegate);
383 }
384 */
385 
addAssignNameOverlay(ItemDelegate * delegate)386 void DigikamItemView::addAssignNameOverlay(ItemDelegate* delegate)
387 {
388     AssignNameOverlay* const nameOverlay = new AssignNameOverlay(this);
389     addOverlay(nameOverlay, delegate);
390 
391     connect(nameOverlay, SIGNAL(confirmFaces(QList<QModelIndex>,int)),
392             this, SLOT(confirmFaces(QList<QModelIndex>,int)));
393 
394     connect(nameOverlay, SIGNAL(removeFaces(QList<QModelIndex>)),
395             this, SLOT(rejectFaces(QList<QModelIndex>)));
396 
397     connect(nameOverlay, SIGNAL(unknownFaces(QList<QModelIndex>)),
398             this, SLOT(unknownFaces(QList<QModelIndex>)));
399 }
400 
confirmFaces(const QList<QModelIndex> & indexes,int tagId)401 void DigikamItemView::confirmFaces(const QList<QModelIndex>& indexes, int tagId)
402 {
403     /**
404      * You aren't allowed to "confirm" a person as
405      * Ignored. Marking as Ignored is treated as a
406      * changeTag() operation.
407      */
408     if (FaceTags::isTheIgnoredPerson(tagId))
409     {
410         rejectFaces(indexes);
411         return;
412     }
413 
414     QList<ItemInfo>      infos;
415     QList<FaceTagsIface> faces;
416     QList<QModelIndex>   sourceIndexes;
417 
418     // fast-remove in the "unknown person" view
419 
420     Album* const album  = currentAlbum();
421     bool needFastRemove = false;
422 
423     if (album)
424     {
425         needFastRemove = (d->faceMode && (tagId != album->id()));
426     }
427 
428     foreach (const QModelIndex& index, indexes)
429     {
430         faces << d->faceDelegate->face(index);
431         infos << ItemModel::retrieveItemInfo(index);
432 
433         if (needFastRemove)
434         {
435             sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
436         }
437     }
438 
439     imageAlbumModel()->removeIndexes(sourceIndexes);
440 
441     for (int i = 0 ; i < infos.size() ; ++i)
442     {
443         d->editPipeline.confirm(infos[i], faces[i], tagId);
444     }
445 }
446 
removeFaces(const QList<QModelIndex> & indexes)447 void DigikamItemView::removeFaces(const QList<QModelIndex>& indexes)
448 {
449     QList<ItemInfo> infos;
450     QList<FaceTagsIface> faces;
451     QList<QModelIndex> sourceIndexes;
452 
453     foreach (const QModelIndex& index, indexes)
454     {
455         faces         << d->faceDelegate->face(index);
456         infos         << ItemModel::retrieveItemInfo(index);
457         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
458     }
459 
460     imageAlbumModel()->removeIndexes(sourceIndexes);
461 
462     for (int i = 0 ; i < infos.size() ; ++i)
463     {
464         d->editPipeline.remove(infos[i], faces[i]);
465     }
466 }
467 
unknownFaces(const QList<QModelIndex> & indexes)468 void DigikamItemView::unknownFaces(const QList<QModelIndex>& indexes)
469 {
470     QList<ItemInfo> infos;
471     QList<FaceTagsIface> faces;
472     QList<QModelIndex> sourceIndexes;
473 
474     foreach (const QModelIndex& index, indexes)
475     {
476         faces         << d->faceDelegate->face(index);
477         infos         << ItemModel::retrieveItemInfo(index);
478         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
479     }
480 
481     imageAlbumModel()->removeIndexes(sourceIndexes);
482 
483     for (int i = 0 ; i < infos.size() ; ++i)
484     {
485         d->editPipeline.editTag(infos[i], faces[i],
486                                 FaceTags::unknownPersonTagId());
487     }
488 }
489 
rejectFaces(const QList<QModelIndex> & indexes)490 void DigikamItemView::rejectFaces(const QList<QModelIndex>& indexes)
491 {
492     QList<ItemInfo> infos;
493     QList<FaceTagsIface> faces;
494     QList<QModelIndex> sourceIndexes;
495 
496     foreach (const QModelIndex& index, indexes)
497     {
498         faces         << d->faceDelegate->face(index);
499         infos         << ItemModel::retrieveItemInfo(index);
500         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
501     }
502 
503     imageAlbumModel()->removeIndexes(sourceIndexes);
504 
505     for (int i = 0 ; i < infos.size() ; ++i)
506     {
507         if (FaceTags::isTheUnknownPerson(faces[i].tagId()))
508         {
509             // Reject signal was sent from an Unknown Face. Mark as Ignored.
510 
511             d->editPipeline.editTag(infos[i], faces[i], FaceTags::ignoredPersonTagId());
512         }
513         else
514         {
515             // Reject face suggestion. Mark as Unknown.
516 
517             d->editPipeline.editTag(infos[i], faces[i], FaceTags::unknownPersonTagId());
518         }
519     }
520 }
521 
activated(const ItemInfo & info,Qt::KeyboardModifiers modifiers)522 void DigikamItemView::activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers)
523 {
524     if (info.isNull())
525     {
526         return;
527     }
528 
529     if (modifiers != Qt::AltModifier)
530     {
531         int leftClickAction = ApplicationSettings::instance()->getItemLeftClickAction();
532 
533         if      (leftClickAction == ApplicationSettings::ShowPreview)
534         {
535             emit previewRequested(info);
536         }
537         else if (leftClickAction == ApplicationSettings::StartEditor)
538         {
539             openFile(info);
540         }
541         else
542         {
543             d->utilities->openInfosWithDefaultApplication(QList<ItemInfo>() << info);
544         }
545     }
546     else
547     {
548         d->utilities->openInfosWithDefaultApplication(QList<ItemInfo>() << info);
549     }
550 }
551 
showContextMenuOnInfo(QContextMenuEvent * event,const ItemInfo & info)552 void DigikamItemView::showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info)
553 {
554     emit signalShowContextMenuOnInfo(event, info, QList<QAction*>(), imageFilterModel());
555 }
556 
showGroupContextMenu(const QModelIndex & index,QContextMenuEvent * event)557 void DigikamItemView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event)
558 {
559     Q_UNUSED(index);
560     emit signalShowGroupContextMenu(event, selectedItemInfosCurrentFirst(), imageFilterModel());
561 }
562 
showContextMenu(QContextMenuEvent * event)563 void DigikamItemView::showContextMenu(QContextMenuEvent* event)
564 {
565     emit signalShowContextMenu(event);
566 }
567 
openFile(const ItemInfo & info)568 void DigikamItemView::openFile(const ItemInfo& info)
569 {
570     d->utilities->openInfos(info, allItemInfos(), currentAlbum());
571 }
572 
deleteSelected(const ItemViewUtilities::DeleteMode deleteMode)573 void DigikamItemView::deleteSelected(const ItemViewUtilities::DeleteMode deleteMode)
574 {
575     ItemInfoList imageInfoList = selectedItemInfos(true);
576 
577     if (d->utilities->deleteImages(imageInfoList, deleteMode))
578     {
579         awayFromSelection();
580     }
581 }
582 
deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode)583 void DigikamItemView::deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode)
584 {
585     ItemInfoList imageInfoList = selectedItemInfos(true);
586 
587     d->utilities->deleteImagesDirectly(imageInfoList, deleteMode);
588     awayFromSelection();
589 }
590 
assignRating(const QList<QModelIndex> & indexes,int rating)591 void DigikamItemView::assignRating(const QList<QModelIndex>& indexes, int rating)
592 {
593     ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata);
594     FileActionMngr::instance()->assignRating(infos, rating);
595 }
596 
groupIndicatorClicked(const QModelIndex & index)597 void DigikamItemView::groupIndicatorClicked(const QModelIndex& index)
598 {
599     ItemInfo info = imageFilterModel()->imageInfo(index);
600 
601     if (info.isNull())
602     {
603         return;
604     }
605 
606     setCurrentIndex(index);
607     imageFilterModel()->toggleGroupOpen(info.id());
608     imageAlbumModel()->ensureHasGroupedImages(info);
609 }
610 
rename()611 void DigikamItemView::rename()
612 {
613     ItemInfoList infos = selectedItemInfos();
614 
615     if (needGroupResolving(ApplicationSettings::Rename, infos))
616     {
617         infos = resolveGrouping(infos);
618     }
619 
620     QList<QUrl> urls = infos.toImageUrlList();
621     bool loop        = false;
622     NewNamesList newNamesList;
623 
624     do
625     {
626         qCDebug(DIGIKAM_GENERAL_LOG) << "Selected URLs to rename: " << urls;
627 
628         QPointer<AdvancedRenameDialog> dlg = new AdvancedRenameDialog(this);
629         dlg->slotAddImages(urls);
630 
631         if (dlg->exec() != QDialog::Accepted)
632         {
633             delete dlg;
634             break;
635         }
636 
637         if (!loop)
638         {
639             QUrl nextUrl = nextInOrder(infos.last(), 1).fileUrl();
640             setCurrentUrl(nextUrl);
641             loop = true;
642         }
643 
644         newNamesList = dlg->newNames();
645         delete dlg;
646 
647         setFocus();
648         qApp->processEvents();
649 
650         if (!newNamesList.isEmpty())
651         {
652             QPointer<AdvancedRenameProcessDialog> dlg2 = new AdvancedRenameProcessDialog(newNamesList, this);
653             (void)dlg2->exec();
654 
655             imageFilterModel()->invalidate();
656             urls = dlg2->failedUrls();
657             delete dlg2;
658         }
659     }
660     while (!urls.isEmpty() && !newNamesList.isEmpty());
661 }
662 
slotRotateLeft(const QList<QModelIndex> & indexes)663 void DigikamItemView::slotRotateLeft(const QList<QModelIndex>& indexes)
664 {
665     ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata);
666     FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate270);
667 }
668 
slotRotateRight(const QList<QModelIndex> & indexes)669 void DigikamItemView::slotRotateRight(const QList<QModelIndex>& indexes)
670 {
671     ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata);
672     FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate90);
673 }
674 
slotFullscreen(const QList<QModelIndex> & indexes)675 void DigikamItemView::slotFullscreen(const QList<QModelIndex>& indexes)
676 {
677    QList<ItemInfo> infos = imageInfos(indexes, ApplicationSettings::Slideshow);
678 
679    if (infos.isEmpty())
680    {
681         return;
682    }
683 
684    // Just fullscreen the first.
685 
686    const ItemInfo& info = infos.at(0);
687 
688    QList<DPluginAction*> actions = DPluginLoader::instance()->
689                                       pluginActions(QLatin1String("org.kde.digikam.plugin.generic.SlideShow"),
690                                       DigikamApp::instance());
691 
692    if (actions.isEmpty())
693    {
694        return;
695    }
696 
697    // Trigger SlideShow manual
698 
699    actions[0]->setData(info.fileUrl());
700    actions[0]->trigger();
701 }
702 
slotInitProgressIndicator()703 void DigikamItemView::slotInitProgressIndicator()
704 {
705     if (!ProgressManager::instance()->findItembyId(QLatin1String("FaceActionProgress")))
706     {
707         FileActionProgress* const item = new FileActionProgress(QLatin1String("FaceActionProgress"));
708 
709         connect(&d->editPipeline, SIGNAL(started(QString)),
710                 item, SLOT(slotProgressStatus(QString)));
711 
712         connect(&d->editPipeline, SIGNAL(progressValueChanged(float)),
713                 item, SLOT(slotProgressValue(float)));
714 
715         connect(&d->editPipeline, SIGNAL(finished()),
716                 item, SLOT(slotCompleted()));
717     }
718 }
719 
720 } // namespace Digikam
721