File indexing completed on 2024-04-14 04:36:56

0001 /***************************************************************************
0002  *   Copyright (C) 2017 by Emmanuel Lepage Vallee                          *
0003  *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@kde.org>             *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 3 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0017  **************************************************************************/
0018 #include "modelviewtester.h"
0019 
0020 #include <QtCore/QDebug>
0021 #include <QMetaObject>
0022 #include <QMetaMethod>
0023 
0024 #include <functional>
0025 
0026 #define DO(slot) steps << QString(#slot) ;
0027 
0028 struct ModelViewTesterItem
0029 {
0030     ModelViewTesterItem() {}
0031     ModelViewTesterItem(ModelViewTesterItem* p, const QHash<int, QVariant>& vals, int i = -1);
0032 
0033     int m_Index {0};;
0034     ModelViewTesterItem* m_pParent {nullptr};
0035     QHash<int, QVariant> m_hValues;
0036     QVector<ModelViewTesterItem*> m_lChildren;
0037 };
0038 
0039 ModelViewTester::ModelViewTester(QObject* parent) : QAbstractItemModel(parent)
0040 {
0041     m_pRoot = new ModelViewTesterItem;
0042 
0043     // Append
0044     DO(appendSimpleRoot);
0045     DO(appendSimpleRoot);
0046     DO(appendSimpleRoot);
0047     DO(appendSimpleRoot);
0048     DO(appendSimpleRoot);
0049 
0050     DO(appendRootChildren);
0051     DO(appendRootChildren);
0052     DO(appendRootChildren);
0053     DO(appendRootChildren);
0054 
0055     // Prepend
0056     DO(prependSimpleRoot);
0057 
0058     // Move
0059     DO(moveRootToFront);
0060     DO(moveChildByOne);
0061     DO(moveChildByParent);
0062     DO(moveToGrandChildren);
0063     //TODO moveFirst
0064     //TODO moveLast
0065 
0066     // Insert
0067     DO(insertRoot);
0068     DO(insertFirst);
0069     DO(insertChild);
0070 
0071     // Remove
0072     DO(removeRoot);
0073     //TODO removeMiddle
0074     //TODO removeLastChild
0075     //TODO removeWithChildren
0076     DO(resetModel);
0077 
0078     // Larger tree
0079     DO(largeFrontTree);
0080     DO(removeLargeTree);
0081     DO(removeLargeTree2);
0082     DO(largeFrontTree2);
0083     DO(removeLargeTree2);
0084     DO(removeLargeTree3);
0085     //TODO move multiple
0086 
0087     // Larger move (with out of view)
0088 
0089 }
0090 
0091 ModelViewTester::~ModelViewTester()
0092 {
0093 
0094 }
0095 
0096 void ModelViewTester::run() {
0097     m_pTimer->setInterval(100);
0098 
0099     QObject::connect(m_pTimer, &QTimer::timeout, this, [this]() {
0100         int methodIndex = metaObject()->indexOfMethod((steps[count]+"()").toLatin1());
0101         metaObject()->method(methodIndex).invoke(this, Qt::QueuedConnection);
0102         count++;
0103         if (count == steps.size()) {
0104             m_pTimer->stop();
0105             count = 0;
0106         }
0107     });
0108 
0109     m_pTimer->start();
0110 }
0111 
0112 bool ModelViewTester::setData( const QModelIndex& index, const QVariant &value, int role   )
0113 {
0114     Q_UNUSED(index)
0115     Q_UNUSED(value)
0116     Q_UNUSED(role)
0117     return false;
0118 }
0119 
0120 QVariant ModelViewTester::data( const QModelIndex& index, int role ) const
0121 {
0122     if (!index.isValid())
0123         return {};
0124 
0125     auto item = static_cast<ModelViewTesterItem*>(index.internalPointer());
0126 
0127     return item->m_hValues[role];
0128 }
0129 
0130 int ModelViewTester::rowCount( const QModelIndex& parent) const
0131 {
0132     if (!parent.isValid())
0133         return m_pRoot->m_lChildren.size();
0134 
0135     auto item = static_cast<ModelViewTesterItem*>(parent.internalPointer());
0136 
0137     return item->m_lChildren.size();
0138 }
0139 
0140 int ModelViewTester::columnCount( const QModelIndex& parent ) const
0141 {
0142     return parent.isValid() ? 0 : 1; //FIXME not really true
0143 }
0144 
0145 QModelIndex ModelViewTester::parent( const QModelIndex& index ) const
0146 {
0147     if (!index.isValid())
0148         return {};
0149 
0150     auto item = static_cast<ModelViewTesterItem*>(index.internalPointer());
0151 
0152     Q_ASSERT(item != m_pRoot);
0153 
0154     if (item->m_pParent == m_pRoot)
0155         return {};
0156 
0157     return createIndex(item->m_pParent->m_Index, 0, item->m_pParent);
0158 }
0159 
0160 QModelIndex ModelViewTester::index( int row, int column, const QModelIndex& parent ) const
0161 {
0162     auto parItem = parent.isValid() ?
0163         static_cast<ModelViewTesterItem*>(parent.internalPointer()): m_pRoot;
0164 
0165     if (column || row >= parItem->m_lChildren.size() || row < 0)
0166         return {};
0167 
0168     return createIndex(row, column, parItem->m_lChildren[row]);
0169 }
0170 
0171 QMimeData* ModelViewTester::mimeData( const QModelIndexList &indexes) const
0172 {
0173     Q_UNUSED(indexes)
0174     return nullptr; //TODO
0175 }
0176 
0177 bool ModelViewTester::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
0178 {
0179     Q_UNUSED(data)
0180     Q_UNUSED(action)
0181     Q_UNUSED(row)
0182     Q_UNUSED(column)
0183     Q_UNUSED(parent)
0184     return false; //TODO
0185 }
0186 
0187 QHash<int,QByteArray> ModelViewTester::roleNames() const
0188 {
0189     return {
0190         {Qt::DisplayRole, "display"},
0191         {Qt::UserRole, "offset"}
0192     };
0193 }
0194 
0195 // Qt::ItemFlags ModelViewTester::flags( const QModelIndex& index) const
0196 
0197 ModelViewTesterItem::ModelViewTesterItem(ModelViewTesterItem* p, const QHash<int, QVariant>& vals, int i) :
0198     m_pParent(p), m_hValues(vals)
0199 {
0200     if (i == -1) {
0201         m_Index = p->m_lChildren.size();
0202         p->m_lChildren << this;
0203     }
0204     else {
0205         m_Index = i;
0206 
0207         p->m_lChildren.insert(i, this);
0208         for (int j=i+1; j < p->m_lChildren.size(); j++)
0209             p->m_lChildren[j]->m_Index++;
0210     }
0211 }
0212 
0213 void ModelViewTester::prependSimpleRoot()
0214 {
0215     beginInsertRows({}, 0, 0);
0216 
0217     QHash<int, QVariant> vals = {
0218         {Qt::DisplayRole, "prep root 1"},
0219         {Qt::UserRole, 0}
0220     };
0221 
0222     new ModelViewTesterItem(m_pRoot, vals, 0);
0223 
0224     endInsertRows();
0225     beginInsertRows({}, 1, 1);
0226 
0227     vals = {
0228         {Qt::DisplayRole, "prep root 2"},
0229         {Qt::UserRole, 0}
0230     };
0231 
0232     new ModelViewTesterItem(m_pRoot, vals, 1);
0233 
0234     endInsertRows();
0235     beginInsertRows({}, 0, 0);
0236 
0237     vals = {
0238         {Qt::DisplayRole, "prep root 0"},
0239         {Qt::UserRole, 0}
0240     };
0241 
0242     new ModelViewTesterItem(m_pRoot, vals, 0);
0243 
0244     endInsertRows();
0245 }
0246 
0247 void ModelViewTester::appendSimpleRoot()
0248 {
0249     beginInsertRows({}, m_pRoot->m_lChildren.size(), m_pRoot->m_lChildren.size());
0250 
0251     QHash<int, QVariant> vals = {
0252         {Qt::DisplayRole, "root "+QString::number(m_pRoot->m_lChildren.size())},
0253         {Qt::UserRole, 0}
0254     };
0255 
0256     new ModelViewTesterItem(m_pRoot, vals);
0257 
0258     endInsertRows();
0259 }
0260 
0261 void ModelViewTester::appendRootChildren()
0262 {
0263     auto par = m_pRoot->m_lChildren[1];
0264     beginInsertRows(index(1,0), par->m_lChildren.size(), par->m_lChildren.size());
0265 
0266     QHash<int, QVariant> vals = {
0267         {Qt::DisplayRole, "child "+QString::number(par->m_lChildren.size())},
0268         {Qt::UserRole, 10}
0269     };
0270 
0271     new ModelViewTesterItem(par, vals);
0272 
0273     endInsertRows();
0274 }
0275 
0276 void ModelViewTester::moveRootToFront()
0277 {
0278     beginMoveRows({}, 2,2, {}, 0);
0279 
0280     auto elem = m_pRoot->m_lChildren[2];
0281 
0282     m_pRoot->m_lChildren.remove(2);
0283     m_pRoot->m_lChildren.insert(0, elem);
0284 
0285     for (int i =0; i < m_pRoot->m_lChildren.size(); i++)
0286         m_pRoot->m_lChildren[i]->m_Index = i;
0287 
0288     endMoveRows();
0289 }
0290 
0291 void ModelViewTester::moveChildByOne()
0292 {
0293     auto parentIdx = index(4,0);
0294 
0295     beginMoveRows(parentIdx, 2,2, parentIdx, 1);
0296 
0297     auto elem = m_pRoot->m_lChildren[4]->m_lChildren[2];
0298 
0299     m_pRoot->m_lChildren[4]->m_lChildren.remove(2);
0300     m_pRoot->m_lChildren[4]->m_lChildren.insert(0, elem);
0301 
0302     for (int i =0; i < m_pRoot->m_lChildren[4]->m_lChildren.size(); i++)
0303         m_pRoot->m_lChildren[4]->m_lChildren[i]->m_Index = i;
0304 
0305     endMoveRows();
0306 }
0307 
0308 void ModelViewTester::moveChildByParent()
0309 {
0310     auto elem = m_pRoot->m_lChildren[4]->m_lChildren[3];
0311 
0312     auto oldParentIdx = index(4,0);
0313     auto newParentIdx = index(m_pRoot->m_lChildren.size()-1,0);
0314 
0315     beginMoveRows(oldParentIdx, 3,3, newParentIdx, 0);
0316 
0317     m_pRoot->m_lChildren[4]->m_lChildren.remove(3);
0318     m_pRoot->m_lChildren[m_pRoot->m_lChildren.size()-1]->m_lChildren.insert(0, elem);
0319 
0320     elem->m_Index = 0;
0321     endMoveRows();
0322 }
0323 
0324 void ModelViewTester::moveToGrandChildren()
0325 {
0326     auto elem1 = m_pRoot->m_lChildren[1];
0327     auto elem2 = m_pRoot->m_lChildren[2];
0328     auto newPar = m_pRoot->m_lChildren[4]->m_lChildren[2];
0329     auto newParentIdx = createIndex(newPar->m_Index, 0, newPar);
0330 
0331     beginMoveRows({}, 1,2, newParentIdx, 0);
0332 
0333     elem1->m_pParent = newPar;
0334     elem2->m_pParent = newPar;
0335 
0336     elem1->m_hValues = {
0337         {Qt::DisplayRole, elem1->m_hValues[0].toString()+" gc"},
0338         {Qt::UserRole, 20}
0339     };
0340     elem2->m_hValues = {
0341         {Qt::DisplayRole, elem2->m_hValues[0].toString()+" gc"},
0342         {Qt::UserRole, 20}
0343     };
0344 
0345     m_pRoot->m_lChildren.remove(1);
0346     m_pRoot->m_lChildren.remove(1);
0347 
0348     newPar->m_lChildren << elem1 << elem2;
0349 
0350     for (int i =0; i < newPar->m_lChildren.size(); i++)
0351         newPar->m_lChildren[i]->m_Index = i;
0352 
0353     for (int i =0; i < m_pRoot->m_lChildren.size(); i++)
0354         m_pRoot->m_lChildren[i]->m_Index = i;
0355 
0356     endMoveRows();
0357 
0358     Q_EMIT dataChanged(index(0, 0, newParentIdx), index(1, 0, newParentIdx));
0359 }
0360 
0361 
0362 void ModelViewTester::insertRoot()
0363 {
0364     beginInsertRows({}, 1, 1);
0365 
0366     QHash<int, QVariant> vals = {
0367         {Qt::DisplayRole, "inserted root 1"},
0368         {Qt::UserRole, 0}
0369     };
0370 
0371     new ModelViewTesterItem(m_pRoot, vals, 1);
0372 
0373     endInsertRows();
0374 }
0375 
0376 void ModelViewTester::insertFirst()
0377 {
0378     beginInsertRows({}, 0, 0);
0379 
0380     QHash<int, QVariant> vals = {
0381         {Qt::DisplayRole, "inserted root 0"},
0382         {Qt::UserRole, 0}
0383     };
0384 
0385     new ModelViewTesterItem(m_pRoot, vals, 0);
0386 
0387     endInsertRows();
0388 }
0389 
0390 void ModelViewTester::insertChild()
0391 {
0392     auto newPar = m_pRoot->m_lChildren[4];
0393     auto parIdx = createIndex(4, 0, newPar);
0394 
0395     Q_ASSERT(parIdx.isValid());
0396 
0397     beginInsertRows(parIdx, 0, 0);
0398 
0399     QHash<int, QVariant> vals = {
0400         {Qt::DisplayRole, "inserted child 0"},
0401         {Qt::UserRole, 0}
0402     };
0403 
0404     new ModelViewTesterItem(newPar, vals, 0);
0405 
0406     endInsertRows();
0407 }
0408 
0409 void ModelViewTester::removeRoot()
0410 {
0411     beginRemoveRows({}, 0, 0);
0412 
0413     QHash<int, QVariant> vals = {
0414         {Qt::DisplayRole, "inserted root 0"},
0415         {Qt::UserRole, 0}
0416     };
0417 
0418     m_pRoot->m_lChildren.remove(1);
0419 
0420     for (int i =0; i < m_pRoot->m_lChildren.size(); i++)
0421         m_pRoot->m_lChildren[i]->m_Index = i;
0422 
0423     endRemoveRows();
0424 }
0425 
0426 void ModelViewTester::resetModel()
0427 {
0428     beginResetModel();
0429     qDeleteAll(m_pRoot->m_lChildren);
0430     m_pRoot->m_lChildren.clear();
0431     endResetModel();
0432 }
0433 
0434 void ModelViewTester::largeFrontTree()
0435 {
0436     for (int i = 0; i < 100; i++) {
0437         beginInsertRows({}, 0, 0);
0438 
0439         QHash<int, QVariant> vals = {
0440             {Qt::DisplayRole, "inserted root 1"},
0441             {Qt::UserRole, 0}
0442         };
0443 
0444         auto itm = new ModelViewTesterItem(m_pRoot, vals, 0);
0445 
0446         endInsertRows();
0447 
0448         auto p = createIndex(0, 0, itm);
0449 
0450         beginInsertRows(p, 0, 4);
0451         for (int j = 0; j < 5; j++) {
0452             QHash<int, QVariant> vals2 = {
0453                 {Qt::DisplayRole, "children "+QString::number(j)},
0454                 {Qt::UserRole, 0}
0455             };
0456 
0457             new ModelViewTesterItem(itm, vals2, 0);
0458         }
0459         endInsertRows();
0460     }
0461 }
0462 
0463 // Test removing elements when some are out of view
0464 void ModelViewTester::removeLargeTree()
0465 {
0466     for (int i = 0; i < 100; i++) {
0467         auto parent = m_pRoot->m_lChildren[i];
0468         auto idx = createIndex(i, 0, parent);
0469 
0470         beginRemoveRows(idx, 3, 3);
0471         delete parent->m_lChildren[3];
0472         parent->m_lChildren.remove(3);
0473         endRemoveRows();
0474     }
0475 }
0476 
0477 // Test removing multiple item at once with out-of-view
0478 void ModelViewTester::removeLargeTree2()
0479 {
0480     for (int i = 0; i < 100; i++) {
0481         auto parent = m_pRoot->m_lChildren[i];
0482         const int s =  parent->m_lChildren.size();
0483         auto idx = createIndex(i, 0, parent);
0484 
0485         beginRemoveRows(idx, 0, s);
0486         for (int j = 0; j < s; j++)
0487             delete parent->m_lChildren[j];
0488         parent->m_lChildren.clear();
0489         endRemoveRows();
0490     }
0491 }
0492 
0493 // Test removing out-of-view item until the viewport is empty
0494 void ModelViewTester::removeLargeTree3()
0495 {
0496     while (m_pRoot->m_lChildren.size()) {
0497         const int pos = m_pRoot->m_lChildren.size()/2;
0498         beginRemoveRows({}, pos, pos);
0499         delete m_pRoot->m_lChildren[pos];
0500         m_pRoot->m_lChildren.remove(pos);
0501         endRemoveRows();
0502     }
0503 }
0504 
0505 // Insert more items that can fit in the view
0506 void ModelViewTester::largeFrontTree2()
0507 {
0508     for (int i = 0; i < 100; i++) {
0509         auto parent = m_pRoot->m_lChildren[i];
0510         auto idx = createIndex(i, 0, parent);
0511 
0512         beginInsertRows(idx, 0, 19);
0513         for (int j = 0; j < 20; j++) {
0514             QHash<int, QVariant> vals = {
0515                 {Qt::DisplayRole, "children v2 "+QString::number(j)},
0516                 {Qt::UserRole, 0}
0517             };
0518 
0519             new ModelViewTesterItem(parent, vals, 0);
0520         }
0521         endInsertRows();
0522     }
0523 }