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

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