File indexing completed on 2025-01-19 03:57:33

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-07-13
0007  * Description : Qt categorized item view for showfoto items
0008  *
0009  * SPDX-FileCopyrightText: 2013 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "showfotocategorizedview.h"
0016 
0017 // Qt includes
0018 
0019 #include <QTimer>
0020 
0021 // Local include
0022 
0023 #include "digikam_debug.h"
0024 #include "loadingcacheinterface.h"
0025 #include "itemviewtooltip.h"
0026 #include "showfotodelegate.h"
0027 #include "showfototooltipfiller.h"
0028 
0029 using namespace Digikam;
0030 
0031 namespace ShowFoto
0032 {
0033 
0034 class Q_DECL_HIDDEN ShowfotoItemViewToolTip : public ItemViewToolTip
0035 {
0036     Q_OBJECT
0037 
0038 public:
0039 
0040     explicit ShowfotoItemViewToolTip(ShowfotoCategorizedView* const view)
0041         : ItemViewToolTip(view)
0042     {
0043     }
0044 
0045     ShowfotoCategorizedView* view() const
0046     {
0047         return static_cast<ShowfotoCategorizedView*>(ItemViewToolTip::view());
0048     }
0049 
0050 protected:
0051 
0052     QString tipContents() override
0053     {
0054         ShowfotoItemInfo info = ShowfotoItemModel::retrieveShowfotoItemInfo(currentIndex());
0055 
0056         return ShowfotoToolTipFiller::ShowfotoItemInfoTipContents(info);
0057     }
0058 };
0059 
0060 class Q_DECL_HIDDEN ShowfotoCategorizedView::Private
0061 {
0062 public:
0063 
0064     explicit Private()
0065       : model            (nullptr),
0066         filterModel      (nullptr),
0067         delegate         (nullptr),
0068         showToolTip      (false),
0069         scrollToItemId   (0),
0070         delayedEnterTimer(nullptr),
0071         currentMouseEvent(nullptr)
0072     {
0073     }
0074 
0075     ShowfotoItemModel*       model;
0076     ShowfotoSortFilterModel* filterModel;
0077 
0078     ShowfotoDelegate*        delegate;
0079     bool                     showToolTip;
0080 
0081     qlonglong                scrollToItemId;
0082 
0083     QTimer*                  delayedEnterTimer;
0084 
0085     QMouseEvent*             currentMouseEvent;
0086 };
0087 
0088 ShowfotoCategorizedView::ShowfotoCategorizedView(QWidget* const parent)
0089     : ItemViewCategorized(parent),
0090       d                  (new Private)
0091 {
0092     setToolTip(new ShowfotoItemViewToolTip(this));
0093 
0094     LoadingCacheInterface::connectToSignalFileChanged(this,
0095             SLOT(slotFileChanged(QString)));
0096 
0097     d->delayedEnterTimer = new QTimer(this);
0098     d->delayedEnterTimer->setInterval(10);
0099     d->delayedEnterTimer->setSingleShot(false);
0100 
0101     connect(d->delayedEnterTimer, &QTimer::timeout,
0102             this, &ShowfotoCategorizedView::slotDelayedEnter);
0103 }
0104 
0105 ShowfotoCategorizedView::~ShowfotoCategorizedView()
0106 {
0107     d->delegate->removeAllOverlays();
0108     delete d;
0109 }
0110 
0111 void ShowfotoCategorizedView::setModels(ShowfotoItemModel* model, ShowfotoSortFilterModel* filterModel)
0112 {
0113     if (d->delegate)
0114     {
0115         d->delegate->setAllOverlaysActive(false);
0116     }
0117 
0118     if (d->filterModel)
0119     {
0120         disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()),
0121                    this, SLOT(layoutAboutToBeChanged()));
0122 
0123         disconnect(d->filterModel, SIGNAL(layoutChanged()),
0124                    this, SLOT(layoutWasChanged()));
0125     }
0126 
0127     d->model       = model;
0128     d->filterModel = filterModel;
0129 
0130     setModel(d->filterModel);
0131 
0132     connect(d->filterModel, &ShowfotoSortFilterModel::layoutAboutToBeChanged,
0133             this, &ShowfotoCategorizedView::layoutAboutToBeChanged);
0134 
0135     connect(d->filterModel, &ShowfotoSortFilterModel::layoutChanged,
0136             this, &ShowfotoCategorizedView::layoutWasChanged, Qt::QueuedConnection);
0137 
0138     Q_EMIT modelChanged();
0139 
0140     if (d->delegate)
0141     {
0142         d->delegate->setAllOverlaysActive(true);
0143     }
0144 }
0145 
0146 ShowfotoItemModel* ShowfotoCategorizedView::showfotoItemModel() const
0147 {
0148     return d->model;
0149 }
0150 
0151 ShowfotoSortFilterModel* ShowfotoCategorizedView::showfotoSortFilterModel() const
0152 {
0153     return d->filterModel;
0154 }
0155 
0156 ShowfotoFilterModel* ShowfotoCategorizedView::showfotoFilterModel() const
0157 {
0158     return d->filterModel->showfotoFilterModel();
0159 }
0160 
0161 ShowfotoThumbnailModel* ShowfotoCategorizedView::showfotoThumbnailModel() const
0162 {
0163     return qobject_cast<ShowfotoThumbnailModel*>(d->model);
0164 }
0165 
0166 QSortFilterProxyModel* ShowfotoCategorizedView::filterModel() const
0167 {
0168     return d->filterModel;
0169 }
0170 
0171 ShowfotoDelegate* ShowfotoCategorizedView::delegate() const
0172 {
0173     return d->delegate;
0174 }
0175 
0176 void ShowfotoCategorizedView::setItemDelegate(ShowfotoDelegate* delegate)
0177 {
0178     ThumbnailSize oldSize               = thumbnailSize();
0179     ShowfotoDelegate* const oldDelegate = d->delegate;
0180 
0181     if (oldDelegate)
0182     {
0183         hideIndexNotification();
0184         d->delegate->setAllOverlaysActive(false);
0185         d->delegate->setViewOnAllOverlays(nullptr);
0186 
0187         // Note: Be precise, no wildcard disconnect!
0188 
0189         disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)),
0190                    this, SLOT(showIndexNotification(QModelIndex,QString)));
0191 
0192         disconnect(d->delegate, SIGNAL(hideNotification()),
0193                    this, SLOT(hideIndexNotification()));
0194     }
0195 
0196     d->delegate = delegate;
0197     d->delegate->setThumbnailSize(oldSize);
0198 
0199     if (oldDelegate)
0200     {
0201         d->delegate->setSpacing(oldDelegate->spacing());
0202     }
0203 
0204     ItemViewCategorized::setItemDelegate(d->delegate);
0205     updateDelegateSizes();
0206 
0207     d->delegate->setViewOnAllOverlays(this);
0208     d->delegate->setAllOverlaysActive(true);
0209 
0210     connect(d->delegate, &ShowfotoDelegate::requestNotification,
0211             this, &ShowfotoCategorizedView::showIndexNotification);
0212 
0213     connect(d->delegate, &ShowfotoDelegate::hideNotification,
0214             this, &ShowfotoCategorizedView::hideIndexNotification);
0215 }
0216 
0217 ShowfotoItemInfo ShowfotoCategorizedView::currentInfo() const
0218 {
0219     return d->filterModel->showfotoItemInfo(currentIndex());
0220 }
0221 
0222 QUrl ShowfotoCategorizedView::currentUrl() const
0223 {
0224     return currentInfo().url;
0225 }
0226 
0227 QList<ShowfotoItemInfo> ShowfotoCategorizedView::selectedShowfotoItemInfos() const
0228 {
0229     return d->filterModel->showfotoItemInfos(selectedIndexes());
0230 }
0231 
0232 QList<ShowfotoItemInfo> ShowfotoCategorizedView::selectedShowfotoItemInfosCurrentFirst() const
0233 {
0234     QList<QModelIndex> indexes = selectedIndexes();
0235     QModelIndex        current = currentIndex();
0236     QList<ShowfotoItemInfo> infos;
0237 
0238     Q_FOREACH (const QModelIndex& index, indexes)
0239     {
0240         ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index);
0241 
0242         if (index == current)
0243         {
0244             infos.prepend(info);
0245         }
0246         else
0247         {
0248             infos.append(info);
0249         }
0250     }
0251 
0252     return infos;
0253 }
0254 
0255 QList<ShowfotoItemInfo> ShowfotoCategorizedView::showfotoItemInfos() const
0256 {
0257     return d->filterModel->showfotoItemInfosSorted();
0258 }
0259 
0260 QList<QUrl> ShowfotoCategorizedView::urls() const
0261 {
0262     QList<ShowfotoItemInfo> infos = showfotoItemInfos();
0263     QList<QUrl>             urls;
0264 
0265     Q_FOREACH (const ShowfotoItemInfo& info, infos)
0266     {
0267         urls << info.url;
0268     }
0269 
0270     return urls;
0271 }
0272 
0273 QList<QUrl> ShowfotoCategorizedView::selectedUrls() const
0274 {
0275     QList<ShowfotoItemInfo> infos = selectedShowfotoItemInfos();
0276     QList<QUrl>             urls;
0277 
0278     Q_FOREACH (const ShowfotoItemInfo& info, infos)
0279     {
0280         urls << info.url;
0281     }
0282 
0283     return urls;
0284 }
0285 
0286 void ShowfotoCategorizedView::toIndex(const QUrl& url)
0287 {
0288     ItemViewCategorized::toIndex(d->filterModel->indexForUrl(url));
0289 }
0290 
0291 ShowfotoItemInfo ShowfotoCategorizedView::nextInOrder(const ShowfotoItemInfo& startingPoint, int nth)
0292 {
0293     QModelIndex index = d->filterModel->indexForShowfotoItemInfo(startingPoint);
0294 
0295     if (!index.isValid())
0296     {
0297         return ShowfotoItemInfo();
0298     }
0299 
0300     return d->filterModel->showfotoItemInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex()));
0301 }
0302 
0303 QModelIndex ShowfotoCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const
0304 {
0305     QModelIndex hint      = ItemViewCategorized::nextIndexHint(anchor, removed);
0306     ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(anchor);
0307 
0308     //qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForShowfotoItemInfo(info);
0309 
0310     // Fixes a special case of multiple entries for the same image.
0311     // If one is removed, any entry of the same image shall be preferred.
0312 
0313     if (d->model->indexesForShowfotoItemInfo(info).size() > 1)
0314     {
0315         // The hint is for a different info, but we may have a hint for the same info
0316 
0317         if (info != d->filterModel->showfotoItemInfo(hint))
0318         {
0319             int minDiff                                   = d->filterModel->rowCount();
0320             QList<QModelIndex> indexesForShowfotoItemInfo = d->filterModel->mapListFromSource(d->model->indexesForShowfotoItemInfo(info));
0321 
0322             Q_FOREACH (const QModelIndex& index, indexesForShowfotoItemInfo)
0323             {
0324                 if ((index == anchor) || !index.isValid() || removed.contains(index))
0325                 {
0326                     continue;
0327                 }
0328 
0329                 int distance = qAbs(index.row() - anchor.row());
0330 
0331                 if (distance < minDiff)
0332                 {
0333                     minDiff = distance;
0334                     hint    = index;
0335 
0336                     //qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor;
0337                 }
0338             }
0339         }
0340     }
0341 
0342     return hint;
0343 }
0344 
0345 ThumbnailSize ShowfotoCategorizedView::thumbnailSize() const
0346 {
0347 /*
0348     ShowfotoThumbnailModel *thumbModel = ShowfotoThumbnailModel();
0349 
0350     if (thumbModel)
0351     {
0352         return thumbModel->thumbnailSize();
0353     }
0354 */
0355     if (d->delegate)
0356     {
0357         return d->delegate->thumbnailSize();
0358     }
0359 
0360     return ThumbnailSize();
0361 }
0362 
0363 void ShowfotoCategorizedView::setThumbnailSize(int size)
0364 {
0365     setThumbnailSize(ThumbnailSize(size));
0366 }
0367 
0368 void ShowfotoCategorizedView::setThumbnailSize(const ThumbnailSize& s)
0369 {
0370     // we abuse this pair of method calls to restore scroll position
0371 
0372     layoutAboutToBeChanged();
0373     d->delegate->setThumbnailSize(s);
0374     layoutWasChanged();
0375 }
0376 
0377 void ShowfotoCategorizedView::setCurrentWhenAvailable(qlonglong showfotoItemId)
0378 {
0379     d->scrollToItemId = showfotoItemId;
0380 }
0381 
0382 void ShowfotoCategorizedView::setCurrentUrl(const QUrl& url)
0383 {
0384     if (url.isEmpty())
0385     {
0386         clearSelection();
0387         setCurrentIndex(QModelIndex());
0388 
0389         return;
0390     }
0391 
0392     QModelIndex index = d->filterModel->indexForUrl(url);
0393 
0394     if (!index.isValid())
0395     {
0396         return;
0397     }
0398 
0399     clearSelection();
0400     setCurrentIndex(index);
0401 }
0402 
0403 void ShowfotoCategorizedView::setCurrentInfo(const ShowfotoItemInfo& info)
0404 {
0405     QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info);
0406     clearSelection();
0407     setCurrentIndex(index);
0408 }
0409 
0410 void ShowfotoCategorizedView::setSelectedUrls(const QList<QUrl>& urlList)
0411 {
0412     QItemSelection mySelection;
0413 
0414     for (QList<QUrl>::const_iterator it = urlList.constBegin() ; it!=urlList.constEnd() ; ++it)
0415     {
0416         const QModelIndex index = d->filterModel->indexForUrl(*it);
0417 
0418         if (!index.isValid())
0419         {
0420             qCWarning(DIGIKAM_GENERAL_LOG) << "no QModelIndex found for" << *it;
0421         }
0422         else
0423         {
0424             // TODO: is there a better way?
0425 
0426             mySelection.select(index, index);
0427         }
0428     }
0429 
0430     clearSelection();
0431     selectionModel()->select(mySelection, QItemSelectionModel::Select);
0432 }
0433 
0434 void ShowfotoCategorizedView::setSelectedShowfotoItemInfos(const QList<ShowfotoItemInfo>& infos)
0435 {
0436     QItemSelection mySelection;
0437 
0438     Q_FOREACH (const ShowfotoItemInfo& info, infos)
0439     {
0440         QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info);
0441         mySelection.select(index, index);
0442     }
0443 
0444     selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect);
0445 }
0446 
0447 void ShowfotoCategorizedView::hintAt(const ShowfotoItemInfo& info)
0448 {
0449     if (info.isNull())
0450     {
0451         return;
0452     }
0453 
0454     QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info);
0455 
0456     if (!index.isValid())
0457     {
0458         return;
0459     }
0460 
0461     selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
0462     scrollTo(index);
0463 }
0464 
0465 void ShowfotoCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ShowfotoDelegate* delegate)
0466 {
0467     if (!delegate)
0468     {
0469         delegate = d->delegate;
0470     }
0471 
0472     delegate->installOverlay(overlay);
0473 
0474     if (delegate == d->delegate)
0475     {
0476         overlay->setView(this);
0477         overlay->setActive(true);
0478     }
0479 }
0480 
0481 void ShowfotoCategorizedView::removeOverlay(ItemDelegateOverlay* overlay)
0482 {
0483     ShowfotoDelegate* const delegate = dynamic_cast<ShowfotoDelegate*>(overlay->delegate());
0484 
0485     if (delegate)
0486     {
0487         delegate->removeOverlay(overlay);
0488     }
0489 
0490     overlay->setView(nullptr);
0491 }
0492 
0493 void ShowfotoCategorizedView::updateGeometries()
0494 {
0495     ItemViewCategorized::updateGeometries();
0496     d->delayedEnterTimer->start();
0497 }
0498 
0499 void ShowfotoCategorizedView::slotDelayedEnter()
0500 {
0501     // re-Q_EMIT entered() for index under mouse (after layout).
0502 
0503     QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos()));
0504 
0505     if (mouseIndex.isValid())
0506     {
0507         Q_EMIT DCategorizedView::entered(mouseIndex);
0508     }
0509 }
0510 
0511 void ShowfotoCategorizedView::slotFileChanged(const QString& filePath)
0512 {
0513     QModelIndex index = d->filterModel->indexForUrl(QUrl::fromLocalFile(filePath));
0514 
0515     if (index.isValid())
0516     {
0517         update(index);
0518     }
0519 }
0520 
0521 void ShowfotoCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers)
0522 {
0523     ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index);
0524 
0525     if (!info.isNull())
0526     {
0527         activated(info, modifiers);
0528         Q_EMIT showfotoItemInfoActivated(info);
0529     }
0530 }
0531 
0532 void ShowfotoCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous)
0533 {
0534     ItemViewCategorized::currentChanged(index, previous);
0535 
0536     Q_EMIT currentChanged(d->filterModel->showfotoItemInfo(index));
0537 }
0538 
0539 void ShowfotoCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems)
0540 {
0541     ItemViewCategorized::selectionChanged(selectedItems, deselectedItems);
0542 
0543     if (!selectedItems.isEmpty())
0544     {
0545         Q_EMIT selected(d->filterModel->showfotoItemInfos(selectedItems.indexes()));
0546     }
0547 
0548     if (!deselectedItems.isEmpty())
0549     {
0550         Q_EMIT deselected(d->filterModel->showfotoItemInfos(deselectedItems.indexes()));
0551     }
0552 }
0553 
0554 void ShowfotoCategorizedView::activated(const ShowfotoItemInfo&, Qt::KeyboardModifiers)
0555 {
0556     // implemented in subclass
0557 }
0558 
0559 void ShowfotoCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index)
0560 {
0561     ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index);
0562     showContextMenuOnInfo(event, info);
0563 }
0564 
0565 void ShowfotoCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const ShowfotoItemInfo&)
0566 {
0567     // implemented in subclass
0568 }
0569 
0570 void ShowfotoCategorizedView::paintEvent(QPaintEvent* e)
0571 {
0572     ItemViewCategorized::paintEvent(e);
0573 }
0574 
0575 QItemSelectionModel* ShowfotoCategorizedView::getSelectionModel() const
0576 {
0577     return selectionModel();
0578 }
0579 
0580 AbstractItemDragDropHandler* ShowfotoCategorizedView::dragDropHandler() const
0581 {
0582     return d->model->dragDropHandler();
0583 }
0584 
0585 ShowfotoItemInfo ShowfotoCategorizedView::previousInfo(const ShowfotoItemInfo& info)
0586 {
0587     return nextInOrder(info, -1);
0588 }
0589 
0590 ShowfotoItemInfo ShowfotoCategorizedView::nextInfo(const ShowfotoItemInfo& info)
0591 {
0592     return nextInOrder(info, 1);
0593 }
0594 
0595 } // namespace Showfoto
0596 
0597 #include "showfotocategorizedview.moc"
0598 
0599 #include "moc_showfotocategorizedview.cpp"