File indexing completed on 2025-04-27 03:58:28
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-04-29 0007 * Description : Qt item view for images - delegate additions 0008 * 0009 * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "itemdelegateoverlay.h" 0016 0017 // Qt includes 0018 0019 #include <QApplication> 0020 #include <QEvent> 0021 #include <QMouseEvent> 0022 0023 // KDE includes 0024 0025 #include <klocalizedstring.h> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "itemviewdelegate.h" 0031 #include "itemviewhoverbutton.h" 0032 0033 namespace Digikam 0034 { 0035 0036 ItemDelegateOverlay::ItemDelegateOverlay(QObject* const parent) 0037 : QObject (parent), 0038 m_view (nullptr), 0039 m_delegate(nullptr) 0040 { 0041 } 0042 0043 ItemDelegateOverlay::~ItemDelegateOverlay() 0044 { 0045 } 0046 0047 void ItemDelegateOverlay::setActive(bool) 0048 { 0049 } 0050 0051 void ItemDelegateOverlay::visualChange() 0052 { 0053 } 0054 0055 void ItemDelegateOverlay::mouseMoved(QMouseEvent*, const QRect&, const QModelIndex&) 0056 { 0057 } 0058 0059 void ItemDelegateOverlay::paint(QPainter*, const QStyleOptionViewItem&, const QModelIndex&) 0060 { 0061 } 0062 0063 void ItemDelegateOverlay::setView(QAbstractItemView* view) 0064 { 0065 if (m_view) 0066 { 0067 disconnect(this, SIGNAL(update(QModelIndex)), 0068 m_view, SLOT(update(QModelIndex))); 0069 } 0070 0071 m_view = view; 0072 0073 if (m_view) 0074 { 0075 connect(this, SIGNAL(update(QModelIndex)), 0076 m_view, SLOT(update(QModelIndex))); 0077 } 0078 } 0079 0080 QAbstractItemView* ItemDelegateOverlay::view() const 0081 { 0082 return m_view; 0083 } 0084 0085 void ItemDelegateOverlay::setDelegate(QAbstractItemDelegate* delegate) 0086 { 0087 if (m_delegate) 0088 { 0089 disconnect(m_delegate, SIGNAL(visualChange()), 0090 this, SLOT(visualChange())); 0091 } 0092 0093 m_delegate = delegate; 0094 0095 if (m_delegate) 0096 { 0097 connect(m_delegate, SIGNAL(visualChange()), 0098 this, SLOT(visualChange())); 0099 } 0100 } 0101 0102 QAbstractItemDelegate* ItemDelegateOverlay::delegate() const 0103 { 0104 return m_delegate; 0105 } 0106 0107 bool ItemDelegateOverlay::affectsMultiple(const QModelIndex& index) const 0108 { 0109 // note how selectionModel->selectedIndexes().contains() can scale badly 0110 0111 QItemSelectionModel* const selectionModel = view()->selectionModel(); 0112 0113 if (!selectionModel->hasSelection()) 0114 { 0115 return false; 0116 } 0117 0118 if (!selectionModel->isSelected(index)) 0119 { 0120 return false; 0121 } 0122 0123 return viewHasMultiSelection(); 0124 } 0125 0126 bool ItemDelegateOverlay::viewHasMultiSelection() const 0127 { 0128 QItemSelection selection = view()->selectionModel()->selection(); 0129 0130 if (selection.size() > 1) 0131 { 0132 return true; 0133 } 0134 0135 return (selection.indexes().size() > 1); 0136 } 0137 0138 QList<QModelIndex> ItemDelegateOverlay::affectedIndexes(const QModelIndex& index) const 0139 { 0140 if (!affectsMultiple(index)) 0141 { 0142 return QList<QModelIndex>() << index; 0143 } 0144 else 0145 { 0146 return view()->selectionModel()->selectedIndexes(); 0147 } 0148 } 0149 0150 int ItemDelegateOverlay::numberOfAffectedIndexes(const QModelIndex& index) const 0151 { 0152 if (!affectsMultiple(index)) 0153 { 0154 return 1; 0155 } 0156 0157 // scales better than selectedIndexes().count() 0158 0159 int count = 0; 0160 0161 Q_FOREACH (const QItemSelectionRange& range, view()->selectionModel()->selection()) 0162 { 0163 // cppcheck-suppress useStlAlgorithm 0164 count += range.height(); 0165 } 0166 0167 return count; 0168 } 0169 0170 // -------------------------------------------------------------------------------------------- 0171 0172 AbstractWidgetDelegateOverlay::AbstractWidgetDelegateOverlay(QObject* const parent) 0173 : ItemDelegateOverlay (parent), 0174 m_widget (nullptr), 0175 m_mouseButtonPressedOnWidget(false) 0176 { 0177 } 0178 0179 void AbstractWidgetDelegateOverlay::setActive(bool active) 0180 { 0181 if (active) 0182 { 0183 if (m_widget) 0184 { 0185 delete m_widget; 0186 m_widget = nullptr; 0187 } 0188 0189 m_widget = createWidget(); 0190 0191 m_widget->setFocusPolicy(Qt::NoFocus); 0192 m_widget->hide(); // hide per default 0193 0194 m_view->viewport()->installEventFilter(this); 0195 m_widget->installEventFilter(this); 0196 0197 if (view()->model()) 0198 { 0199 connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), 0200 this, SLOT(slotRowsRemoved(QModelIndex,int,int))); 0201 0202 connect(m_view->model(), SIGNAL(layoutChanged()), 0203 this, SLOT(slotLayoutChanged())); 0204 0205 connect(m_view->model(), SIGNAL(modelReset()), 0206 this, SLOT(slotReset())); 0207 } 0208 0209 connect(m_view, SIGNAL(entered(QModelIndex)), 0210 this, SLOT(slotEntered(QModelIndex))); 0211 0212 connect(m_view, SIGNAL(viewportEntered()), 0213 this, SLOT(slotViewportEntered())); 0214 } 0215 else 0216 { 0217 delete m_widget; 0218 m_widget = nullptr; 0219 0220 if (m_view) 0221 { 0222 m_view->viewport()->removeEventFilter(this); 0223 0224 if (view()->model()) 0225 { 0226 disconnect(m_view->model(), nullptr, this, nullptr); 0227 } 0228 0229 disconnect(m_view, SIGNAL(entered(QModelIndex)), 0230 this, SLOT(slotEntered(QModelIndex))); 0231 0232 disconnect(m_view, SIGNAL(viewportEntered()), 0233 this, SLOT(slotViewportEntered())); 0234 } 0235 } 0236 } 0237 0238 void AbstractWidgetDelegateOverlay::hide() 0239 { 0240 if (m_widget) 0241 { 0242 m_widget->hide(); 0243 } 0244 } 0245 0246 QWidget* AbstractWidgetDelegateOverlay::parentWidget() const 0247 { 0248 return m_view->viewport(); 0249 } 0250 0251 void AbstractWidgetDelegateOverlay::slotReset() 0252 { 0253 hide(); 0254 } 0255 0256 void AbstractWidgetDelegateOverlay::slotEntered(const QModelIndex& index) 0257 { 0258 hide(); 0259 0260 if (!checkIndexOnEnter(index)) 0261 { 0262 return; 0263 } 0264 0265 m_widget->show(); 0266 } 0267 0268 bool AbstractWidgetDelegateOverlay::checkIndexOnEnter(const QModelIndex& index) const 0269 { 0270 if (!index.isValid()) 0271 { 0272 return false; 0273 } 0274 0275 if (QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) 0276 { 0277 return false; 0278 } 0279 0280 if (!checkIndex(index)) 0281 { 0282 return false; 0283 } 0284 0285 return true; 0286 } 0287 0288 bool AbstractWidgetDelegateOverlay::checkIndex(const QModelIndex& index) const 0289 { 0290 Q_UNUSED(index); 0291 0292 return true; 0293 } 0294 0295 void AbstractWidgetDelegateOverlay::slotViewportEntered() 0296 { 0297 hide(); 0298 } 0299 0300 void AbstractWidgetDelegateOverlay::slotRowsRemoved(const QModelIndex&, int, int) 0301 { 0302 hide(); 0303 } 0304 0305 void AbstractWidgetDelegateOverlay::slotLayoutChanged() 0306 { 0307 hide(); 0308 } 0309 0310 void AbstractWidgetDelegateOverlay::viewportLeaveEvent(QObject*, QEvent*) 0311 { 0312 hide(); 0313 } 0314 0315 void AbstractWidgetDelegateOverlay::widgetEnterEvent() 0316 { 0317 } 0318 0319 void AbstractWidgetDelegateOverlay::widgetLeaveEvent() 0320 { 0321 } 0322 0323 void AbstractWidgetDelegateOverlay::widgetEnterNotifyMultiple(const QModelIndex& index) 0324 { 0325 if (index.isValid() && affectsMultiple(index)) 0326 { 0327 Q_EMIT requestNotification(index, notifyMultipleMessage(index, numberOfAffectedIndexes(index))); 0328 } 0329 } 0330 0331 void AbstractWidgetDelegateOverlay::widgetLeaveNotifyMultiple() 0332 { 0333 Q_EMIT hideNotification(); 0334 } 0335 0336 QString AbstractWidgetDelegateOverlay::notifyMultipleMessage(const QModelIndex&, int number) 0337 { 0338 return i18ncp("@info: item overlay", 0339 "Applying operation to\nthe selected picture", 0340 "Applying operation to \n\"%1\" selected pictures", 0341 number); 0342 } 0343 0344 bool AbstractWidgetDelegateOverlay::eventFilter(QObject* obj, QEvent* event) 0345 { 0346 if (m_widget && (obj == m_widget->parent())) // events on view's viewport 0347 { 0348 switch (event->type()) 0349 { 0350 case QEvent::Leave: 0351 { 0352 viewportLeaveEvent(obj, event); 0353 break; 0354 } 0355 0356 case QEvent::MouseMove: 0357 { 0358 if (m_mouseButtonPressedOnWidget) 0359 { 0360 // Don't forward mouse move events to the viewport, 0361 // otherwise a rubberband selection will be shown when 0362 // clicking on the selection toggle and moving the mouse 0363 // above the viewport. 0364 return true; 0365 } 0366 0367 break; 0368 } 0369 0370 case QEvent::MouseButtonRelease: 0371 { 0372 m_mouseButtonPressedOnWidget = false; 0373 break; 0374 } 0375 0376 default: 0377 { 0378 break; 0379 } 0380 } 0381 } 0382 else if (obj == m_widget) 0383 { 0384 switch (event->type()) 0385 { 0386 case QEvent::MouseButtonPress: 0387 { 0388 if (static_cast<QMouseEvent*>(event)->buttons() & Qt::LeftButton) 0389 { 0390 m_mouseButtonPressedOnWidget = true; 0391 } 0392 0393 break; 0394 } 0395 0396 case QEvent::MouseButtonRelease: 0397 { 0398 m_mouseButtonPressedOnWidget = false; 0399 break; 0400 } 0401 0402 case QEvent::Enter: 0403 { 0404 widgetEnterEvent(); 0405 break; 0406 } 0407 0408 case QEvent::Leave: 0409 { 0410 widgetLeaveEvent(); 0411 break; 0412 } 0413 0414 default: 0415 { 0416 break; 0417 } 0418 } 0419 } 0420 0421 return ItemDelegateOverlay::eventFilter(obj, event); 0422 } 0423 0424 // ------------------------------------------------------------------------------------------ 0425 0426 HoverButtonDelegateOverlay::HoverButtonDelegateOverlay(QObject* const parent) 0427 : AbstractWidgetDelegateOverlay(parent) 0428 { 0429 } 0430 0431 ItemViewHoverButton* HoverButtonDelegateOverlay::button() const 0432 { 0433 return static_cast<ItemViewHoverButton*>(m_widget); 0434 } 0435 0436 void HoverButtonDelegateOverlay::setActive(bool active) 0437 { 0438 AbstractWidgetDelegateOverlay::setActive(active); 0439 0440 if (active) 0441 { 0442 button()->initIcon(); 0443 } 0444 } 0445 0446 QWidget* HoverButtonDelegateOverlay::createWidget() 0447 { 0448 return createButton(); 0449 } 0450 0451 void HoverButtonDelegateOverlay::visualChange() 0452 { 0453 if (m_widget && m_widget->isVisible()) 0454 { 0455 updateButton(button()->index()); 0456 } 0457 } 0458 0459 void HoverButtonDelegateOverlay::slotReset() 0460 { 0461 AbstractWidgetDelegateOverlay::slotReset(); 0462 0463 button()->reset(); 0464 } 0465 0466 void HoverButtonDelegateOverlay::slotEntered(const QModelIndex& index) 0467 { 0468 AbstractWidgetDelegateOverlay::slotEntered(index); 0469 0470 if (index.isValid() && checkIndex(index)) 0471 { 0472 button()->setIndex(index); 0473 updateButton(index); 0474 } 0475 else 0476 { 0477 button()->setIndex(index); 0478 } 0479 } 0480 0481 // ----------------------------------------------------------------------------------- 0482 0483 class Q_DECL_HIDDEN PersistentWidgetDelegateOverlay::Private 0484 { 0485 public: 0486 0487 explicit Private() 0488 : persistent (false), 0489 restoreFocus(false) 0490 { 0491 } 0492 0493 bool persistent; 0494 bool restoreFocus; 0495 0496 QPersistentModelIndex index; 0497 QPersistentModelIndex enteredIndex; 0498 }; 0499 0500 PersistentWidgetDelegateOverlay::PersistentWidgetDelegateOverlay(QObject* const parent) 0501 : AbstractWidgetDelegateOverlay(parent), 0502 d (new Private) 0503 { 0504 } 0505 0506 PersistentWidgetDelegateOverlay::~PersistentWidgetDelegateOverlay() 0507 { 0508 delete d; 0509 } 0510 0511 QModelIndex PersistentWidgetDelegateOverlay::index() const 0512 { 0513 return d->index; 0514 } 0515 0516 void PersistentWidgetDelegateOverlay::setActive(bool active) 0517 { 0518 d->persistent = false; 0519 0520 AbstractWidgetDelegateOverlay::setActive(active); 0521 0522 if (active) 0523 { 0524 connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), 0525 this, SLOT(leavePersistentMode())); 0526 0527 connect(m_view, SIGNAL(viewportClicked(const QMouseEvent*)), 0528 this, SLOT(leavePersistentMode())); 0529 } 0530 else 0531 { 0532 if (m_view) 0533 { 0534 disconnect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), 0535 this, SLOT(leavePersistentMode())); 0536 0537 disconnect(m_view, SIGNAL(viewportClicked(const QMouseEvent*)), 0538 this, SLOT(leavePersistentMode())); 0539 } 0540 } 0541 } 0542 0543 void PersistentWidgetDelegateOverlay::setPersistent(bool persistent) 0544 { 0545 if (d->persistent == persistent) 0546 { 0547 return; 0548 } 0549 0550 d->persistent = persistent; 0551 0552 if (d->persistent && d->index.isValid()) 0553 { 0554 showOnIndex(d->index); 0555 } 0556 else if (!d->persistent) 0557 { 0558 if (d->enteredIndex.isValid()) 0559 { 0560 slotEntered(d->enteredIndex); 0561 } 0562 else 0563 { 0564 hide(); 0565 } 0566 } 0567 } 0568 0569 void PersistentWidgetDelegateOverlay::enterPersistentMode() 0570 { 0571 setPersistent(true); 0572 } 0573 0574 void PersistentWidgetDelegateOverlay::leavePersistentMode() 0575 { 0576 setPersistent(false); 0577 } 0578 0579 bool PersistentWidgetDelegateOverlay::isPersistent() const 0580 { 0581 return d->persistent; 0582 } 0583 0584 void PersistentWidgetDelegateOverlay::slotEntered(const QModelIndex& index) 0585 { 0586 d->enteredIndex = index; 0587 0588 if (d->persistent && m_widget->isVisible()) 0589 { 0590 return; 0591 } 0592 0593 hide(); 0594 0595 if (!checkIndexOnEnter(index)) 0596 { 0597 return; 0598 } 0599 0600 m_widget->show(); 0601 0602 showOnIndex(index); 0603 } 0604 0605 void PersistentWidgetDelegateOverlay::slotReset() 0606 { 0607 setPersistent(false); 0608 d->restoreFocus = false; 0609 AbstractWidgetDelegateOverlay::slotReset(); 0610 } 0611 0612 void PersistentWidgetDelegateOverlay::slotViewportEntered() 0613 { 0614 d->enteredIndex = QModelIndex(); 0615 0616 if (!d->persistent) 0617 { 0618 AbstractWidgetDelegateOverlay::slotViewportEntered(); 0619 } 0620 } 0621 0622 void PersistentWidgetDelegateOverlay::viewportLeaveEvent(QObject* obj, QEvent* event) 0623 { 0624 setPersistent(false); 0625 d->restoreFocus = false; 0626 AbstractWidgetDelegateOverlay::viewportLeaveEvent(obj, event); 0627 } 0628 0629 void PersistentWidgetDelegateOverlay::slotRowsRemoved(const QModelIndex& parent, int begin, int end) 0630 { 0631 AbstractWidgetDelegateOverlay::slotRowsRemoved(parent, begin, end); 0632 setPersistent(false); 0633 } 0634 0635 void PersistentWidgetDelegateOverlay::slotLayoutChanged() 0636 { 0637 AbstractWidgetDelegateOverlay::slotLayoutChanged(); 0638 setPersistent(false); 0639 } 0640 0641 void PersistentWidgetDelegateOverlay::hide() 0642 { 0643 if (!d->restoreFocus && m_widget->isVisible()) 0644 { 0645 QWidget* const f = QApplication::focusWidget(); 0646 d->restoreFocus = f && m_widget->isAncestorOf(f); 0647 } 0648 0649 AbstractWidgetDelegateOverlay::hide(); 0650 } 0651 0652 void PersistentWidgetDelegateOverlay::showOnIndex(const QModelIndex& index) 0653 { 0654 d->index = QPersistentModelIndex(index); 0655 restoreFocus(); 0656 } 0657 0658 void PersistentWidgetDelegateOverlay::storeFocus() 0659 { 0660 d->restoreFocus = true; 0661 } 0662 0663 void PersistentWidgetDelegateOverlay::restoreFocus() 0664 { 0665 if (d->restoreFocus) 0666 { 0667 setFocusOnWidget(); 0668 d->restoreFocus = false; 0669 } 0670 } 0671 0672 void PersistentWidgetDelegateOverlay::setFocusOnWidget() 0673 { 0674 m_widget->setFocus(); 0675 } 0676 0677 // ----------------------------------------------------------------------------------- 0678 0679 void ItemDelegateOverlayContainer::installOverlay(ItemDelegateOverlay* overlay) 0680 { 0681 if (!overlay->acceptsDelegate(asDelegate())) 0682 { 0683 qCDebug(DIGIKAM_WIDGETS_LOG) << "Cannot accept delegate" << asDelegate() 0684 << "for installing" << overlay; 0685 return; 0686 } 0687 0688 overlay->setDelegate(asDelegate()); 0689 m_overlays << overlay; 0690 0691 // let the view call setActive 0692 0693 QObject::connect(overlay, SIGNAL(destroyed(QObject*)), 0694 asDelegate(), SLOT(overlayDestroyed(QObject*))); 0695 0696 QObject::connect(overlay, SIGNAL(requestNotification(QModelIndex,QString)), 0697 asDelegate(), SIGNAL(requestNotification(QModelIndex,QString))); 0698 0699 QObject::connect(overlay, SIGNAL(hideNotification()), 0700 asDelegate(), SIGNAL(hideNotification())); 0701 } 0702 0703 QList<ItemDelegateOverlay*> ItemDelegateOverlayContainer::overlays() const 0704 { 0705 return m_overlays; 0706 } 0707 0708 void ItemDelegateOverlayContainer::removeOverlay(ItemDelegateOverlay* overlay) 0709 { 0710 overlay->setActive(false); 0711 overlay->setDelegate(nullptr); 0712 m_overlays.removeAll(overlay); 0713 QObject::disconnect(overlay, nullptr, asDelegate(), nullptr); 0714 } 0715 0716 void ItemDelegateOverlayContainer::setAllOverlaysActive(bool active) 0717 { 0718 Q_FOREACH (ItemDelegateOverlay* const overlay, m_overlays) 0719 { 0720 overlay->setActive(active); 0721 } 0722 } 0723 0724 void ItemDelegateOverlayContainer::setViewOnAllOverlays(QAbstractItemView* view) 0725 { 0726 Q_FOREACH (ItemDelegateOverlay* const overlay, m_overlays) 0727 { 0728 overlay->setView(view); 0729 } 0730 } 0731 0732 void ItemDelegateOverlayContainer::removeAllOverlays() 0733 { 0734 Q_FOREACH (ItemDelegateOverlay* const overlay, m_overlays) 0735 { 0736 overlay->setActive(false); 0737 overlay->setDelegate(nullptr); 0738 overlay->setView(nullptr); 0739 } 0740 0741 m_overlays.clear(); 0742 } 0743 0744 void ItemDelegateOverlayContainer::overlayDestroyed(QObject* o) 0745 { 0746 ItemDelegateOverlay* const overlay = qobject_cast<ItemDelegateOverlay*>(o); 0747 0748 if (overlay) 0749 { 0750 removeOverlay(overlay); 0751 } 0752 } 0753 0754 void ItemDelegateOverlayContainer::mouseMoved(QMouseEvent* e, 0755 const QRect& visualRect, 0756 const QModelIndex& index) 0757 { 0758 Q_FOREACH (ItemDelegateOverlay* const overlay, m_overlays) 0759 { 0760 overlay->mouseMoved(e, visualRect, index); 0761 } 0762 } 0763 0764 void ItemDelegateOverlayContainer::drawOverlays(QPainter* p, 0765 const QStyleOptionViewItem& option, 0766 const QModelIndex& index) const 0767 { 0768 Q_FOREACH (ItemDelegateOverlay* const overlay, m_overlays) 0769 { 0770 overlay->paint(p, option, index); 0771 } 0772 } 0773 0774 } // namespace Digikam 0775 0776 #include "moc_itemdelegateoverlay.cpp"