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