1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qabstractproxymodel.h"
5#include "qitemselectionmodel.h"
6#include <private/qabstractproxymodel_p.h>
7#include <QtCore/QSize>
8#include <QtCore/QStringList>
9#include <QtCore/QMap>
10
11
12QT_BEGIN_NAMESPACE
13
14/*!
15 \since 4.1
16 \class QAbstractProxyModel
17 \brief The QAbstractProxyModel class provides a base class for proxy item
18 models that can do sorting, filtering or other data processing tasks.
19 \ingroup model-view
20 \inmodule QtCore
21
22 This class defines the standard interface that proxy models must use to be
23 able to interoperate correctly with other model/view components. It is not
24 supposed to be instantiated directly.
25
26 All standard proxy models are derived from the QAbstractProxyModel class.
27 If you need to create a new proxy model class, it is usually better to
28 subclass an existing class that provides the closest behavior to the one
29 you want to provide.
30
31 Proxy models that filter or sort items of data from a source model should
32 be created by using or subclassing QSortFilterProxyModel.
33
34 To subclass QAbstractProxyModel, you need to implement mapFromSource() and
35 mapToSource(). The mapSelectionFromSource() and mapSelectionToSource()
36 functions only need to be reimplemented if you need a behavior different
37 from the default behavior.
38
39 \note If the source model is deleted or no source model is specified, the
40 proxy model operates on a empty placeholder model.
41
42 \sa QSortFilterProxyModel, QAbstractItemModel, {Model/View Programming}
43*/
44
45/*!
46 \property QAbstractProxyModel::sourceModel
47
48 \brief the source model of this proxy model.
49*/
50
51//detects the deletion of the source model
52void QAbstractProxyModelPrivate::_q_sourceModelDestroyed()
53{
54 invalidatePersistentIndexes();
55 model = QAbstractItemModelPrivate::staticEmptyModel();
56}
57
58static auto emitHeaderDataChanged(QAbstractItemModel *model,
59 Qt::Orientation orientation,
60 int count)
61{
62 return [=](){ emit model->headerDataChanged(orientation, first: 0, last: count); };
63}
64
65void QAbstractProxyModelPrivate::_q_sourceModelRowsAboutToBeInserted(const QModelIndex &parent, int, int)
66{
67 if (parent.isValid())
68 return;
69 sourceHadZeroRows = model->rowCount() == 0;
70}
71
72void QAbstractProxyModelPrivate::_q_sourceModelRowsInserted(const QModelIndex &parent, int, int)
73{
74 if (parent.isValid())
75 return;
76 if (sourceHadZeroRows) {
77 Q_Q(QAbstractProxyModel);
78 const int columnCount = q->columnCount();
79 if (columnCount > 0)
80 QMetaObject::invokeMethod(object: q, function: emitHeaderDataChanged(model: q, orientation: Qt::Horizontal, count: columnCount - 1), type: Qt::QueuedConnection);
81 }
82}
83
84
85void QAbstractProxyModelPrivate::_q_sourceModelRowsRemoved(const QModelIndex &parent, int, int)
86{
87 if (parent.isValid())
88 return;
89 if (model->rowCount() == 0) {
90 Q_Q(QAbstractProxyModel);
91 const int columnCount = q->columnCount();
92 if (columnCount > 0)
93 QMetaObject::invokeMethod(object: q, function: emitHeaderDataChanged(model: q, orientation: Qt::Horizontal, count: columnCount - 1), type: Qt::QueuedConnection);
94 }
95}
96
97void QAbstractProxyModelPrivate::_q_sourceModelColumnsAboutToBeInserted(const QModelIndex &parent, int, int)
98{
99 if (parent.isValid())
100 return;
101 sourceHadZeroColumns = model->columnCount() == 0;
102}
103
104void QAbstractProxyModelPrivate::_q_sourceModelColumnsInserted(const QModelIndex &parent, int, int)
105{
106 if (parent.isValid())
107 return;
108 if (sourceHadZeroColumns) {
109 Q_Q(QAbstractProxyModel);
110 const int rowCount = q->rowCount();
111 if (rowCount > 0)
112 QMetaObject::invokeMethod(object: q, function: emitHeaderDataChanged(model: q, orientation: Qt::Vertical, count: rowCount - 1), type: Qt::QueuedConnection);
113 }
114}
115
116void QAbstractProxyModelPrivate::_q_sourceModelColumnsRemoved(const QModelIndex &parent, int, int)
117{
118 if (parent.isValid())
119 return;
120 if (model->columnCount() == 0) {
121 Q_Q(QAbstractProxyModel);
122 const int rowCount = q->rowCount();
123 if (rowCount > 0)
124 QMetaObject::invokeMethod(object: q, function: emitHeaderDataChanged(model: q, orientation: Qt::Vertical, count: rowCount - 1), type: Qt::QueuedConnection);
125 }
126}
127
128/*!
129 Constructs a proxy model with the given \a parent.
130*/
131
132QAbstractProxyModel::QAbstractProxyModel(QObject *parent)
133 :QAbstractItemModel(*new QAbstractProxyModelPrivate, parent)
134{
135 setSourceModel(QAbstractItemModelPrivate::staticEmptyModel());
136}
137
138/*!
139 \internal
140*/
141
142QAbstractProxyModel::QAbstractProxyModel(QAbstractProxyModelPrivate &dd, QObject *parent)
143 : QAbstractItemModel(dd, parent)
144{
145 setSourceModel(QAbstractItemModelPrivate::staticEmptyModel());
146}
147
148/*!
149 Destroys the proxy model.
150*/
151QAbstractProxyModel::~QAbstractProxyModel()
152{
153
154}
155
156/*!
157 Sets the given \a sourceModel to be processed by the proxy model.
158
159 Subclasses should call beginResetModel() at the beginning of the method,
160 disconnect from the old model, call this method, connect to the new model,
161 and call endResetModel().
162*/
163void QAbstractProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
164{
165 Q_D(QAbstractProxyModel);
166 d->model.removeBindingUnlessInWrapper();
167 // Special case to handle nullptr models. Otherwise we will have unwanted
168 // notifications.
169 const QAbstractItemModel *currentModel = d->model.valueBypassingBindings();
170 if (!sourceModel && currentModel == QAbstractItemModelPrivate::staticEmptyModel())
171 return;
172 static const struct {
173 const char *signalName;
174 const char *slotName;
175 } connectionTable[] = {
176 // clang-format off
177 { SIGNAL(destroyed()), SLOT(_q_sourceModelDestroyed()) },
178 { SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), SLOT(_q_sourceModelRowsAboutToBeInserted(QModelIndex,int,int)) },
179 { SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(_q_sourceModelRowsInserted(QModelIndex,int,int)) },
180 { SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(_q_sourceModelRowsRemoved(QModelIndex,int,int)) },
181 { SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), SLOT(_q_sourceModelColumnsAboutToBeInserted(QModelIndex,int,int)) },
182 { SIGNAL(columnsInserted(QModelIndex,int,int)), SLOT(_q_sourceModelColumnsInserted(QModelIndex,int,int)) },
183 { SIGNAL(columnsRemoved(QModelIndex,int,int)), SLOT(_q_sourceModelColumnsRemoved(QModelIndex,int,int)) }
184 // clang-format on
185 };
186
187 if (sourceModel != currentModel) {
188 if (currentModel) {
189 for (const auto &c : connectionTable)
190 disconnect(sender: currentModel, signal: c.signalName, receiver: this, member: c.slotName);
191 }
192
193 if (sourceModel) {
194 d->model.setValueBypassingBindings(sourceModel);
195 for (const auto &c : connectionTable)
196 connect(sender: sourceModel, signal: c.signalName, receiver: this, member: c.slotName);
197 } else {
198 d->model.setValueBypassingBindings(QAbstractItemModelPrivate::staticEmptyModel());
199 }
200 d->model.notify();
201 }
202}
203
204/*!
205 Returns the model that contains the data that is available through the proxy model.
206*/
207QAbstractItemModel *QAbstractProxyModel::sourceModel() const
208{
209 Q_D(const QAbstractProxyModel);
210 if (d->model == QAbstractItemModelPrivate::staticEmptyModel())
211 return nullptr;
212 return d->model;
213}
214
215QBindable<QAbstractItemModel *> QAbstractProxyModel::bindableSourceModel()
216{
217 Q_D(QAbstractProxyModel);
218 return QBindable<QAbstractItemModel *>(&d->model);
219}
220
221/*!
222 \reimp
223 */
224bool QAbstractProxyModel::submit()
225{
226 Q_D(QAbstractProxyModel);
227 return d->model->submit();
228}
229
230/*!
231 \reimp
232 */
233void QAbstractProxyModel::revert()
234{
235 Q_D(QAbstractProxyModel);
236 d->model->revert();
237}
238
239
240/*!
241 \fn QModelIndex QAbstractProxyModel::mapToSource(const QModelIndex &proxyIndex) const
242
243 Reimplement this function to return the model index in the source model that
244 corresponds to the \a proxyIndex in the proxy model.
245
246 \sa mapFromSource()
247*/
248
249/*!
250 \fn QModelIndex QAbstractProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
251
252 Reimplement this function to return the model index in the proxy model that
253 corresponds to the \a sourceIndex from the source model.
254
255 \sa mapToSource()
256*/
257
258/*!
259 Returns a source selection mapped from the specified \a proxySelection.
260
261 Reimplement this method to map proxy selections to source selections.
262 */
263QItemSelection QAbstractProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
264{
265 QModelIndexList proxyIndexes = proxySelection.indexes();
266 QItemSelection sourceSelection;
267 for (int i = 0; i < proxyIndexes.size(); ++i) {
268 const QModelIndex proxyIdx = mapToSource(proxyIndex: proxyIndexes.at(i));
269 if (!proxyIdx.isValid())
270 continue;
271 sourceSelection << QItemSelectionRange(proxyIdx);
272 }
273 return sourceSelection;
274}
275
276/*!
277 Returns a proxy selection mapped from the specified \a sourceSelection.
278
279 Reimplement this method to map source selections to proxy selections.
280*/
281QItemSelection QAbstractProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
282{
283 QModelIndexList sourceIndexes = sourceSelection.indexes();
284 QItemSelection proxySelection;
285 for (int i = 0; i < sourceIndexes.size(); ++i) {
286 const QModelIndex srcIdx = mapFromSource(sourceIndex: sourceIndexes.at(i));
287 if (!srcIdx.isValid())
288 continue;
289 proxySelection << QItemSelectionRange(srcIdx);
290 }
291 return proxySelection;
292}
293
294/*!
295 \reimp
296 */
297QVariant QAbstractProxyModel::data(const QModelIndex &proxyIndex, int role) const
298{
299 Q_D(const QAbstractProxyModel);
300 return d->model->data(index: mapToSource(proxyIndex), role);
301}
302
303/*!
304 \reimp
305 */
306QVariant QAbstractProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
307{
308 Q_D(const QAbstractProxyModel);
309 int sourceSection = section;
310 if (orientation == Qt::Horizontal) {
311 if (rowCount() > 0) {
312 const QModelIndex proxyIndex = index(row: 0, column: section);
313 sourceSection = mapToSource(proxyIndex).column();
314 }
315 } else {
316 if (columnCount() > 0) {
317 const QModelIndex proxyIndex = index(row: section, column: 0);
318 sourceSection = mapToSource(proxyIndex).row();
319 }
320 }
321 return d->model->headerData(section: sourceSection, orientation, role);
322}
323
324/*!
325 \reimp
326 */
327QMap<int, QVariant> QAbstractProxyModel::itemData(const QModelIndex &proxyIndex) const
328{
329 Q_D(const QAbstractProxyModel);
330 return d->model->itemData(index: mapToSource(proxyIndex));
331}
332
333/*!
334 \reimp
335 */
336Qt::ItemFlags QAbstractProxyModel::flags(const QModelIndex &index) const
337{
338 Q_D(const QAbstractProxyModel);
339 return d->model->flags(index: mapToSource(proxyIndex: index));
340}
341
342/*!
343 \reimp
344 */
345bool QAbstractProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
346{
347 Q_D(QAbstractProxyModel);
348 return d->model->setData(index: mapToSource(proxyIndex: index), value, role);
349}
350
351/*!
352 \reimp
353 */
354bool QAbstractProxyModel::setItemData(const QModelIndex &index, const QMap< int, QVariant >& roles)
355{
356 Q_D(QAbstractProxyModel);
357 return d->model->setItemData(index: mapToSource(proxyIndex: index), roles);
358}
359
360/*!
361 \reimp
362 */
363bool QAbstractProxyModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
364{
365 Q_D(QAbstractProxyModel);
366 int sourceSection;
367 if (orientation == Qt::Horizontal) {
368 const QModelIndex proxyIndex = index(row: 0, column: section);
369 sourceSection = mapToSource(proxyIndex).column();
370 } else {
371 const QModelIndex proxyIndex = index(row: section, column: 0);
372 sourceSection = mapToSource(proxyIndex).row();
373 }
374 return d->model->setHeaderData(section: sourceSection, orientation, value, role);
375}
376
377/*!
378 \reimp
379 \since 6.0
380 */
381bool QAbstractProxyModel::clearItemData(const QModelIndex &index)
382{
383 Q_D(QAbstractProxyModel);
384 return d->model->clearItemData(index: mapToSource(proxyIndex: index));
385}
386
387/*!
388 \reimp
389 */
390QModelIndex QAbstractProxyModel::buddy(const QModelIndex &index) const
391{
392 Q_D(const QAbstractProxyModel);
393 return mapFromSource(sourceIndex: d->model->buddy(index: mapToSource(proxyIndex: index)));
394}
395
396/*!
397 \reimp
398 */
399bool QAbstractProxyModel::canFetchMore(const QModelIndex &parent) const
400{
401 Q_D(const QAbstractProxyModel);
402 return d->model->canFetchMore(parent: mapToSource(proxyIndex: parent));
403}
404
405/*!
406 \reimp
407 */
408void QAbstractProxyModel::fetchMore(const QModelIndex &parent)
409{
410 Q_D(QAbstractProxyModel);
411 d->model->fetchMore(parent: mapToSource(proxyIndex: parent));
412}
413
414/*!
415 \reimp
416 */
417void QAbstractProxyModel::sort(int column, Qt::SortOrder order)
418{
419 Q_D(QAbstractProxyModel);
420 d->model->sort(column, order);
421}
422
423/*!
424 \reimp
425 */
426QSize QAbstractProxyModel::span(const QModelIndex &index) const
427{
428 Q_D(const QAbstractProxyModel);
429 return d->model->span(index: mapToSource(proxyIndex: index));
430}
431
432/*!
433 \reimp
434 */
435bool QAbstractProxyModel::hasChildren(const QModelIndex &parent) const
436{
437 Q_D(const QAbstractProxyModel);
438 return d->model->hasChildren(parent: mapToSource(proxyIndex: parent));
439}
440
441/*!
442 \reimp
443 */
444QModelIndex QAbstractProxyModel::sibling(int row, int column, const QModelIndex &idx) const
445{
446 return index(row, column, parent: idx.parent());
447}
448
449/*!
450 \reimp
451 */
452QMimeData* QAbstractProxyModel::mimeData(const QModelIndexList &indexes) const
453{
454 Q_D(const QAbstractProxyModel);
455 QModelIndexList list;
456 list.reserve(asize: indexes.size());
457 for (const QModelIndex &index : indexes)
458 list << mapToSource(proxyIndex: index);
459 return d->model->mimeData(indexes: list);
460}
461
462void QAbstractProxyModelPrivate::mapDropCoordinatesToSource(int row, int column, const QModelIndex &parent,
463 int *sourceRow, int *sourceColumn, QModelIndex *sourceParent) const
464{
465 Q_Q(const QAbstractProxyModel);
466 *sourceRow = -1;
467 *sourceColumn = -1;
468 if (row == -1 && column == -1) {
469 *sourceParent = q->mapToSource(proxyIndex: parent);
470 } else if (row == q->rowCount(parent)) {
471 *sourceParent = q->mapToSource(proxyIndex: parent);
472 *sourceRow = model->rowCount(parent: *sourceParent);
473 } else {
474 QModelIndex proxyIndex = q->index(row, column, parent);
475 QModelIndex sourceIndex = q->mapToSource(proxyIndex);
476 *sourceRow = sourceIndex.row();
477 *sourceColumn = sourceIndex.column();
478 *sourceParent = sourceIndex.parent();
479 }
480}
481
482/*!
483 \reimp
484 \since 5.4
485 */
486bool QAbstractProxyModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
487 int row, int column, const QModelIndex &parent) const
488{
489 Q_D(const QAbstractProxyModel);
490 int sourceDestinationRow;
491 int sourceDestinationColumn;
492 QModelIndex sourceParent;
493 d->mapDropCoordinatesToSource(row, column, parent, sourceRow: &sourceDestinationRow, sourceColumn: &sourceDestinationColumn, sourceParent: &sourceParent);
494 return d->model->canDropMimeData(data, action, row: sourceDestinationRow, column: sourceDestinationColumn, parent: sourceParent);
495}
496
497/*!
498 \reimp
499 \since 5.4
500 */
501bool QAbstractProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
502 int row, int column, const QModelIndex &parent)
503{
504 Q_D(QAbstractProxyModel);
505 int sourceDestinationRow;
506 int sourceDestinationColumn;
507 QModelIndex sourceParent;
508 d->mapDropCoordinatesToSource(row, column, parent, sourceRow: &sourceDestinationRow, sourceColumn: &sourceDestinationColumn, sourceParent: &sourceParent);
509 return d->model->dropMimeData(data, action, row: sourceDestinationRow, column: sourceDestinationColumn, parent: sourceParent);
510}
511
512/*!
513 \reimp
514 */
515QStringList QAbstractProxyModel::mimeTypes() const
516{
517 Q_D(const QAbstractProxyModel);
518 return d->model->mimeTypes();
519}
520
521/*!
522 \reimp
523 */
524Qt::DropActions QAbstractProxyModel::supportedDragActions() const
525{
526 Q_D(const QAbstractProxyModel);
527 return d->model->supportedDragActions();
528}
529
530/*!
531 \reimp
532 */
533Qt::DropActions QAbstractProxyModel::supportedDropActions() const
534{
535 Q_D(const QAbstractProxyModel);
536 return d->model->supportedDropActions();
537}
538
539/*!
540 \reimp
541 */
542QHash<int,QByteArray> QAbstractProxyModel::roleNames() const
543{
544 Q_D(const QAbstractProxyModel);
545 return d->model->roleNames();
546}
547
548/*!
549 Equivalent to calling createIndex on the source model.
550
551 This method is useful if your proxy model wants to maintain the
552 parent-child relationship of items in the source model.
553 When reimplementing mapToSource(), you can call this method to
554 create an index for row \a row and column \a col of the source model.
555
556 A typical use would be to save the internal pointer coming from the source model
557 in the proxy index when reimplementing mapFromSource() and use the same internal
558 pointer as \a internalPtr to recover the original source index when
559 reimplementing mapToSource().
560 \since 6.2
561 */
562QModelIndex QAbstractProxyModel::createSourceIndex(int row, int col, void *internalPtr) const
563{
564 if (sourceModel())
565 return sourceModel()->createIndex(arow: row, acolumn: col, adata: internalPtr);
566 return QModelIndex();
567}
568
569QT_END_NAMESPACE
570
571#include "moc_qabstractproxymodel.cpp"
572

source code of qtbase/src/corelib/itemmodels/qabstractproxymodel.cpp