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