File indexing completed on 2025-01-05 03:51:13

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-04-24
0007  * Description : Qt model-view for items
0008  *
0009  * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2011      by Andi Clemens <andi dot clemens at gmail dot com>
0012  * SPDX-FileCopyrightText: 2013      by Michael G. Hansen <mike at mghansen dot de>
0013  * SPDX-FileCopyrightText: 2014      by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0014  * SPDX-FileCopyrightText: 2017      by Simon Frei <freisim93 at gmail dot com>
0015  *
0016  * SPDX-License-Identifier: GPL-2.0-or-later
0017  *
0018  * ============================================================ */
0019 
0020 #include "digikamitemview_p.h"
0021 
0022 // Qt includes
0023 
0024 #include <QApplication>
0025 #include <QPointer>
0026 #include <QAction>
0027 #include <QMenu>
0028 #include <QIcon>
0029 #include <QUrl>
0030 
0031 // Local includes
0032 
0033 #include "digikam_debug.h"
0034 #include "albummanager.h"
0035 #include "coredb.h"
0036 #include "coredboperationgroup.h"
0037 #include "advancedrenamedialog.h"
0038 #include "advancedrenameprocessdialog.h"
0039 #include "assignnameoverlay.h"
0040 #include "contextmenuhelper.h"
0041 #include "coredbaccess.h"
0042 #include "ddragobjects.h"
0043 #include "digikamapp.h"
0044 #include "digikamitemdelegate.h"
0045 #include "itemfacedelegate.h"
0046 #include "dio.h"
0047 #include "groupindicatoroverlay.h"
0048 #include "itemalbumfiltermodel.h"
0049 #include "itemalbummodel.h"
0050 #include "itemdragdrop.h"
0051 #include "itemratingoverlay.h"
0052 #include "itemfullscreenoverlay.h"
0053 #include "itemcoordinatesoverlay.h"
0054 #include "tagslineeditoverlay.h"
0055 #include "imagewindow.h"
0056 #include "fileactionmngr.h"
0057 #include "fileactionprogress.h"
0058 #include "thumbnailloadthread.h"
0059 #include "tagregion.h"
0060 #include "addtagslineedit.h"
0061 #include "facerejectionoverlay.h"
0062 #include "facetagsiface.h"
0063 
0064 namespace Digikam
0065 {
0066 
0067 DigikamItemView::DigikamItemView(QWidget* const parent)
0068     : ItemCategorizedView(parent),
0069       d                  (new Private(this))
0070 {
0071     installDefaultModels();
0072 
0073     d->editPipeline.plugDatabaseEditor();
0074     d->editPipeline.plugTrainer();
0075     d->editPipeline.construct();
0076 
0077     connect(&d->editPipeline, SIGNAL(scheduled()),
0078             this, SLOT(slotInitProgressIndicator()));
0079 
0080     d->normalDelegate = new DigikamItemDelegate(this);
0081     d->faceDelegate   = new ItemFaceDelegate(this);
0082 
0083     setItemDelegate(d->normalDelegate);
0084     setSpacing(10);
0085 
0086     ApplicationSettings* const settings = ApplicationSettings::instance();
0087 
0088     imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByAlbum);
0089 
0090     imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread());
0091 
0092     // Virtual method: use Dynamic binding.
0093 
0094     this->setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize()));
0095 
0096     imageAlbumModel()->setPreloadThumbnails(true);
0097 
0098     imageModel()->setDragDropHandler(new ItemDragDropHandler(imageModel()));
0099     setDragEnabled(true);
0100     setAcceptDrops(true);
0101     setDropIndicatorShown(false);
0102 
0103     setToolTipEnabled(settings->showToolTipsIsValid());
0104     imageFilterModel()->setStringTypeNatural(settings->isStringTypeNatural());
0105     imageFilterModel()->setSortRole((ItemSortSettings::SortRole)settings->getImageSortOrder());
0106     imageFilterModel()->setSortOrder((ItemSortSettings::SortOrder)settings->getImageSorting());
0107     imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)settings->getImageSeparationMode());
0108     imageFilterModel()->setCategorizationSortOrder((ItemSortSettings::SortOrder) settings->getImageSeparationSortOrder());
0109 
0110     // selection overlay
0111 
0112     addSelectionOverlay(d->normalDelegate);
0113     addSelectionOverlay(d->faceDelegate);
0114 
0115     // rotation overlays
0116 
0117     d->rotateLeftOverlay  = ItemRotateOverlay::left(this);
0118     d->rotateRightOverlay = ItemRotateOverlay::right(this);
0119     d->fullscreenOverlay  = ItemFullScreenOverlay::instance(this);
0120     d->updateOverlays();
0121 
0122     // rating overlay
0123 
0124     ItemRatingOverlay* const ratingOverlay = new ItemRatingOverlay(this);
0125     addOverlay(ratingOverlay);
0126 
0127     // face overlays
0128     // NOTE: order to plug this overlay is important, else rejection cant be suitable (see bug #324759).
0129 
0130     addAssignNameOverlay(d->faceDelegate);
0131     addRejectionOverlay(d->faceDelegate);
0132 
0133     GroupIndicatorOverlay* const groupOverlay = new GroupIndicatorOverlay(this);
0134     addOverlay(groupOverlay);
0135 
0136     addOverlay(new ItemCoordinatesOverlay(this));
0137 
0138     connect(ratingOverlay, SIGNAL(ratingEdited(QList<QModelIndex>,int)),
0139             this, SLOT(assignRating(QList<QModelIndex>,int)));
0140 
0141     connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)),
0142             this, SLOT(groupIndicatorClicked(QModelIndex)));
0143 
0144     connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)),
0145             this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*)));
0146 
0147     d->utilities = new ItemViewUtilities(this);
0148 
0149     connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList<ItemInfo>,QList<int>)),
0150             FileActionMngr::instance(), SLOT(assignTags(QList<ItemInfo>,QList<int>)));
0151 
0152     connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ItemInfo,QList<ItemInfo>)),
0153             FileActionMngr::instance(), SLOT(addToGroup(ItemInfo,QList<ItemInfo>)));
0154 
0155     connect(imageModel()->dragDropHandler(), SIGNAL(dragDropSort(ItemInfo,QList<ItemInfo>)),
0156             this, SLOT(dragDropSort(ItemInfo,QList<ItemInfo>)));
0157 
0158     connect(d->utilities, SIGNAL(editorCurrentUrlChanged(QUrl)),
0159             this, SLOT(setCurrentUrlWhenAvailable(QUrl)));
0160 
0161     // --- NOTE: use dynamic binding as slotSetupChanged() is a virtual method which can be re-implemented in derived classes.
0162 
0163     connect(settings, &ApplicationSettings::setupChanged,
0164             this, &DigikamItemView::slotSetupChanged);
0165 
0166     this->slotSetupChanged();
0167 }
0168 
0169 DigikamItemView::~DigikamItemView()
0170 {
0171     delete d;
0172 }
0173 
0174 ItemViewUtilities* DigikamItemView::utilities() const
0175 {
0176     return d->utilities;
0177 }
0178 
0179 void DigikamItemView::setThumbnailSize(const ThumbnailSize& size)
0180 {
0181     imageThumbnailModel()->setPreloadThumbnailSize(size);
0182     ItemCategorizedView::setThumbnailSize(size);
0183 }
0184 
0185 ItemInfoList DigikamItemView::allItemInfos(bool grouping) const
0186 {
0187     if (grouping)
0188     {
0189         return resolveGrouping(ItemCategorizedView::allItemInfos());
0190     }
0191 
0192     return ItemCategorizedView::allItemInfos();
0193 }
0194 
0195 ItemInfoList DigikamItemView::selectedItemInfos(bool grouping) const
0196 {
0197     if (grouping)
0198     {
0199         return resolveGrouping(ItemCategorizedView::selectedItemInfos());
0200     }
0201 
0202     return ItemCategorizedView::selectedItemInfos();
0203 }
0204 
0205 ItemInfoList DigikamItemView::selectedItemInfosCurrentFirst(bool grouping) const
0206 {
0207     if (grouping)
0208     {
0209         return resolveGrouping(ItemCategorizedView::selectedItemInfosCurrentFirst());
0210     }
0211 
0212     return ItemCategorizedView::selectedItemInfosCurrentFirst();
0213 }
0214 
0215 void DigikamItemView::dragDropSort(const ItemInfo& pick, const QList<ItemInfo>& infos)
0216 {
0217     if (pick.isNull() || infos.isEmpty())
0218     {
0219         return;
0220     }
0221 
0222     ItemInfoList infoList = allItemInfos(false);
0223     qlonglong counter     = pick.manualOrder();
0224     bool order            = (ApplicationSettings::instance()->
0225                                getImageSorting() == Qt::AscendingOrder);
0226     bool found            = false;
0227 
0228     QApplication::setOverrideCursor(Qt::WaitCursor);
0229 
0230     CoreDbOperationGroup group;
0231     group.setMaximumTime(200);
0232 
0233     Q_FOREACH (ItemInfo info, infoList)
0234     {
0235         if      (!found && info.id() == pick.id())
0236         {
0237             Q_FOREACH (ItemInfo dropInfo, infos)
0238             {
0239                 dropInfo.setManualOrder(counter);
0240                 counter += (order ? 1 : -1);
0241             }
0242 
0243             info.setManualOrder(counter);
0244             found = true;
0245         }
0246         else if (found && !infos.contains(info))
0247         {
0248             if (( order && info.manualOrder() > counter) ||
0249                 (!order && info.manualOrder() < counter))
0250             {
0251                 break;
0252             }
0253 
0254             counter += (order ? 100 : -100);
0255             info.setManualOrder(counter);
0256         }
0257 
0258         group.allowLift();
0259     }
0260 
0261     QApplication::restoreOverrideCursor();
0262 
0263     imageFilterModel()->invalidate();
0264  }
0265 
0266 bool DigikamItemView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const
0267 {
0268     return needGroupResolving(type, allItemInfos());
0269 }
0270 
0271 bool DigikamItemView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const
0272 {
0273     return needGroupResolving(type, selectedItemInfos());
0274 }
0275 
0276 int DigikamItemView::fitToWidthIcons()
0277 {
0278     return delegate()->calculatethumbSizeToFit(viewport()->size().width());
0279 }
0280 
0281 void DigikamItemView::slotSetupChanged()
0282 {
0283     imageFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural());
0284     setToolTipEnabled(ApplicationSettings::instance()->showToolTipsIsValid());
0285     setFont(ApplicationSettings::instance()->getIconViewFont());
0286 
0287     d->updateOverlays();
0288 
0289     ItemCategorizedView::slotSetupChanged();
0290 }
0291 
0292 bool DigikamItemView::hasHiddenGroupedImages(const ItemInfo& info) const
0293 {
0294     return (info.hasGroupedImages()                &&
0295             !imageFilterModel()->isAllGroupsOpen() &&
0296             !imageFilterModel()->isGroupOpen(info.id()));
0297 }
0298 
0299 ItemInfoList DigikamItemView::imageInfos(const QList<QModelIndex>& indexes,
0300                                          ApplicationSettings::OperationType type) const
0301 {
0302     ItemInfoList infos = ItemCategorizedView::imageInfos(indexes);
0303 
0304     if (needGroupResolving(type, infos))
0305     {
0306         return resolveGrouping(infos);
0307     }
0308 
0309     return infos;
0310 }
0311 
0312 bool DigikamItemView::getFaceMode() const
0313 {
0314     return d->faceMode;
0315 }
0316 
0317 void DigikamItemView::setFaceMode(bool on)
0318 {
0319     d->faceMode = on;
0320 
0321     if (on)
0322     {
0323         // See ItemLister, which creates a search the implements listing tag in the ioslave
0324 
0325         imageAlbumModel()->setSpecialTagListing(QLatin1String("faces"));
0326         setItemDelegate(d->faceDelegate);
0327 
0328         // grouping is not very much compatible with faces
0329 
0330         imageFilterModel()->setAllGroupsOpen(true);
0331 
0332         // by default, Face View is categorized by Faces.
0333 
0334         imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByFaces);
0335 
0336         Q_EMIT signalSeparationModeChanged((int)ItemSortSettings::CategoryByFaces);
0337     }
0338     else
0339     {
0340         imageAlbumModel()->setSpecialTagListing(QString());
0341         setItemDelegate(d->normalDelegate);
0342 
0343         bool open = ApplicationSettings::instance()->getAllGroupsOpen();
0344         int separationMode = ApplicationSettings::instance()->getImageSeparationMode();
0345 
0346         imageFilterModel()->setAllGroupsOpen(open);
0347         imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)separationMode);
0348 
0349         Q_EMIT signalSeparationModeChanged((int)separationMode);
0350     }
0351 }
0352 
0353 void DigikamItemView::addRejectionOverlay(ItemDelegate* delegate)
0354 {
0355     FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this);
0356 
0357     connect(rejectionOverlay, SIGNAL(rejectFaces(QList<QModelIndex>)),
0358             this, SLOT(removeFaces(QList<QModelIndex>)));
0359 
0360     addOverlay(rejectionOverlay, delegate);
0361 }
0362 
0363 /*
0364 void DigikamItemView::addTagEditOverlay(ItemDelegate* delegate)
0365 {
0366     TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this);
0367 
0368     connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)),
0369             this, SLOT(assignTag(QModelIndex,QString)));
0370 
0371     addOverlay(tagOverlay, delegate);
0372 }
0373 */
0374 
0375 void DigikamItemView::addAssignNameOverlay(ItemDelegate* delegate)
0376 {
0377     AssignNameOverlay* const nameOverlay = new AssignNameOverlay(this);
0378     addOverlay(nameOverlay, delegate);
0379 
0380     connect(nameOverlay, SIGNAL(confirmFaces(QList<QModelIndex>,int)),
0381             this, SLOT(confirmFaces(QList<QModelIndex>,int)));
0382 
0383     connect(nameOverlay, SIGNAL(removeFaces(QList<QModelIndex>)),
0384             this, SLOT(rejectFaces(QList<QModelIndex>)));
0385 
0386     connect(nameOverlay, SIGNAL(unknownFaces(QList<QModelIndex>)),
0387             this, SLOT(unknownFaces(QList<QModelIndex>)));
0388 
0389     connect(nameOverlay, SIGNAL(ignoreFaces(QList<QModelIndex>)),
0390             this, SLOT(ignoreFaces(QList<QModelIndex>)));
0391 }
0392 
0393 void DigikamItemView::confirmFaces(const QList<QModelIndex>& indexes, int tagId)
0394 {
0395     /**
0396      * You aren't allowed to "confirm" a person as
0397      * Ignored. Marking as Ignored is treated as a
0398      * changeTag() operation.
0399      */
0400     if (FaceTags::isTheIgnoredPerson(tagId))
0401     {
0402         rejectFaces(indexes);
0403         return;
0404     }
0405 
0406     QList<ItemInfo>      infos;
0407     QList<FaceTagsIface> faces;
0408     QList<QModelIndex>   sourceIndexes;
0409 
0410     // fast-remove in the "unknown person" view
0411 
0412     Album* const album  = currentAlbum();
0413     bool needFastRemove = false;
0414 
0415     if (album)
0416     {
0417         needFastRemove = (d->faceMode && (tagId != album->id()));
0418     }
0419 
0420     Q_FOREACH (const QModelIndex& index, indexes)
0421     {
0422         faces << d->faceDelegate->face(index);
0423         infos << ItemModel::retrieveItemInfo(index);
0424 
0425         if (needFastRemove)
0426         {
0427             sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
0428         }
0429     }
0430 
0431     imageAlbumModel()->removeIndexes(sourceIndexes);
0432 
0433     for (int i = 0 ; i < infos.size() ; ++i)
0434     {
0435         d->editPipeline.confirm(infos[i], faces[i], tagId);
0436     }
0437 
0438     clearSelection();
0439 }
0440 
0441 void DigikamItemView::removeFaces(const QList<QModelIndex>& indexes)
0442 {
0443     QList<ItemInfo> infos;
0444     QList<FaceTagsIface> faces;
0445     QList<QModelIndex> sourceIndexes;
0446 
0447     Q_FOREACH (const QModelIndex& index, indexes)
0448     {
0449         faces         << d->faceDelegate->face(index);
0450         infos         << ItemModel::retrieveItemInfo(index);
0451         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
0452     }
0453 
0454     imageAlbumModel()->removeIndexes(sourceIndexes);
0455 
0456     for (int i = 0 ; i < infos.size() ; ++i)
0457     {
0458         d->editPipeline.remove(infos[i], faces[i]);
0459     }
0460 
0461     clearSelection();
0462 }
0463 
0464 void DigikamItemView::unknownFaces(const QList<QModelIndex>& indexes)
0465 {
0466     QList<ItemInfo> infos;
0467     QList<FaceTagsIface> faces;
0468     QList<QModelIndex> sourceIndexes;
0469 
0470     Q_FOREACH (const QModelIndex& index, indexes)
0471     {
0472         faces         << d->faceDelegate->face(index);
0473         infos         << ItemModel::retrieveItemInfo(index);
0474         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
0475     }
0476 
0477     imageAlbumModel()->removeIndexes(sourceIndexes);
0478 
0479     for (int i = 0 ; i < infos.size() ; ++i)
0480     {
0481         d->editPipeline.editTag(infos[i], faces[i],
0482                                 FaceTags::unknownPersonTagId());
0483     }
0484 
0485     clearSelection();
0486 }
0487 
0488 void DigikamItemView::rejectFaces(const QList<QModelIndex>& indexes)
0489 {
0490     QList<ItemInfo> infos;
0491     QList<FaceTagsIface> faces;
0492     QList<QModelIndex> sourceIndexes;
0493 
0494     Q_FOREACH (const QModelIndex& index, indexes)
0495     {
0496         faces         << d->faceDelegate->face(index);
0497         infos         << ItemModel::retrieveItemInfo(index);
0498         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
0499     }
0500 
0501     imageAlbumModel()->removeIndexes(sourceIndexes);
0502 
0503     for (int i = 0 ; i < infos.size() ; ++i)
0504     {
0505         if      (FaceTags::isTheUnknownPerson(faces[i].tagId()))
0506         {
0507             // Reject signal was sent from an Unknown Face. Mark as Ignored.
0508 
0509             d->editPipeline.editTag(infos[i], faces[i], FaceTags::ignoredPersonTagId());
0510         }
0511         else if (FaceTags::isTheIgnoredPerson(faces[i].tagId()))
0512         {
0513             // Reject signal was sent from an Ignored Face. Remove face.
0514 
0515             d->editPipeline.remove(infos[i], faces[i]);
0516         }
0517         else
0518         {
0519             // Reject face suggestion. Mark as Unknown.
0520 
0521             d->editPipeline.editTag(infos[i], faces[i], FaceTags::unknownPersonTagId());
0522         }
0523     }
0524 
0525     clearSelection();
0526 }
0527 
0528 void DigikamItemView::ignoreFaces(const QList<QModelIndex>& indexes)
0529 {
0530     QList<ItemInfo> infos;
0531     QList<FaceTagsIface> faces;
0532     QList<QModelIndex> sourceIndexes;
0533 
0534     Q_FOREACH (const QModelIndex& index, indexes)
0535     {
0536         faces         << d->faceDelegate->face(index);
0537         infos         << ItemModel::retrieveItemInfo(index);
0538         sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index);
0539     }
0540 
0541     imageAlbumModel()->removeIndexes(sourceIndexes);
0542 
0543     for (int i = 0 ; i < infos.size() ; ++i)
0544     {
0545         d->editPipeline.editTag(infos[i], faces[i],
0546                                 FaceTags::ignoredPersonTagId());
0547     }
0548 
0549     clearSelection();
0550 }
0551 
0552 QList<int> DigikamItemView::getFaceIds(const QList<QModelIndex>& indexes) const
0553 {
0554     QList<int> ids;
0555 
0556     Q_FOREACH (const QModelIndex& index, indexes)
0557     {
0558         ids << d->faceDelegate->face(index).tagId();
0559     }
0560 
0561     return ids;
0562 }
0563 
0564 void DigikamItemView::activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers)
0565 {
0566     if (info.isNull())
0567     {
0568         return;
0569     }
0570 
0571     if (modifiers != Qt::AltModifier)
0572     {
0573         int leftClickAction = ApplicationSettings::instance()->getItemLeftClickAction();
0574 
0575         if      (leftClickAction == ApplicationSettings::ShowPreview)
0576         {
0577             Q_EMIT previewRequested(info);
0578         }
0579         else if (leftClickAction == ApplicationSettings::StartEditor)
0580         {
0581             openFile(info);
0582         }
0583         else if (leftClickAction == ApplicationSettings::ShowOnTable)
0584         {
0585             d->utilities->insertToLightTable(allItemInfos(), info, false);
0586         }
0587         else
0588         {
0589             d->utilities->openInfosWithDefaultApplication(QList<ItemInfo>() << info);
0590         }
0591     }
0592     else
0593     {
0594         d->utilities->openInfosWithDefaultApplication(QList<ItemInfo>() << info);
0595     }
0596 }
0597 
0598 void DigikamItemView::showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info)
0599 {
0600     Q_EMIT signalShowContextMenuOnInfo(event, info, QList<QAction*>(), imageFilterModel());
0601 }
0602 
0603 void DigikamItemView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event)
0604 {
0605     Q_UNUSED(index);
0606     Q_EMIT signalShowGroupContextMenu(event, selectedItemInfosCurrentFirst(), imageFilterModel());
0607 }
0608 
0609 void DigikamItemView::showContextMenu(QContextMenuEvent* event)
0610 {
0611     Q_EMIT signalShowContextMenu(event);
0612 }
0613 
0614 void DigikamItemView::openFile(const ItemInfo& info)
0615 {
0616     d->utilities->openInfos(info, allItemInfos(), currentAlbum());
0617 }
0618 
0619 void DigikamItemView::deleteSelected(const ItemViewUtilities::DeleteMode deleteMode)
0620 {
0621     ItemInfoList imageInfoList = selectedItemInfos(true);
0622 
0623     if (d->utilities->deleteImages(imageInfoList, deleteMode))
0624     {
0625         awayFromSelection();
0626     }
0627 }
0628 
0629 void DigikamItemView::deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode)
0630 {
0631     ItemInfoList imageInfoList = selectedItemInfos(true);
0632 
0633     d->utilities->deleteImagesDirectly(imageInfoList, deleteMode);
0634     awayFromSelection();
0635 }
0636 
0637 void DigikamItemView::assignRating(const QList<QModelIndex>& indexes, int rating)
0638 {
0639     ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata);
0640     FileActionMngr::instance()->assignRating(infos, rating);
0641 }
0642 
0643 void DigikamItemView::groupIndicatorClicked(const QModelIndex& index)
0644 {
0645     ItemInfo info = imageFilterModel()->imageInfo(index);
0646 
0647     if (info.isNull())
0648     {
0649         return;
0650     }
0651 
0652     setCurrentIndex(index);
0653     imageFilterModel()->toggleGroupOpen(info.id());
0654     imageAlbumModel()->ensureHasGroupedImages(info);
0655 }
0656 
0657 void DigikamItemView::rename()
0658 {
0659     ItemInfoList infos = selectedItemInfos();
0660 
0661     if (needGroupResolving(ApplicationSettings::Rename, infos))
0662     {
0663         infos = resolveGrouping(infos);
0664     }
0665 
0666     QList<QUrl> urls = infos.toImageUrlList();
0667     bool loop        = false;
0668     NewNamesList newNamesList;
0669 
0670     do
0671     {
0672         qCDebug(DIGIKAM_GENERAL_LOG) << "Selected URLs to rename: " << urls;
0673 
0674         QPointer<AdvancedRenameDialog> dlg = new AdvancedRenameDialog(this);
0675         dlg->slotAddImages(urls);
0676 
0677         if (dlg->exec() != QDialog::Accepted)
0678         {
0679             delete dlg;
0680             break;
0681         }
0682 
0683         if (!loop)
0684         {
0685             QUrl nextUrl = nextInOrder(infos.last(), 1).fileUrl();
0686             setCurrentUrl(nextUrl);
0687             loop = true;
0688         }
0689 
0690         newNamesList = dlg->newNames();
0691         delete dlg;
0692 
0693         setFocus();
0694         qApp->processEvents();
0695 
0696         if (!newNamesList.isEmpty())
0697         {
0698             QPointer<AdvancedRenameProcessDialog> dlg2 = new AdvancedRenameProcessDialog(newNamesList, this);
0699             (void)dlg2->exec();
0700 
0701             imageFilterModel()->invalidate();
0702             urls = dlg2->failedUrls();
0703             delete dlg2;
0704         }
0705     }
0706     while (!urls.isEmpty() && !newNamesList.isEmpty());
0707 }
0708 
0709 void DigikamItemView::slotRotateLeft(const QList<QModelIndex>& indexes)
0710 {
0711     ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata);
0712     FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate270);
0713 }
0714 
0715 void DigikamItemView::slotRotateRight(const QList<QModelIndex>& indexes)
0716 {
0717     ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata);
0718     FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate90);
0719 }
0720 
0721 void DigikamItemView::slotFullscreen(const QList<QModelIndex>& indexes)
0722 {
0723    QList<ItemInfo> infos = imageInfos(indexes, ApplicationSettings::Slideshow);
0724 
0725    if (infos.isEmpty())
0726    {
0727         return;
0728    }
0729 
0730    // Just fullscreen the first.
0731 
0732    const ItemInfo& info = infos.at(0);
0733 
0734    QList<DPluginAction*> actions = DPluginLoader::instance()->
0735                                       pluginActions(QLatin1String("org.kde.digikam.plugin.generic.SlideShow"),
0736                                       DigikamApp::instance());
0737 
0738    if (actions.isEmpty())
0739    {
0740        return;
0741    }
0742 
0743    // Trigger SlideShow manual
0744 
0745    actions[0]->setData(info.fileUrl());
0746    actions[0]->trigger();
0747 }
0748 
0749 void DigikamItemView::slotInitProgressIndicator()
0750 {
0751     if (!ProgressManager::instance()->findItembyId(QLatin1String("FaceActionProgress")))
0752     {
0753         FileActionProgress* const item = new FileActionProgress(QLatin1String("FaceActionProgress"));
0754 
0755         connect(&d->editPipeline, SIGNAL(started(QString)),
0756                 item, SLOT(slotProgressStatus(QString)));
0757 
0758         connect(&d->editPipeline, SIGNAL(progressValueChanged(float)),
0759                 item, SLOT(slotProgressValue(float)));
0760 
0761         connect(&d->editPipeline, SIGNAL(finished()),
0762                 item, SLOT(slotCompleted()));
0763     }
0764 }
0765 
0766 void DigikamItemView::scrollTo(const QModelIndex& index, ScrollHint hint)
0767 {
0768     // we do not want to change the view, when in the "Thumbnails" view in "People"
0769     // see bugs 444692, 40232, ...
0770 
0771     bool runningFaceAction = (ProgressManager::instance()->findItembyId(QLatin1String("FacesDetector"))    ||
0772                               ProgressManager::instance()->findItembyId(QLatin1String("FaceActionProgress")));
0773 
0774     if ((viewMode() == QListView::IconMode) && getFaceMode() && runningFaceAction)
0775     {
0776         return;
0777     }
0778 
0779     ItemCategorizedView::scrollTo(index, hint);
0780 }
0781 
0782 } // namespace Digikam
0783 
0784 #include "moc_digikamitemview.cpp"