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 | |
12 | QT_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 |
52 | void QAbstractProxyModelPrivate::_q_sourceModelDestroyed() |
53 | { |
54 | invalidatePersistentIndexes(); |
55 | model = QAbstractItemModelPrivate::staticEmptyModel(); |
56 | } |
57 | |
58 | static auto (QAbstractItemModel *model, |
59 | Qt::Orientation orientation, |
60 | int count) |
61 | { |
62 | return [=](){ emit model->headerDataChanged(orientation, first: 0, last: count); }; |
63 | } |
64 | |
65 | void QAbstractProxyModelPrivate::_q_sourceModelRowsAboutToBeInserted(const QModelIndex &parent, int, int) |
66 | { |
67 | if (parent.isValid()) |
68 | return; |
69 | sourceHadZeroRows = model->rowCount() == 0; |
70 | } |
71 | |
72 | void 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 | |
85 | void 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 | |
97 | void QAbstractProxyModelPrivate::_q_sourceModelColumnsAboutToBeInserted(const QModelIndex &parent, int, int) |
98 | { |
99 | if (parent.isValid()) |
100 | return; |
101 | sourceHadZeroColumns = model->columnCount() == 0; |
102 | } |
103 | |
104 | void 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 | |
116 | void 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 | |
132 | QAbstractProxyModel::QAbstractProxyModel(QObject *parent) |
133 | :QAbstractItemModel(*new QAbstractProxyModelPrivate, parent) |
134 | { |
135 | setSourceModel(QAbstractItemModelPrivate::staticEmptyModel()); |
136 | } |
137 | |
138 | /*! |
139 | \internal |
140 | */ |
141 | |
142 | QAbstractProxyModel::QAbstractProxyModel(QAbstractProxyModelPrivate &dd, QObject *parent) |
143 | : QAbstractItemModel(dd, parent) |
144 | { |
145 | setSourceModel(QAbstractItemModelPrivate::staticEmptyModel()); |
146 | } |
147 | |
148 | /*! |
149 | Destroys the proxy model. |
150 | */ |
151 | QAbstractProxyModel::~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 | */ |
163 | void 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 | */ |
207 | QAbstractItemModel *QAbstractProxyModel::sourceModel() const |
208 | { |
209 | Q_D(const QAbstractProxyModel); |
210 | if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) |
211 | return nullptr; |
212 | return d->model; |
213 | } |
214 | |
215 | QBindable<QAbstractItemModel *> QAbstractProxyModel::bindableSourceModel() |
216 | { |
217 | Q_D(QAbstractProxyModel); |
218 | return QBindable<QAbstractItemModel *>(&d->model); |
219 | } |
220 | |
221 | /*! |
222 | \reimp |
223 | */ |
224 | bool QAbstractProxyModel::submit() |
225 | { |
226 | Q_D(QAbstractProxyModel); |
227 | return d->model->submit(); |
228 | } |
229 | |
230 | /*! |
231 | \reimp |
232 | */ |
233 | void 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 | */ |
263 | QItemSelection 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 | */ |
281 | QItemSelection 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 | */ |
297 | QVariant 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 | */ |
306 | QVariant QAbstractProxyModel::(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 | */ |
327 | QMap<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 | */ |
336 | Qt::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 | */ |
345 | bool 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 | */ |
354 | bool 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 | */ |
363 | bool QAbstractProxyModel::(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 | */ |
381 | bool 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 | */ |
390 | QModelIndex 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 | */ |
399 | bool 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 | */ |
408 | void QAbstractProxyModel::fetchMore(const QModelIndex &parent) |
409 | { |
410 | Q_D(QAbstractProxyModel); |
411 | d->model->fetchMore(parent: mapToSource(proxyIndex: parent)); |
412 | } |
413 | |
414 | /*! |
415 | \reimp |
416 | */ |
417 | void QAbstractProxyModel::sort(int column, Qt::SortOrder order) |
418 | { |
419 | Q_D(QAbstractProxyModel); |
420 | d->model->sort(column, order); |
421 | } |
422 | |
423 | /*! |
424 | \reimp |
425 | */ |
426 | QSize 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 | */ |
435 | bool 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 | */ |
444 | QModelIndex 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 | */ |
452 | QMimeData* 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 | |
462 | void 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 | */ |
486 | bool 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 | */ |
501 | bool 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 | */ |
515 | QStringList QAbstractProxyModel::mimeTypes() const |
516 | { |
517 | Q_D(const QAbstractProxyModel); |
518 | return d->model->mimeTypes(); |
519 | } |
520 | |
521 | /*! |
522 | \reimp |
523 | */ |
524 | Qt::DropActions QAbstractProxyModel::supportedDragActions() const |
525 | { |
526 | Q_D(const QAbstractProxyModel); |
527 | return d->model->supportedDragActions(); |
528 | } |
529 | |
530 | /*! |
531 | \reimp |
532 | */ |
533 | Qt::DropActions QAbstractProxyModel::supportedDropActions() const |
534 | { |
535 | Q_D(const QAbstractProxyModel); |
536 | return d->model->supportedDropActions(); |
537 | } |
538 | |
539 | /*! |
540 | \reimp |
541 | */ |
542 | QHash<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 | */ |
562 | QModelIndex 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 | |
569 | QT_END_NAMESPACE |
570 | |
571 | #include "moc_qabstractproxymodel.cpp" |
572 | |