File indexing completed on 2025-02-09 04:25:21
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2007-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 "kwidgetitemdelegate.h" 0010 #include "kwidgetitemdelegate_p.h" 0011 0012 #include <QAbstractItemView> 0013 #include <QApplication> 0014 #include <QCursor> 0015 #include <QPainter> 0016 #include <QStyleOption> 0017 #include <QTimer> 0018 #include <QTreeView> 0019 0020 #include "kwidgetitemdelegatepool_p.h" 0021 0022 Q_DECLARE_METATYPE(QList<QEvent::Type>) 0023 0024 /** 0025 KWidgetItemDelegatePrivate class that helps to provide binary compatibility between releases. 0026 @internal 0027 */ 0028 //@cond PRIVATE 0029 KWidgetItemDelegatePrivate::KWidgetItemDelegatePrivate(KWidgetItemDelegate *q, QObject *parent) 0030 : QObject(parent) 0031 , widgetPool(new KWidgetItemDelegatePool(q)) 0032 , q(q) 0033 { 0034 } 0035 0036 KWidgetItemDelegatePrivate::~KWidgetItemDelegatePrivate() 0037 { 0038 if (!viewDestroyed) { 0039 widgetPool->fullClear(); 0040 } 0041 delete widgetPool; 0042 } 0043 0044 void KWidgetItemDelegatePrivate::_k_slotRowsInserted(const QModelIndex &parent, int start, int end) 0045 { 0046 Q_UNUSED(end); 0047 // We need to update the rows behind the inserted row as well because the widgets need to be 0048 // moved to their new position 0049 updateRowRange(parent, start, model->rowCount(parent), false); 0050 } 0051 0052 void KWidgetItemDelegatePrivate::_k_slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0053 { 0054 updateRowRange(parent, start, end, true); 0055 } 0056 0057 void KWidgetItemDelegatePrivate::_k_slotRowsRemoved(const QModelIndex &parent, int start, int end) 0058 { 0059 Q_UNUSED(end); 0060 // We need to update the rows that come behind the deleted rows because the widgets need to be 0061 // moved to the new position 0062 updateRowRange(parent, start, model->rowCount(parent), false); 0063 } 0064 0065 void KWidgetItemDelegatePrivate::_k_slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0066 { 0067 for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { 0068 for (int j = topLeft.column(); j <= bottomRight.column(); ++j) { 0069 const QModelIndex index = model->index(i, j, topLeft.parent()); 0070 widgetPool->findWidgets(index, optionView(index)); 0071 } 0072 } 0073 } 0074 0075 void KWidgetItemDelegatePrivate::_k_slotLayoutChanged() 0076 { 0077 const auto lst = widgetPool->invalidIndexesWidgets(); 0078 for (QWidget *widget : lst) { 0079 widget->setVisible(false); 0080 } 0081 QTimer::singleShot(0, this, SLOT(initializeModel())); 0082 } 0083 0084 void KWidgetItemDelegatePrivate::_k_slotModelReset() 0085 { 0086 widgetPool->fullClear(); 0087 QTimer::singleShot(0, this, SLOT(initializeModel())); 0088 } 0089 0090 void KWidgetItemDelegatePrivate::_k_slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) 0091 { 0092 const auto lstSelected = selected.indexes(); 0093 for (const QModelIndex &index : lstSelected) { 0094 widgetPool->findWidgets(index, optionView(index)); 0095 } 0096 const auto lstDeselected = deselected.indexes(); 0097 for (const QModelIndex &index : lstDeselected) { 0098 widgetPool->findWidgets(index, optionView(index)); 0099 } 0100 } 0101 0102 void KWidgetItemDelegatePrivate::updateRowRange(const QModelIndex &parent, int start, int end, bool isRemoving) 0103 { 0104 int i = start; 0105 while (i <= end) { 0106 for (int j = 0; j < model->columnCount(parent); ++j) { 0107 const QModelIndex index = model->index(i, j, parent); 0108 const QList<QWidget *> widgetList = 0109 widgetPool->findWidgets(index, 0110 optionView(index), 0111 isRemoving ? KWidgetItemDelegatePool::NotUpdateWidgets : KWidgetItemDelegatePool::UpdateWidgets); 0112 if (isRemoving) { 0113 widgetPool->d->allocatedWidgets.removeAll(widgetList); 0114 for (QWidget *widget : widgetList) { 0115 const QModelIndex idx = widgetPool->d->widgetInIndex[widget]; 0116 widgetPool->d->usedWidgets.remove(idx); 0117 widgetPool->d->widgetInIndex.remove(widget); 0118 delete widget; 0119 } 0120 } 0121 } 0122 i++; 0123 } 0124 } 0125 0126 inline QStyleOptionViewItem KWidgetItemDelegatePrivate::optionView(const QModelIndex &index) 0127 { 0128 QStyleOptionViewItem optionView; 0129 optionView.initFrom(itemView->viewport()); 0130 optionView.rect = itemView->visualRect(index); 0131 optionView.decorationSize = itemView->iconSize(); 0132 return optionView; 0133 } 0134 0135 void KWidgetItemDelegatePrivate::initializeModel(const QModelIndex &parent) 0136 { 0137 if (!model) { 0138 return; 0139 } 0140 0141 for (int i = 0; i < model->rowCount(parent); ++i) { 0142 for (int j = 0; j < model->columnCount(parent); ++j) { 0143 const QModelIndex index = model->index(i, j, parent); 0144 if (index.isValid()) { 0145 widgetPool->findWidgets(index, optionView(index)); 0146 } 0147 } 0148 // Check if we need to go recursively through the children of parent (if any) to initialize 0149 // all possible indexes that are shown. 0150 const QModelIndex index = model->index(i, 0, parent); 0151 if (index.isValid() && model->hasChildren(index)) { 0152 initializeModel(index); 0153 } 0154 } 0155 } 0156 //@endcond 0157 0158 KWidgetItemDelegate::KWidgetItemDelegate(QAbstractItemView *itemView, QObject *parent) 0159 : QAbstractItemDelegate(parent) 0160 , d(new KWidgetItemDelegatePrivate(this)) 0161 { 0162 Q_ASSERT(itemView); 0163 0164 itemView->setMouseTracking(true); 0165 itemView->viewport()->setAttribute(Qt::WA_Hover); 0166 0167 d->itemView = itemView; 0168 0169 itemView->viewport()->installEventFilter(d.get()); // mouse events 0170 itemView->installEventFilter(d.get()); // keyboard events 0171 0172 if (qobject_cast<QTreeView *>(itemView)) { 0173 connect(itemView, SIGNAL(collapsed(QModelIndex)), d.get(), SLOT(initializeModel())); 0174 connect(itemView, SIGNAL(expanded(QModelIndex)), d.get(), SLOT(initializeModel())); 0175 } 0176 } 0177 0178 KWidgetItemDelegate::~KWidgetItemDelegate() = default; 0179 0180 QAbstractItemView *KWidgetItemDelegate::itemView() const 0181 { 0182 return d->itemView; 0183 } 0184 0185 QPersistentModelIndex KWidgetItemDelegate::focusedIndex() const 0186 { 0187 const QPersistentModelIndex idx = d->widgetPool->d->widgetInIndex.value(QApplication::focusWidget()); 0188 if (idx.isValid()) { 0189 return idx; 0190 } 0191 // Use the mouse position, if the widget refused to take keyboard focus. 0192 const QPoint pos = d->itemView->viewport()->mapFromGlobal(QCursor::pos()); 0193 return d->itemView->indexAt(pos); 0194 } 0195 0196 //@cond PRIVATE 0197 bool KWidgetItemDelegatePrivate::eventFilter(QObject *watched, QEvent *event) 0198 { 0199 if (event->type() == QEvent::Destroy) { 0200 // we care for the view since it deletes the widgets (parentage). 0201 // if the view hasn't been deleted, it might be that just the 0202 // delegate is removed from it, in which case we need to remove the widgets 0203 // manually, otherwise they still get drawn. 0204 if (watched == itemView) { 0205 viewDestroyed = true; 0206 } 0207 return false; 0208 } 0209 0210 Q_ASSERT(itemView); 0211 0212 // clang-format off 0213 if (model != itemView->model()) { 0214 if (model) { 0215 disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(_k_slotRowsInserted(QModelIndex,int,int))); 0216 disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsAboutToBeRemoved(QModelIndex,int,int))); 0217 disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsRemoved(QModelIndex,int,int))); 0218 disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(_k_slotDataChanged(QModelIndex,QModelIndex))); 0219 disconnect(model, SIGNAL(layoutChanged()), q, SLOT(_k_slotLayoutChanged())); 0220 disconnect(model, SIGNAL(modelReset()), q, SLOT(_k_slotModelReset())); 0221 } 0222 model = itemView->model(); 0223 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(_k_slotRowsInserted(QModelIndex,int,int))); 0224 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsAboutToBeRemoved(QModelIndex,int,int))); 0225 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsRemoved(QModelIndex,int,int))); 0226 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(_k_slotDataChanged(QModelIndex,QModelIndex))); 0227 connect(model, SIGNAL(layoutChanged()), q, SLOT(_k_slotLayoutChanged())); 0228 connect(model, SIGNAL(modelReset()), q, SLOT(_k_slotModelReset())); 0229 QTimer::singleShot(0, this, SLOT(initializeModel())); 0230 } 0231 0232 if (selectionModel != itemView->selectionModel()) { 0233 if (selectionModel) { 0234 disconnect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(_k_slotSelectionChanged(QItemSelection,QItemSelection))); 0235 } 0236 selectionModel = itemView->selectionModel(); 0237 connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(_k_slotSelectionChanged(QItemSelection,QItemSelection))); 0238 QTimer::singleShot(0, this, SLOT(initializeModel())); 0239 } 0240 // clang-format on 0241 0242 switch (event->type()) { 0243 case QEvent::Polish: 0244 case QEvent::Resize: 0245 if (!qobject_cast<QAbstractItemView *>(watched)) { 0246 QTimer::singleShot(0, this, SLOT(initializeModel())); 0247 } 0248 break; 0249 case QEvent::FocusIn: 0250 case QEvent::FocusOut: 0251 if (qobject_cast<QAbstractItemView *>(watched)) { 0252 const auto lst = selectionModel->selectedIndexes(); 0253 for (const QModelIndex &index : lst) { 0254 if (index.isValid()) { 0255 widgetPool->findWidgets(index, optionView(index)); 0256 } 0257 } 0258 } 0259 break; 0260 default: 0261 break; 0262 } 0263 0264 return QObject::eventFilter(watched, event); 0265 } 0266 //@endcond 0267 0268 void KWidgetItemDelegate::setBlockedEventTypes(QWidget *widget, const QList<QEvent::Type> &types) const 0269 { 0270 widget->setProperty("goya:blockedEventTypes", QVariant::fromValue(types)); 0271 } 0272 0273 QList<QEvent::Type> KWidgetItemDelegate::blockedEventTypes(QWidget *widget) const 0274 { 0275 return widget->property("goya:blockedEventTypes").value<QList<QEvent::Type>>(); 0276 } 0277 0278 void KWidgetItemDelegate::resetModel() 0279 { 0280 d->_k_slotModelReset(); 0281 } 0282 0283 #include "moc_kwidgetitemdelegate.cpp" 0284 #include "moc_kwidgetitemdelegate_p.cpp"