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"