File indexing completed on 2024-04-21 11:34:11

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
0004     SPDX-FileCopyrightText: 2008 Kevin Ottens <ervin@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kwidgetitemdelegatepool_p.h"
0010 
0011 #include <QAbstractItemView>
0012 #include <QAbstractProxyModel>
0013 #include <QApplication>
0014 #include <QHash>
0015 #include <QInputEvent>
0016 #include <QList>
0017 #include <QMetaMethod>
0018 #include <QWidget>
0019 #include <qobjectdefs.h>
0020 
0021 #include "kwidgetitemdelegate.h"
0022 #include "kwidgetitemdelegate_p.h"
0023 #include <kitemviews_debug.h>
0024 
0025 /**
0026   Private class that helps to provide binary compatibility between releases.
0027   @internal
0028 */
0029 //@cond PRIVATE
0030 class KWidgetItemDelegateEventListener : public QObject
0031 {
0032 public:
0033     KWidgetItemDelegateEventListener(KWidgetItemDelegatePoolPrivate *poolPrivate, QObject *parent = nullptr)
0034         : QObject(parent)
0035         , poolPrivate(poolPrivate)
0036     {
0037     }
0038 
0039     bool eventFilter(QObject *watched, QEvent *event) override;
0040 
0041 private:
0042     KWidgetItemDelegatePoolPrivate *const poolPrivate;
0043 };
0044 
0045 KWidgetItemDelegatePoolPrivate::KWidgetItemDelegatePoolPrivate(KWidgetItemDelegate *d)
0046     : delegate(d)
0047     , eventListener(new KWidgetItemDelegateEventListener(this))
0048 {
0049 }
0050 
0051 KWidgetItemDelegatePool::KWidgetItemDelegatePool(KWidgetItemDelegate *delegate)
0052     : d(new KWidgetItemDelegatePoolPrivate(delegate))
0053 {
0054 }
0055 
0056 KWidgetItemDelegatePool::~KWidgetItemDelegatePool()
0057 {
0058     delete d->eventListener;
0059     delete d;
0060 }
0061 
0062 QList<QWidget *>
0063 KWidgetItemDelegatePool::findWidgets(const QPersistentModelIndex &idx, const QStyleOptionViewItem &option, UpdateWidgetsEnum updateWidgets) const
0064 {
0065     QList<QWidget *> result;
0066 
0067     if (!idx.isValid()) {
0068         return result;
0069     }
0070 
0071     QModelIndex index;
0072     if (const QAbstractProxyModel *proxyModel = qobject_cast<const QAbstractProxyModel *>(idx.model())) {
0073         index = proxyModel->mapToSource(idx);
0074     } else {
0075         index = idx;
0076     }
0077 
0078     if (!index.isValid()) {
0079         return result;
0080     }
0081 
0082     if (d->usedWidgets.contains(index)) {
0083         result = d->usedWidgets[index];
0084     } else {
0085         result = d->delegate->createItemWidgets(index);
0086         d->allocatedWidgets << result;
0087         d->usedWidgets[index] = result;
0088         for (QWidget *widget : std::as_const(result)) {
0089             d->widgetInIndex[widget] = index;
0090             widget->setParent(d->delegate->d->itemView->viewport());
0091             widget->installEventFilter(d->eventListener);
0092             widget->setVisible(true);
0093         }
0094     }
0095 
0096     if (updateWidgets == UpdateWidgets) {
0097         for (QWidget *widget : std::as_const(result)) {
0098             widget->setVisible(true);
0099         }
0100 
0101         d->delegate->updateItemWidgets(result, option, idx);
0102 
0103         for (QWidget *widget : std::as_const(result)) {
0104             widget->move(widget->x() + option.rect.left(), widget->y() + option.rect.top());
0105         }
0106     }
0107 
0108     return result;
0109 }
0110 
0111 QList<QWidget *> KWidgetItemDelegatePool::invalidIndexesWidgets() const
0112 {
0113     QList<QWidget *> result;
0114     QHashIterator<QWidget *, QPersistentModelIndex> i(d->widgetInIndex);
0115     while (i.hasNext()) {
0116         i.next();
0117         const QAbstractProxyModel *proxyModel = qobject_cast<const QAbstractProxyModel *>(d->delegate->d->model);
0118         QModelIndex index;
0119         if (proxyModel) {
0120             index = proxyModel->mapFromSource(i.value());
0121         } else {
0122             index = i.value();
0123         }
0124         if (!index.isValid()) {
0125             result << i.key();
0126         }
0127     }
0128     return result;
0129 }
0130 
0131 void KWidgetItemDelegatePool::fullClear()
0132 {
0133     d->clearing = true;
0134     qDeleteAll(d->widgetInIndex.keys());
0135     d->clearing = false;
0136     d->allocatedWidgets.clear();
0137     d->usedWidgets.clear();
0138     d->widgetInIndex.clear();
0139 }
0140 
0141 bool KWidgetItemDelegateEventListener::eventFilter(QObject *watched, QEvent *event)
0142 {
0143     QWidget *widget = static_cast<QWidget *>(watched);
0144 
0145     if (event->type() == QEvent::Destroy && !poolPrivate->clearing) {
0146         qCWarning(KITEMVIEWS_LOG) << "User of KWidgetItemDelegate should not delete widgets created by createItemWidgets!";
0147         // assume the application has kept a list of widgets and tries to delete them manually
0148         // they have been reparented to the view in any case, so no leaking occurs
0149         poolPrivate->widgetInIndex.remove(widget);
0150         QWidget *viewport = poolPrivate->delegate->d->itemView->viewport();
0151         QApplication::sendEvent(viewport, event);
0152     }
0153     if (dynamic_cast<QInputEvent *>(event) && !poolPrivate->delegate->blockedEventTypes(widget).contains(event->type())) {
0154         QWidget *viewport = poolPrivate->delegate->d->itemView->viewport();
0155         switch (event->type()) {
0156         case QEvent::MouseMove:
0157         case QEvent::MouseButtonPress:
0158         case QEvent::MouseButtonRelease:
0159         case QEvent::MouseButtonDblClick: {
0160             QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
0161             QMouseEvent evt(event->type(),
0162                             viewport->mapFromGlobal(mouseEvent->globalPos()),
0163                             mouseEvent->button(),
0164                             mouseEvent->buttons(),
0165                             mouseEvent->modifiers());
0166             QApplication::sendEvent(viewport, &evt);
0167         } break;
0168         case QEvent::Wheel: {
0169             QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
0170             QWheelEvent evt(viewport->mapFromGlobal(wheelEvent->position().toPoint()),
0171                             viewport->mapFromGlobal(wheelEvent->globalPosition().toPoint()),
0172                             wheelEvent->pixelDelta(),
0173                             wheelEvent->angleDelta(),
0174                             wheelEvent->buttons(),
0175                             wheelEvent->modifiers(),
0176                             wheelEvent->phase(),
0177                             wheelEvent->inverted(),
0178                             wheelEvent->source());
0179             QApplication::sendEvent(viewport, &evt);
0180         } break;
0181         case QEvent::TabletMove:
0182         case QEvent::TabletPress:
0183         case QEvent::TabletRelease:
0184         case QEvent::TabletEnterProximity:
0185         case QEvent::TabletLeaveProximity: {
0186             QTabletEvent *tabletEvent = static_cast<QTabletEvent *>(event);
0187 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0188             QTabletEvent evt(event->type(),
0189                              tabletEvent->pointingDevice(),
0190                              viewport->mapFromGlobal(tabletEvent->globalPosition()),
0191                              tabletEvent->globalPosition(),
0192                              tabletEvent->pressure(),
0193                              tabletEvent->xTilt(),
0194                              tabletEvent->yTilt(),
0195                              tabletEvent->tangentialPressure(),
0196                              tabletEvent->rotation(),
0197                              tabletEvent->z(),
0198                              tabletEvent->modifiers(),
0199                              tabletEvent->button(),
0200                              tabletEvent->buttons());
0201 #else
0202             QTabletEvent evt(event->type(),
0203                              QPointF(viewport->mapFromGlobal(tabletEvent->globalPos())),
0204                              tabletEvent->globalPosF(),
0205                              tabletEvent->deviceType(),
0206                              tabletEvent->pointerType(),
0207                              tabletEvent->pressure(),
0208                              tabletEvent->xTilt(),
0209                              tabletEvent->yTilt(),
0210                              tabletEvent->tangentialPressure(),
0211                              tabletEvent->rotation(),
0212                              tabletEvent->z(),
0213                              tabletEvent->modifiers(),
0214                              tabletEvent->uniqueId(),
0215                              tabletEvent->button(),
0216                              tabletEvent->buttons());
0217 #endif
0218             QApplication::sendEvent(viewport, &evt);
0219             break;
0220         }
0221         default:
0222             QApplication::sendEvent(viewport, event);
0223             break;
0224         }
0225     }
0226 
0227     return QObject::eventFilter(watched, event);
0228 }
0229 //@endcond