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