1 | // Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> |
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 "qtransposeproxymodel.h" |
5 | #include <private/qtransposeproxymodel_p.h> |
6 | #include <QtCore/qlist.h> |
7 | #include <QtCore/qmetaobject.h> |
8 | #include <QtCore/qsize.h> |
9 | #include <QtCore/qmap.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | QModelIndex QTransposeProxyModelPrivate::uncheckedMapToSource(const QModelIndex &proxyIndex) const |
14 | { |
15 | if (!model || !proxyIndex.isValid()) |
16 | return QModelIndex(); |
17 | Q_Q(const QTransposeProxyModel); |
18 | return q->createSourceIndex(row: proxyIndex.column(), col: proxyIndex.row(), internalPtr: proxyIndex.internalPointer()); |
19 | } |
20 | |
21 | QModelIndex QTransposeProxyModelPrivate::uncheckedMapFromSource(const QModelIndex &sourceIndex) const |
22 | { |
23 | if (!model || !sourceIndex.isValid()) |
24 | return QModelIndex(); |
25 | Q_Q(const QTransposeProxyModel); |
26 | return q->createIndex(arow: sourceIndex.column(), acolumn: sourceIndex.row(), adata: sourceIndex.internalPointer()); |
27 | } |
28 | |
29 | void QTransposeProxyModelPrivate::onLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) |
30 | { |
31 | Q_Q(QTransposeProxyModel); |
32 | Q_ASSERT(layoutChangeProxyIndexes.size() == layoutChangePersistentIndexes.size()); |
33 | QModelIndexList toList; |
34 | toList.reserve(size: layoutChangePersistentIndexes.size()); |
35 | for (const QPersistentModelIndex &persistIdx : std::as_const(t&: layoutChangePersistentIndexes)) |
36 | toList << q->mapFromSource(sourceIndex: persistIdx); |
37 | q->changePersistentIndexList(from: layoutChangeProxyIndexes, to: toList); |
38 | layoutChangeProxyIndexes.clear(); |
39 | layoutChangePersistentIndexes.clear(); |
40 | QList<QPersistentModelIndex> proxyParents; |
41 | proxyParents.reserve(size: parents.size()); |
42 | for (const QPersistentModelIndex &srcParent : parents) |
43 | proxyParents << q->mapFromSource(sourceIndex: srcParent); |
44 | QAbstractItemModel::LayoutChangeHint proxyHint = QAbstractItemModel::NoLayoutChangeHint; |
45 | if (hint == QAbstractItemModel::VerticalSortHint) |
46 | proxyHint = QAbstractItemModel::HorizontalSortHint; |
47 | else if (hint == QAbstractItemModel::HorizontalSortHint) |
48 | proxyHint = QAbstractItemModel::VerticalSortHint; |
49 | emit q->layoutChanged(parents: proxyParents, hint: proxyHint); |
50 | } |
51 | |
52 | void QTransposeProxyModelPrivate::onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) |
53 | { |
54 | Q_Q(QTransposeProxyModel); |
55 | QList<QPersistentModelIndex> proxyParents; |
56 | proxyParents.reserve(size: sourceParents.size()); |
57 | for (const QPersistentModelIndex &parent : sourceParents) { |
58 | if (!parent.isValid()) { |
59 | proxyParents << QPersistentModelIndex(); |
60 | continue; |
61 | } |
62 | const QModelIndex mappedParent = q->mapFromSource(sourceIndex: parent); |
63 | Q_ASSERT(mappedParent.isValid()); |
64 | proxyParents << mappedParent; |
65 | } |
66 | QAbstractItemModel::LayoutChangeHint proxyHint = QAbstractItemModel::NoLayoutChangeHint; |
67 | if (hint == QAbstractItemModel::VerticalSortHint) |
68 | proxyHint = QAbstractItemModel::HorizontalSortHint; |
69 | else if (hint == QAbstractItemModel::HorizontalSortHint) |
70 | proxyHint = QAbstractItemModel::VerticalSortHint; |
71 | emit q->layoutAboutToBeChanged(parents: proxyParents, hint: proxyHint); |
72 | const QModelIndexList proxyPersistentIndexes = q->persistentIndexList(); |
73 | layoutChangeProxyIndexes.clear(); |
74 | layoutChangePersistentIndexes.clear(); |
75 | layoutChangeProxyIndexes.reserve(size: proxyPersistentIndexes.size()); |
76 | layoutChangePersistentIndexes.reserve(size: proxyPersistentIndexes.size()); |
77 | for (const QModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { |
78 | layoutChangeProxyIndexes << proxyPersistentIndex; |
79 | Q_ASSERT(proxyPersistentIndex.isValid()); |
80 | const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyIndex: proxyPersistentIndex); |
81 | Q_ASSERT(srcPersistentIndex.isValid()); |
82 | layoutChangePersistentIndexes << srcPersistentIndex; |
83 | } |
84 | } |
85 | |
86 | void QTransposeProxyModelPrivate::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, |
87 | const QList<int> &roles) |
88 | { |
89 | Q_Q(QTransposeProxyModel); |
90 | emit q->dataChanged(topLeft: q->mapFromSource(sourceIndex: topLeft), bottomRight: q->mapFromSource(sourceIndex: bottomRight), roles); |
91 | } |
92 | |
93 | void QTransposeProxyModelPrivate::(Qt::Orientation orientation, int first, int last) |
94 | { |
95 | Q_Q(QTransposeProxyModel); |
96 | emit q->headerDataChanged(orientation: orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, first, last); |
97 | } |
98 | |
99 | void QTransposeProxyModelPrivate::onColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last) |
100 | { |
101 | Q_Q(QTransposeProxyModel); |
102 | q->beginInsertRows(parent: q->mapFromSource(sourceIndex: parent), first, last); |
103 | } |
104 | |
105 | void QTransposeProxyModelPrivate::onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last) |
106 | { |
107 | Q_Q(QTransposeProxyModel); |
108 | q->beginRemoveRows(parent: q->mapFromSource(sourceIndex: parent), first, last); |
109 | } |
110 | |
111 | void QTransposeProxyModelPrivate::onColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) |
112 | { |
113 | Q_Q(QTransposeProxyModel); |
114 | q->beginMoveRows(sourceParent: q->mapFromSource(sourceIndex: sourceParent), sourceFirst: sourceStart, sourceLast: sourceEnd, destinationParent: q->mapFromSource(sourceIndex: destinationParent), destinationRow: destinationColumn); |
115 | } |
116 | |
117 | void QTransposeProxyModelPrivate::onRowsAboutToBeInserted(const QModelIndex &parent, int first, int last) |
118 | { |
119 | Q_Q(QTransposeProxyModel); |
120 | q->beginInsertColumns(parent: q->mapFromSource(sourceIndex: parent), first, last); |
121 | } |
122 | |
123 | void QTransposeProxyModelPrivate::onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) |
124 | { |
125 | Q_Q(QTransposeProxyModel); |
126 | q->beginRemoveColumns(parent: q->mapFromSource(sourceIndex: parent), first, last); |
127 | } |
128 | |
129 | void QTransposeProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) |
130 | { |
131 | Q_Q(QTransposeProxyModel); |
132 | q->beginMoveColumns(sourceParent: q->mapFromSource(sourceIndex: sourceParent), sourceFirst: sourceStart, sourceLast: sourceEnd, destinationParent: q->mapFromSource(sourceIndex: destinationParent), destinationColumn: destinationRow); |
133 | } |
134 | |
135 | /*! |
136 | \since 5.13 |
137 | \class QTransposeProxyModel |
138 | \inmodule QtCore |
139 | \brief This proxy transposes the source model. |
140 | |
141 | This model will make the rows of the source model become columns of the proxy model and vice-versa. |
142 | |
143 | If the model is a tree, the parents will be transposed as well. For example, if an index in the source model had parent `index(2,0)`, it will have parent `index(0,2)` in the proxy. |
144 | */ |
145 | |
146 | /*! |
147 | Constructs a new proxy model with the given \a parent. |
148 | */ |
149 | QTransposeProxyModel::QTransposeProxyModel(QObject* parent) |
150 | : QAbstractProxyModel(*new QTransposeProxyModelPrivate, parent) |
151 | {} |
152 | |
153 | /*! |
154 | Destructs the proxy model. |
155 | */ |
156 | QTransposeProxyModel::~QTransposeProxyModel() = default; |
157 | |
158 | /*! |
159 | \internal |
160 | */ |
161 | QTransposeProxyModel::QTransposeProxyModel(QTransposeProxyModelPrivate &dd, QObject *parent) |
162 | : QAbstractProxyModel(dd, parent) |
163 | {} |
164 | |
165 | /*! |
166 | \reimp |
167 | */ |
168 | void QTransposeProxyModel::setSourceModel(QAbstractItemModel* newSourceModel) |
169 | { |
170 | Q_D(QTransposeProxyModel); |
171 | if (newSourceModel == d->model) |
172 | return; |
173 | beginResetModel(); |
174 | if (d->model) { |
175 | for (const QMetaObject::Connection& discIter : std::as_const(t&: d->sourceConnections)) |
176 | disconnect(discIter); |
177 | } |
178 | d->sourceConnections.clear(); |
179 | QAbstractProxyModel::setSourceModel(newSourceModel); |
180 | if (d->model) { |
181 | using namespace std::placeholders; |
182 | d->sourceConnections = QList<QMetaObject::Connection>{ |
183 | connect(sender: d->model, signal: &QAbstractItemModel::modelAboutToBeReset, context: this, slot: &QTransposeProxyModel::beginResetModel), |
184 | connect(sender: d->model, signal: &QAbstractItemModel::modelReset, context: this, slot: &QTransposeProxyModel::endResetModel), |
185 | connect(sender: d->model, signal: &QAbstractItemModel::dataChanged, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onDataChanged, args: d, args: _1, args: _2, args: _3)), |
186 | connect(sender: d->model, signal: &QAbstractItemModel::headerDataChanged, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onHeaderDataChanged, args: d, args: _1, args: _2, args: _3)), |
187 | connect(sender: d->model, signal: &QAbstractItemModel::columnsAboutToBeInserted, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onColumnsAboutToBeInserted, args: d, args: _1, args: _2, args: _3)), |
188 | connect(sender: d->model, signal: &QAbstractItemModel::columnsAboutToBeMoved, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onColumnsAboutToBeMoved, args: d, args: _1, args: _2, args: _3, args: _4, args: _5)), |
189 | connect(sender: d->model, signal: &QAbstractItemModel::columnsAboutToBeRemoved, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onColumnsAboutToBeRemoved, args: d, args: _1, args: _2, args: _3)), |
190 | connect(sender: d->model, signal: &QAbstractItemModel::columnsInserted, context: this, slot: &QTransposeProxyModel::endInsertRows), |
191 | connect(sender: d->model, signal: &QAbstractItemModel::columnsRemoved, context: this, slot: &QTransposeProxyModel::endRemoveRows), |
192 | connect(sender: d->model, signal: &QAbstractItemModel::columnsMoved, context: this, slot: &QTransposeProxyModel::endMoveRows), |
193 | connect(sender: d->model, signal: &QAbstractItemModel::rowsAboutToBeInserted, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onRowsAboutToBeInserted, args: d, args: _1, args: _2, args: _3)), |
194 | connect(sender: d->model, signal: &QAbstractItemModel::rowsAboutToBeMoved, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onRowsAboutToBeMoved, args: d, args: _1, args: _2, args: _3, args: _4, args: _5)), |
195 | connect(sender: d->model, signal: &QAbstractItemModel::rowsAboutToBeRemoved, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onRowsAboutToBeRemoved, args: d, args: _1, args: _2, args: _3)), |
196 | connect(sender: d->model, signal: &QAbstractItemModel::rowsInserted, context: this, slot: &QTransposeProxyModel::endInsertColumns), |
197 | connect(sender: d->model, signal: &QAbstractItemModel::rowsRemoved, context: this, slot: &QTransposeProxyModel::endRemoveColumns), |
198 | connect(sender: d->model, signal: &QAbstractItemModel::rowsMoved, context: this, slot: &QTransposeProxyModel::endMoveColumns), |
199 | connect(sender: d->model, signal: &QAbstractItemModel::layoutAboutToBeChanged, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onLayoutAboutToBeChanged, args: d, args: _1, args: _2)), |
200 | connect(sender: d->model, signal: &QAbstractItemModel::layoutChanged, context: this, slot: std::bind(f: &QTransposeProxyModelPrivate::onLayoutChanged, args: d, args: _1, args: _2)) |
201 | }; |
202 | } |
203 | endResetModel(); |
204 | } |
205 | |
206 | /*! |
207 | \reimp |
208 | */ |
209 | int QTransposeProxyModel::rowCount(const QModelIndex &parent) const |
210 | { |
211 | Q_D(const QTransposeProxyModel); |
212 | if (!d->model) |
213 | return 0; |
214 | Q_ASSERT(checkIndex(parent)); |
215 | return d->model->columnCount(parent: mapToSource(proxyIndex: parent)); |
216 | } |
217 | |
218 | /*! |
219 | \reimp |
220 | */ |
221 | int QTransposeProxyModel::columnCount(const QModelIndex &parent) const |
222 | { |
223 | Q_D(const QTransposeProxyModel); |
224 | if (!d->model) |
225 | return 0; |
226 | Q_ASSERT(checkIndex(parent)); |
227 | return d->model->rowCount(parent: mapToSource(proxyIndex: parent)); |
228 | } |
229 | |
230 | /*! |
231 | \reimp |
232 | */ |
233 | QVariant QTransposeProxyModel::(int section, Qt::Orientation orientation, int role) const |
234 | { |
235 | Q_D(const QTransposeProxyModel); |
236 | if (!d->model) |
237 | return QVariant(); |
238 | return d->model->headerData(section, orientation: orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, role); |
239 | } |
240 | |
241 | /*! |
242 | \reimp |
243 | */ |
244 | bool QTransposeProxyModel::(int section, Qt::Orientation orientation, const QVariant &value, int role) |
245 | { |
246 | Q_D(QTransposeProxyModel); |
247 | if (!d->model) |
248 | return false; |
249 | return d->model->setHeaderData(section, orientation: orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, value, role); |
250 | } |
251 | |
252 | /*! |
253 | \reimp |
254 | */ |
255 | bool QTransposeProxyModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) |
256 | { |
257 | Q_D(QTransposeProxyModel); |
258 | Q_ASSERT(checkIndex(index)); |
259 | if (!d->model || !index.isValid()) |
260 | return false; |
261 | return d->model->setItemData(index: mapToSource(proxyIndex: index), roles); |
262 | } |
263 | |
264 | /*! |
265 | \reimp |
266 | */ |
267 | QSize QTransposeProxyModel::span(const QModelIndex &index) const |
268 | { |
269 | Q_D(const QTransposeProxyModel); |
270 | Q_ASSERT(checkIndex(index)); |
271 | if (!d->model || !index.isValid()) |
272 | return QSize(); |
273 | return d->model->span(index: mapToSource(proxyIndex: index)).transposed(); |
274 | } |
275 | |
276 | /*! |
277 | \reimp |
278 | */ |
279 | QMap<int, QVariant> QTransposeProxyModel::itemData(const QModelIndex &index) const |
280 | { |
281 | Q_D(const QTransposeProxyModel); |
282 | if (!d->model) |
283 | return QMap<int, QVariant>(); |
284 | Q_ASSERT(checkIndex(index)); |
285 | return d->model->itemData(index: mapToSource(proxyIndex: index)); |
286 | } |
287 | |
288 | /*! |
289 | \reimp |
290 | */ |
291 | QModelIndex QTransposeProxyModel::mapFromSource(const QModelIndex &sourceIndex) const |
292 | { |
293 | Q_D(const QTransposeProxyModel); |
294 | if (!d->model || !sourceIndex.isValid()) |
295 | return QModelIndex(); |
296 | Q_ASSERT(d->model->checkIndex(sourceIndex)); |
297 | return d->uncheckedMapFromSource(sourceIndex); |
298 | } |
299 | |
300 | /*! |
301 | \reimp |
302 | */ |
303 | QModelIndex QTransposeProxyModel::mapToSource(const QModelIndex &proxyIndex) const |
304 | { |
305 | Q_D(const QTransposeProxyModel); |
306 | Q_ASSERT(checkIndex(proxyIndex)); |
307 | if (!d->model || !proxyIndex.isValid()) |
308 | return QModelIndex(); |
309 | return d->uncheckedMapToSource(proxyIndex); |
310 | } |
311 | |
312 | /*! |
313 | \reimp |
314 | */ |
315 | QModelIndex QTransposeProxyModel::parent(const QModelIndex &index) const |
316 | { |
317 | Q_D(const QTransposeProxyModel); |
318 | Q_ASSERT(checkIndex(index, CheckIndexOption::DoNotUseParent)); |
319 | if (!d->model || !index.isValid()) |
320 | return QModelIndex(); |
321 | return d->uncheckedMapFromSource(sourceIndex: d->uncheckedMapToSource(proxyIndex: index).parent()); |
322 | } |
323 | |
324 | /*! |
325 | \reimp |
326 | */ |
327 | QModelIndex QTransposeProxyModel::index(int row, int column, const QModelIndex &parent) const |
328 | { |
329 | Q_D(const QTransposeProxyModel); |
330 | Q_ASSERT(checkIndex(parent)); |
331 | if (!d->model) |
332 | return QModelIndex(); |
333 | return mapFromSource(sourceIndex: d->model->index(row: column, column: row, parent: mapToSource(proxyIndex: parent))); |
334 | } |
335 | |
336 | /*! |
337 | \reimp |
338 | */ |
339 | bool QTransposeProxyModel::insertRows(int row, int count, const QModelIndex &parent) |
340 | { |
341 | Q_D(QTransposeProxyModel); |
342 | Q_ASSERT(checkIndex(parent)); |
343 | if (!d->model) |
344 | return false; |
345 | return d->model->insertColumns(column: row, count, parent: mapToSource(proxyIndex: parent)); |
346 | } |
347 | |
348 | /*! |
349 | \reimp |
350 | */ |
351 | bool QTransposeProxyModel::removeRows(int row, int count, const QModelIndex &parent) |
352 | { |
353 | Q_D(QTransposeProxyModel); |
354 | Q_ASSERT(checkIndex(parent)); |
355 | if (!d->model) |
356 | return false; |
357 | return d->model->removeColumns(column: row, count, parent: mapToSource(proxyIndex: parent)); |
358 | } |
359 | |
360 | /*! |
361 | \reimp |
362 | */ |
363 | bool QTransposeProxyModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) |
364 | { |
365 | Q_D(QTransposeProxyModel); |
366 | Q_ASSERT(checkIndex(sourceParent)); |
367 | Q_ASSERT(checkIndex(destinationParent)); |
368 | if (!d->model) |
369 | return false; |
370 | return d->model->moveColumns(sourceParent: mapToSource(proxyIndex: sourceParent), sourceColumn: sourceRow, count, destinationParent: mapToSource(proxyIndex: destinationParent), destinationChild); |
371 | } |
372 | |
373 | /*! |
374 | \reimp |
375 | */ |
376 | bool QTransposeProxyModel::insertColumns(int column, int count, const QModelIndex &parent) |
377 | { |
378 | Q_D(QTransposeProxyModel); |
379 | Q_ASSERT(checkIndex(parent)); |
380 | if (!d->model) |
381 | return false; |
382 | return d->model->insertRows(row: column, count, parent: mapToSource(proxyIndex: parent)); |
383 | } |
384 | |
385 | /*! |
386 | \reimp |
387 | */ |
388 | bool QTransposeProxyModel::removeColumns(int column, int count, const QModelIndex &parent) |
389 | { |
390 | Q_D(QTransposeProxyModel); |
391 | Q_ASSERT(checkIndex(parent)); |
392 | if (!d->model) |
393 | return false; |
394 | return d->model->removeRows(row: column, count, parent: mapToSource(proxyIndex: parent)); |
395 | } |
396 | |
397 | /*! |
398 | \reimp |
399 | */ |
400 | bool QTransposeProxyModel::moveColumns(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) |
401 | { |
402 | Q_D(QTransposeProxyModel); |
403 | Q_ASSERT(checkIndex(sourceParent)); |
404 | Q_ASSERT(checkIndex(destinationParent)); |
405 | if (!d->model) |
406 | return false; |
407 | return d->model->moveRows(sourceParent: mapToSource(proxyIndex: sourceParent), sourceRow, count, destinationParent: mapToSource(proxyIndex: destinationParent), destinationChild); |
408 | } |
409 | |
410 | /*! |
411 | \reimp |
412 | This method will perform no action. Use a QSortFilterProxyModel on top of this one if you require sorting. |
413 | */ |
414 | void QTransposeProxyModel::sort(int column, Qt::SortOrder order) |
415 | { |
416 | Q_UNUSED(column); |
417 | Q_UNUSED(order); |
418 | return; |
419 | } |
420 | |
421 | QT_END_NAMESPACE |
422 | |
423 | #include "moc_qtransposeproxymodel.cpp" |
424 | |