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
11QT_BEGIN_NAMESPACE
12
13QModelIndex 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
21QModelIndex 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
29void 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
52void 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
86void 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
93void QTransposeProxyModelPrivate::onHeaderDataChanged(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
99void 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
105void 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
111void 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
117void 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
123void 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
129void 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*/
149QTransposeProxyModel::QTransposeProxyModel(QObject* parent)
150 : QAbstractProxyModel(*new QTransposeProxyModelPrivate, parent)
151{}
152
153/*!
154 Destructs the proxy model.
155*/
156QTransposeProxyModel::~QTransposeProxyModel() = default;
157
158/*!
159 \internal
160*/
161QTransposeProxyModel::QTransposeProxyModel(QTransposeProxyModelPrivate &dd, QObject *parent)
162 : QAbstractProxyModel(dd, parent)
163{}
164
165/*!
166 \reimp
167*/
168void 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*/
209int 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*/
221int 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*/
233QVariant QTransposeProxyModel::headerData(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*/
244bool QTransposeProxyModel::setHeaderData(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*/
255bool 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*/
267QSize 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*/
279QMap<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*/
291QModelIndex 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*/
303QModelIndex 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*/
315QModelIndex 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*/
327QModelIndex 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*/
339bool 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*/
351bool 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*/
363bool 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*/
376bool 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*/
388bool 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*/
400bool 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*/
414void QTransposeProxyModel::sort(int column, Qt::SortOrder order)
415{
416 Q_UNUSED(column);
417 Q_UNUSED(order);
418 return;
419}
420
421QT_END_NAMESPACE
422
423#include "moc_qtransposeproxymodel.cpp"
424

source code of qtbase/src/corelib/itemmodels/qtransposeproxymodel.cpp