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 
46 QT_BEGIN_NAMESPACE
47 
uncheckedMapToSource(const QModelIndex & proxyIndex) const48 QModelIndex QTransposeProxyModelPrivate::uncheckedMapToSource(const QModelIndex &proxyIndex) const
49 {
50     if (!model || !proxyIndex.isValid())
51         return QModelIndex();
52     if (proxyIndex.internalPointer())
53         return model->createIndex(proxyIndex.column(), proxyIndex.row(), proxyIndex.internalPointer());
54     return model->index(proxyIndex.column(), proxyIndex.row());
55 }
56 
uncheckedMapFromSource(const QModelIndex & sourceIndex) const57 QModelIndex QTransposeProxyModelPrivate::uncheckedMapFromSource(const QModelIndex &sourceIndex) const
58 {
59     if (!model || !sourceIndex.isValid())
60         return QModelIndex();
61     Q_Q(const QTransposeProxyModel);
62     return q->createIndex(sourceIndex.column(), sourceIndex.row(), sourceIndex.internalPointer());
63 }
64 
onLayoutChanged(const QList<QPersistentModelIndex> & parents,QAbstractItemModel::LayoutChangeHint hint)65 void QTransposeProxyModelPrivate::onLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
66 {
67     Q_Q(QTransposeProxyModel);
68     QModelIndexList toList;
69     toList.reserve(layoutChangePersistentIndexes.size());
70     for (const QPersistentModelIndex &persistIdx : qAsConst(layoutChangePersistentIndexes))
71         toList << q->mapFromSource(persistIdx);
72     q->changePersistentIndexList(layoutChangeProxyIndexes, toList);
73     layoutChangeProxyIndexes.clear();
74     layoutChangePersistentIndexes.clear();
75     QList<QPersistentModelIndex> proxyParents;
76     proxyParents.reserve(parents.size());
77     for (const QPersistentModelIndex &srcParent : parents)
78         proxyParents << q->mapFromSource(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(proxyParents, proxyHint);
85 }
86 
onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> & parents,QAbstractItemModel::LayoutChangeHint hint)87 void 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(proxyPersistentIndexes.size());
94     layoutChangePersistentIndexes.reserve(proxyPersistentIndexes.size());
95     for (const QModelIndex &proxyPersistentIndex : proxyPersistentIndexes) {
96         layoutChangeProxyIndexes << proxyPersistentIndex;
97         Q_ASSERT(proxyPersistentIndex.isValid());
98         const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
99         Q_ASSERT(srcPersistentIndex.isValid());
100         layoutChangePersistentIndexes << srcPersistentIndex;
101     }
102     QList<QPersistentModelIndex> proxyParents;
103     proxyParents.reserve(parents.size());
104     for (auto& srcParent : parents)
105         proxyParents << q->mapFromSource(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(proxyParents, proxyHint);
112 }
113 
onDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles)114 void QTransposeProxyModelPrivate::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
115 {
116     Q_Q(QTransposeProxyModel);
117     emit q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight), roles);
118 }
119 
onHeaderDataChanged(Qt::Orientation orientation,int first,int last)120 void QTransposeProxyModelPrivate::onHeaderDataChanged(Qt::Orientation orientation, int first, int last)
121 {
122     Q_Q(QTransposeProxyModel);
123     emit q->headerDataChanged(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, first, last);
124 }
125 
onColumnsAboutToBeInserted(const QModelIndex & parent,int first,int last)126 void QTransposeProxyModelPrivate::onColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
127 {
128     Q_Q(QTransposeProxyModel);
129     q->beginInsertRows(q->mapFromSource(parent), first, last);
130 }
131 
onColumnsAboutToBeRemoved(const QModelIndex & parent,int first,int last)132 void QTransposeProxyModelPrivate::onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
133 {
134     Q_Q(QTransposeProxyModel);
135     q->beginRemoveRows(q->mapFromSource(parent), first, last);
136 }
137 
onColumnsAboutToBeMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationColumn)138 void QTransposeProxyModelPrivate::onColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn)
139 {
140     Q_Q(QTransposeProxyModel);
141     q->beginMoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destinationParent), destinationColumn);
142 }
143 
onRowsAboutToBeInserted(const QModelIndex & parent,int first,int last)144 void QTransposeProxyModelPrivate::onRowsAboutToBeInserted(const QModelIndex &parent, int first, int last)
145 {
146     Q_Q(QTransposeProxyModel);
147     q->beginInsertColumns(q->mapFromSource(parent), first, last);
148 }
149 
onRowsAboutToBeRemoved(const QModelIndex & parent,int first,int last)150 void QTransposeProxyModelPrivate::onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
151 {
152     Q_Q(QTransposeProxyModel);
153     q->beginRemoveColumns(q->mapFromSource(parent), first, last);
154 }
155 
onRowsAboutToBeMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationRow)156 void QTransposeProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
157 {
158     Q_Q(QTransposeProxyModel);
159     q->beginMoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destinationParent), 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 */
QTransposeProxyModel(QObject * parent)175 QTransposeProxyModel::QTransposeProxyModel(QObject* parent)
176     : QAbstractProxyModel(*new QTransposeProxyModelPrivate, parent)
177 {}
178 
179 /*!
180     Destructs the proxy model.
181 */
182 QTransposeProxyModel::~QTransposeProxyModel() = default;
183 
184 /*!
185     \internal
186 */
QTransposeProxyModel(QTransposeProxyModelPrivate & dd,QObject * parent)187 QTransposeProxyModel::QTransposeProxyModel(QTransposeProxyModelPrivate &dd, QObject *parent)
188     : QAbstractProxyModel(dd, parent)
189 {}
190 
191 /*!
192     \reimp
193 */
setSourceModel(QAbstractItemModel * newSourceModel)194 void 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(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(d->model, &QAbstractItemModel::modelAboutToBeReset, this, &QTransposeProxyModel::beginResetModel),
210             connect(d->model, &QAbstractItemModel::modelReset, this, &QTransposeProxyModel::endResetModel),
211             connect(d->model, &QAbstractItemModel::dataChanged, this, std::bind(&QTransposeProxyModelPrivate::onDataChanged, d, _1, _2, _3)),
212             connect(d->model, &QAbstractItemModel::headerDataChanged, this, std::bind(&QTransposeProxyModelPrivate::onHeaderDataChanged, d, _1, _2, _3)),
213             connect(d->model, &QAbstractItemModel::columnsAboutToBeInserted, this, std::bind(&QTransposeProxyModelPrivate::onColumnsAboutToBeInserted, d, _1, _2, _3)),
214             connect(d->model, &QAbstractItemModel::columnsAboutToBeMoved, this, std::bind(&QTransposeProxyModelPrivate::onColumnsAboutToBeMoved, d, _1, _2, _3, _4, _5)),
215             connect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved, this, std::bind(&QTransposeProxyModelPrivate::onColumnsAboutToBeRemoved, d, _1, _2, _3)),
216             connect(d->model, &QAbstractItemModel::columnsInserted, this, &QTransposeProxyModel::endInsertRows),
217             connect(d->model, &QAbstractItemModel::columnsRemoved, this, &QTransposeProxyModel::endRemoveRows),
218             connect(d->model, &QAbstractItemModel::columnsMoved, this, &QTransposeProxyModel::endMoveRows),
219             connect(d->model, &QAbstractItemModel::rowsAboutToBeInserted, this, std::bind(&QTransposeProxyModelPrivate::onRowsAboutToBeInserted, d, _1, _2, _3)),
220             connect(d->model, &QAbstractItemModel::rowsAboutToBeMoved, this, std::bind(&QTransposeProxyModelPrivate::onRowsAboutToBeMoved, d, _1, _2, _3, _4, _5)),
221             connect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved, this, std::bind(&QTransposeProxyModelPrivate::onRowsAboutToBeRemoved, d, _1, _2, _3)),
222             connect(d->model, &QAbstractItemModel::rowsInserted, this, &QTransposeProxyModel::endInsertColumns),
223             connect(d->model, &QAbstractItemModel::rowsRemoved, this, &QTransposeProxyModel::endRemoveColumns),
224             connect(d->model, &QAbstractItemModel::rowsMoved, this, &QTransposeProxyModel::endMoveColumns),
225             connect(d->model, &QAbstractItemModel::layoutAboutToBeChanged, this, std::bind(&QTransposeProxyModelPrivate::onLayoutAboutToBeChanged, d, _1, _2)),
226             connect(d->model, &QAbstractItemModel::layoutChanged, this, std::bind(&QTransposeProxyModelPrivate::onLayoutChanged, d, _1, _2))
227         };
228     }
229     endResetModel();
230 }
231 
232 /*!
233     \reimp
234 */
rowCount(const QModelIndex & parent) const235 int 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(mapToSource(parent));
242 }
243 
244 /*!
245     \reimp
246 */
columnCount(const QModelIndex & parent) const247 int 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(mapToSource(parent));
254 }
255 
256 /*!
257     \reimp
258 */
headerData(int section,Qt::Orientation orientation,int role) const259 QVariant 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 == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, role);
265 }
266 
267 /*!
268     \reimp
269 */
setHeaderData(int section,Qt::Orientation orientation,const QVariant & value,int role)270 bool 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 == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, value, role);
276 }
277 
278 /*!
279     \reimp
280 */
setItemData(const QModelIndex & index,const QMap<int,QVariant> & roles)281 bool 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(mapToSource(index), roles);
288 }
289 
290 /*!
291     \reimp
292 */
span(const QModelIndex & index) const293 QSize 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(mapToSource(index)).transposed();
300 }
301 
302 /*!
303     \reimp
304 */
itemData(const QModelIndex & index) const305 QMap<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(mapToSource(index));
312 }
313 
314 /*!
315     \reimp
316 */
mapFromSource(const QModelIndex & sourceIndex) const317 QModelIndex 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 */
mapToSource(const QModelIndex & proxyIndex) const329 QModelIndex 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 */
parent(const QModelIndex & index) const341 QModelIndex 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(d->uncheckedMapToSource(index).parent());
348 }
349 
350 /*!
351     \reimp
352 */
index(int row,int column,const QModelIndex & parent) const353 QModelIndex 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(d->model->index(column, row, mapToSource(parent)));
360 }
361 
362 /*!
363     \reimp
364 */
insertRows(int row,int count,const QModelIndex & parent)365 bool 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(row, count, mapToSource(parent));
372 }
373 
374 /*!
375     \reimp
376 */
removeRows(int row,int count,const QModelIndex & parent)377 bool 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(row, count, mapToSource(parent));
384 }
385 
386 /*!
387     \reimp
388 */
moveRows(const QModelIndex & sourceParent,int sourceRow,int count,const QModelIndex & destinationParent,int destinationChild)389 bool 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(mapToSource(sourceParent), sourceRow, count, mapToSource(destinationParent), destinationChild);
397 }
398 
399 /*!
400     \reimp
401 */
insertColumns(int column,int count,const QModelIndex & parent)402 bool 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(column, count, mapToSource(parent));
409 }
410 
411 /*!
412     \reimp
413 */
removeColumns(int column,int count,const QModelIndex & parent)414 bool 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(column, count, mapToSource(parent));
421 }
422 
423 /*!
424     \reimp
425 */
moveColumns(const QModelIndex & sourceParent,int sourceRow,int count,const QModelIndex & destinationParent,int destinationChild)426 bool 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(mapToSource(sourceParent), sourceRow, count, mapToSource(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 */
sort(int column,Qt::SortOrder order)440 void QTransposeProxyModel::sort(int column, Qt::SortOrder order)
441 {
442     Q_UNUSED(column)
443     Q_UNUSED(order)
444     return;
445 }
446 
447 QT_END_NAMESPACE
448 
449 #include "moc_qtransposeproxymodel.cpp"
450