File indexing completed on 2024-04-14 04:45:14

0001 /*
0002     SPDX-FileCopyrightText: 2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2009 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
0004     SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
0005     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "k3bmetaitemmodel.h"
0011 // this header is required to return the ItemTypeRole correctly for the
0012 // places items
0013 #include "k3bdataprojectmodel.h"
0014 
0015 #include "k3bcore.h"
0016 
0017 #include <list>
0018 
0019 #include <QDebug>
0020 #include <QMimeData>
0021 #include <QVector>
0022 #include <QIcon>
0023 
0024 // IDEA: K3b::MetaItemModel::placeData( int row, int column );
0025 
0026 #ifdef __clang__
0027 #define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
0028 #else
0029 #define CLANG_ANALYZER_NORETURN
0030 #endif
0031 
0032 namespace {
0033     class Place;
0034 
0035     class Node {
0036     public:
0037         Node()
0038             : parent( 0 ),
0039               m_place( 0 ) {
0040 
0041         }
0042 
0043         virtual ~Node() {
0044             qDeleteAll(children);
0045         }
0046 
0047         virtual bool isPlace() const { return false; }
0048         virtual Place* place() const;
0049         void setPlace( Place* place );
0050         virtual QAbstractItemModel* model() const;
0051         Node* findNodeForOriginalIndex( const QModelIndex& index );
0052         Node* createNodeForOriginalIndex( const QModelIndex& index );
0053         Node* getChildNode( const QModelIndex& originalIndex );
0054         void updateChildren();
0055         void reset();
0056         bool supportsMimeData( const QMimeData* data ) const;
0057 
0058         // the parent node, 0 for Place instances
0059         Node* parent;
0060 
0061         // the model index as returned by the original model, not
0062         // to be used in the public API (exception: mapToSubModel())
0063         QPersistentModelIndex originalModelIndex;
0064 
0065         // the child nodes
0066         QVector<Node*> children;
0067 
0068     private:
0069         // the root element of this node
0070         Place* m_place;
0071     };
0072 
0073 
0074     class Place : public Node {
0075     public:
0076         Place( QAbstractItemModel* model )
0077             : m_model( model ) {
0078         }
0079 
0080         Place* place() const override;
0081         bool isPlace() const override { return true; }
0082         QAbstractItemModel* model() const override;
0083 
0084         // a name and icon for the place (used for display)
0085         // FIXME: better use something like placeData(...)
0086         QString name;
0087         QIcon icon;
0088 
0089         // the row index of this node
0090         int row;
0091 
0092         bool flat;
0093 
0094     private:
0095         QAbstractItemModel* m_model;
0096     };
0097 
0098 
0099     QAbstractItemModel* Node::model() const
0100     {
0101         return place()->model();
0102     }
0103 
0104 
0105     QAbstractItemModel* Place::model() const
0106     {
0107         return m_model;
0108     }
0109 
0110 
0111     Place* Node::place() const
0112     {
0113         return m_place;
0114     }
0115 
0116 
0117     void Node::setPlace( Place* place )
0118     {
0119         m_place = place;
0120     }
0121 
0122 
0123     Place* Place::place() const
0124     {
0125         return const_cast<Place*>( this );
0126     }
0127 
0128 
0129     void Node::updateChildren()
0130     {
0131         // only update children when there is no items in the list
0132         if ( children.isEmpty() ) {
0133             // TODO: this is kind of evil since indexes store pointers to the nodes
0134             //qDebug() << "resizing children from" << children.size() << "to" << rows;
0135 
0136             int rows = model()->rowCount( originalModelIndex );
0137             for ( int i = 0; i < rows; ++i ) {
0138                 Node *node = new Node();
0139                 node->setPlace( place() );
0140                 node->parent = this;
0141                 node->originalModelIndex = model()->index( i, 0, originalModelIndex );
0142                 children.append(node);
0143             }
0144         }
0145     }
0146 
0147 
0148     Node* Node::getChildNode( const QModelIndex& originalIndex )
0149     {
0150         updateChildren();
0151 
0152         Q_ASSERT(children.size() > originalIndex.row());
0153 
0154         Node* node = children[originalIndex.row()];
0155         return node;
0156     }
0157 
0158 
0159     Node* Node::findNodeForOriginalIndex( const QModelIndex& index )
0160     {
0161         if ( originalModelIndex == index ) {
0162             return this;
0163         }
0164 
0165         for ( int i = 0; i < children.count(); ++i ) {
0166             if ( Node* node = children[i]->findNodeForOriginalIndex( index ) )
0167                 return node;
0168         }
0169 
0170         return 0;
0171     }
0172 
0173     static void K3b_ASSERT(bool test) CLANG_ANALYZER_NORETURN { Q_ASSERT(test); }
0174 
0175     Node* Node::createNodeForOriginalIndex( const QModelIndex& index )
0176     {
0177         if ( !index.isValid() && isPlace() ) {
0178             return this;
0179         }
0180 
0181         K3b_ASSERT(index.isValid());
0182 
0183         // all the node mapping is done on the first col, so make sure we use
0184         // an index on the first col
0185         // 
0186         // A valid index belongs to a model, and has non-negative row and column numbers
0187         // so index.model() is NOT nullptr if index.isValid()
0188         QModelIndex firstColIndex = index.model()->index(index.row(), 0, index.parent());
0189         Node* node = findNodeForOriginalIndex( firstColIndex );
0190         if ( !node ) {
0191             Node* parentNode = createNodeForOriginalIndex( firstColIndex.parent() );
0192             node = parentNode->getChildNode( firstColIndex );
0193         }
0194 
0195         return node;
0196     }
0197 
0198 
0199     void Node::reset()
0200     {
0201         qDeleteAll(children);
0202         children.clear();
0203     }
0204 
0205     bool Node::supportsMimeData( const QMimeData* data ) const
0206     {
0207         QStringList supportedFormats = model()->mimeTypes();
0208         QStringList formats = data->formats();
0209         Q_FOREACH( const QString& format, formats )
0210         {
0211             if( supportedFormats.indexOf( format ) >= 0 )
0212                 return true;
0213         }
0214         return false;
0215     }
0216 } // namespace
0217 
0218 
0219 
0220 class K3b::MetaItemModel::Private
0221 {
0222 public:
0223     Private( MetaItemModel* model ) : q( model ) {}
0224 
0225     Place* placeForModel( const QAbstractItemModel* model ) {
0226         for (auto &place : places) {
0227             if ( place.model() == model ) {
0228                 return &place;
0229             }
0230         }
0231         return 0;
0232     }
0233 
0234     void updatePlaceRows() {
0235         int row = 0;
0236         auto end = places.end();
0237         for ( auto it = places.begin();
0238               it != end; ++it ) {
0239             it->row = row;
0240             if ( it->flat ) {
0241                 row += it->model()->rowCount( QModelIndex() );
0242             }
0243             else {
0244                 ++row;
0245             }
0246         }
0247     }
0248 
0249     /**
0250      * root nodes are all non-flat places +
0251      * all root items from flat places.
0252      */
0253     Node* getRootNode( int row ) {
0254         int i = 0;
0255         auto end = places.end();
0256         for ( auto it = places.begin();
0257               it != end; ++it ) {
0258             if ( it->flat ) {
0259                 it->updateChildren();
0260                 if ( i + it->children.count() > row ) {
0261                     return it->children[row-i];
0262                 }
0263                 else {
0264                     i += it->children.count();
0265                 }
0266             }
0267             else if ( row == i ) {
0268                 return &( *it );
0269             }
0270             else {
0271                 ++i;
0272             }
0273         }
0274 
0275         return 0;
0276     }
0277 
0278     int getRootNodeRow(Node *node)
0279     {
0280         int row = 0;
0281         auto end = places.end();
0282         for ( auto it = places.begin();
0283               it != end; ++it ) {
0284             if (node->isPlace() && node->model() == it->model())
0285                 return row;
0286 
0287             if ( it->flat ) {
0288                 it->updateChildren();
0289                 for (int i = 0; i < it->children.count(); ++i)
0290                 {
0291                     if (!node->isPlace() && it->children[i]->originalModelIndex == node->originalModelIndex)
0292                         return row;
0293 
0294                     ++row;
0295                 }
0296             }
0297             else
0298                 ++row;
0299         }
0300 
0301         return -1;
0302     }
0303     /**
0304      * returns the node pointer for the given index.
0305      * This makes it easier to handle multiple column
0306      */
0307     Node* nodeForIndex( const QModelIndex &index )
0308     {
0309         // all indexes store the node in their internal pointers
0310         if( index.isValid() && index.model() == q )
0311             return static_cast<Node*>(index.internalPointer());
0312         else
0313             return 0;
0314     }
0315 
0316     /**
0317      * returns an index from the source model for the given index
0318      */
0319     QModelIndex sourceIndex( const QModelIndex &index )
0320     {
0321         Node *node = nodeForIndex(index);
0322         if (!node || node->isPlace())
0323             return QModelIndex();
0324 
0325         return node->model()->index( node->originalModelIndex.row(), index.column(), node->originalModelIndex.parent() );
0326     }
0327 
0328     std::list<Place> places;
0329     MetaItemModel* q;
0330 };
0331 
0332 
0333 
0334 K3b::MetaItemModel::MetaItemModel( QObject* parent )
0335     : QAbstractItemModel( parent ),
0336       d( new Private( this ) )
0337 {
0338 }
0339 
0340 
0341 K3b::MetaItemModel::~MetaItemModel()
0342 {
0343     delete d;
0344 }
0345 
0346 
0347 QModelIndex K3b::MetaItemModel::indexForSubModel( QAbstractItemModel* model ) const
0348 {
0349     if( !d->places.empty() ) {
0350         if( Place* place = d->placeForModel( model ) )
0351             return createIndex( place->row, 0, place );
0352     }
0353     return QModelIndex();
0354 }
0355 
0356 
0357 QAbstractItemModel* K3b::MetaItemModel::subModelForIndex( const QModelIndex& index ) const
0358 {
0359     if( Node* node = d->nodeForIndex( index ) )
0360         return node->model();
0361     else
0362         return 0;
0363 }
0364 
0365 
0366 QModelIndex K3b::MetaItemModel::mapToSubModel( const QModelIndex& index ) const
0367 {
0368     if ( index.isValid() ) {
0369         Q_ASSERT( index.model() == this );
0370         return d->sourceIndex( index );
0371     }
0372     else {
0373         return QModelIndex();
0374     }
0375 }
0376 
0377 
0378 QModelIndex K3b::MetaItemModel::mapFromSubModel( const QModelIndex& index ) const
0379 {
0380     if ( index.isValid() ) {
0381         Place *place = d->placeForModel( index.model() );
0382         Node* node = place->createNodeForOriginalIndex( index );
0383         Q_ASSERT( node );
0384 
0385         // if the place is not flat, or the parent index is valid
0386         // we just have to return the index for the row and column
0387         if ( !place->flat || index.parent().isValid() )
0388             return createIndex( index.row(), index.column(), node );
0389         else {
0390             // now if the place is flat and the parent is not valid,
0391             // we have to adjust the row according to its position in the
0392             // tree
0393             int row = d->getRootNodeRow( node );
0394             if ( row < 0 )
0395                 return QModelIndex();
0396             else
0397                 return createIndex( row, index.column(), node );
0398         }
0399     }
0400     else {
0401         return QModelIndex();
0402     }
0403 }
0404 
0405 
0406 int K3b::MetaItemModel::columnCount( const QModelIndex& parent ) const
0407 {
0408     QAbstractItemModel *model = subModelForIndex( parent );
0409     if (!model)
0410         return 1;
0411 
0412     return model->columnCount( mapToSubModel( parent ) );
0413 }
0414 
0415 
0416 QVariant K3b::MetaItemModel::data( const QModelIndex& index, int role ) const
0417 {
0418     if( Node* node = d->nodeForIndex( index ) ) {
0419         Q_ASSERT( node->model() );
0420         Q_ASSERT( node->isPlace() || node->originalModelIndex.isValid() );
0421 
0422         if ( node->isPlace() ) {
0423             // provide the root elements of the places
0424             switch( role ) {
0425             case Qt::DisplayRole:
0426                 return node->place()->name;
0427 
0428             case Qt::DecorationRole:
0429                 return node->place()->icon;
0430 
0431             case DataProjectModel::ItemTypeRole:
0432                 return (int) DataProjectModel::DirItemType;
0433 
0434             default:
0435                 return QVariant();
0436             }
0437         }
0438         else {
0439             return node->model()->data( mapToSubModel( index ), role );
0440         }
0441     }
0442     else {
0443         return QVariant();
0444     }
0445 }
0446 
0447 
0448 QModelIndex K3b::MetaItemModel::index( int row, int column, const QModelIndex& parent ) const
0449 {
0450     //qDebug() << row << column << parent;
0451 
0452     if ( row < 0 || column < 0 ) {
0453         return QModelIndex();
0454     }
0455 
0456     if ( parent.isValid() ) {
0457         Node* parentNode = d->nodeForIndex( parent );
0458 
0459         Q_ASSERT( parentNode->parent || parentNode->isPlace() );
0460         Q_ASSERT( parentNode->place() );
0461         Q_ASSERT( parentNode->model() );
0462 
0463         // for places the originalModelIndex is invalid
0464         QModelIndex originalIndex = parentNode->model()->index( row, 0, parentNode->originalModelIndex );
0465         Node* node = parentNode->place()->createNodeForOriginalIndex( originalIndex );
0466         return createIndex( row, column, node );
0467     }
0468     else {
0469         if ( Node* node = d->getRootNode( row ) ) {
0470             return createIndex( row, column, node );
0471         }
0472         else {
0473             return QModelIndex();
0474         }
0475     }
0476 }
0477 
0478 
0479 QModelIndex K3b::MetaItemModel::parent( const QModelIndex& index ) const
0480 {
0481     //qDebug() << "Parent of" << index;
0482     Node* node = d->nodeForIndex( index );
0483 
0484     if ( !index.isValid() || !node || node->isPlace() )
0485         return QModelIndex();
0486 
0487     Q_ASSERT( node->parent );
0488     Q_ASSERT( node->place() );
0489     Q_ASSERT( node->model() );
0490 
0491 
0492     QModelIndex origIndex = mapToSubModel( index ).parent();
0493 
0494     if ( origIndex.isValid() ) {
0495         return mapFromSubModel( origIndex );
0496     }
0497     else if ( !node->place()->flat ) {
0498         return createIndex( node->place()->row, 0, node->place() );
0499     }
0500     else {
0501         return QModelIndex();
0502     }
0503 }
0504 
0505 
0506 Qt::ItemFlags K3b::MetaItemModel::flags( const QModelIndex& index ) const
0507 {
0508     if ( index.isValid() ) {
0509         Node* node = d->nodeForIndex( index );
0510         if ( node->isPlace() ) {
0511             // flags from invalid index can be helpful when model is drop-enabled
0512             return node->model()->flags( QModelIndex() )|Qt::ItemIsSelectable|Qt::ItemIsEnabled;
0513         }
0514         else {
0515             return mapToSubModel( index ).flags();
0516         }
0517     }
0518 
0519     return QAbstractItemModel::flags( index );
0520 }
0521 
0522 
0523 bool K3b::MetaItemModel::hasChildren( const QModelIndex& parent ) const
0524 {
0525 //    qDebug() << parent;
0526 
0527     if ( parent.isValid() ) {
0528         Node* parentNode = d->nodeForIndex( parent );
0529 
0530         Q_ASSERT( parentNode->place() );
0531         Q_ASSERT( parentNode->place()->model() );
0532         Q_ASSERT( parentNode->model() );
0533 
0534         // the originalModelIndex is invalid for place nodes
0535         return parentNode->model()->hasChildren( mapToSubModel( parent ) );
0536     }
0537     else {
0538         return !d->places.empty();
0539     }
0540 }
0541 
0542 
0543 bool K3b::MetaItemModel::canFetchMore( const QModelIndex& parent ) const
0544 {
0545 //    qDebug() << parent;
0546 
0547     if ( parent.isValid() ) {
0548         Node* parentNode = d->nodeForIndex( parent );
0549         return parentNode->model()->canFetchMore( mapToSubModel( parent ) );
0550     }
0551     else {
0552         return false;
0553     }
0554 }
0555 
0556 
0557 void K3b::MetaItemModel::fetchMore( const QModelIndex& parent )
0558 {
0559 //    qDebug() << parent;
0560 
0561     if ( parent.isValid() ) {
0562         Node* parentNode = d->nodeForIndex( parent );
0563         parentNode->model()->fetchMore( mapToSubModel( parent ) );
0564     }
0565 }
0566 
0567 
0568 int K3b::MetaItemModel::rowCount( const QModelIndex& parent ) const
0569 {
0570 //    qDebug() << parent;
0571     if ( parent.column() > 0 )
0572         return 0;
0573 
0574     if ( parent.isValid() ) {
0575         Node* parentNode = d->nodeForIndex( parent );
0576         return parentNode->model()->rowCount( mapToSubModel( parent ) );
0577     }
0578     else {
0579         int cnt = 0;
0580         auto end = d->places.end();
0581         for ( auto it = d->places.begin();
0582               it != end; ++it ) {
0583             if( it->flat ) {
0584                 cnt += it->model()->rowCount( QModelIndex() );
0585             }
0586             else {
0587                 ++cnt;
0588             }
0589         }
0590         return cnt;
0591     }
0592 }
0593 
0594 
0595 bool K3b::MetaItemModel::setData( const QModelIndex& index, const QVariant& value, int role )
0596 {
0597 //    qDebug() << index;
0598 
0599     if ( index.isValid() ) {
0600         Node* node = d->nodeForIndex( index );
0601         if ( node->isPlace() ) {
0602             // cannot edit the place, should not happen anyway, see flags()
0603             return false;
0604         }
0605         else {
0606             return node->model()->setData( mapToSubModel( index ), value, role );
0607         }
0608     }
0609     else {
0610         return false;
0611     }
0612 }
0613 
0614 
0615 QStringList K3b::MetaItemModel::mimeTypes() const
0616 {
0617     QSet<QString> types;
0618     for( auto it = d->places.cbegin();
0619         it != d->places.cend(); ++it )
0620     {
0621         const QStringList &mimeTypes = it->model()->mimeTypes();
0622         types += QSet<QString>(mimeTypes.begin(), mimeTypes.end());
0623     }
0624     return types.values();
0625 }
0626 
0627 
0628 bool K3b::MetaItemModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
0629 {
0630 //    qDebug();
0631 
0632     if ( parent.isValid() ) {
0633         Node* parentNode = d->nodeForIndex( parent );
0634 
0635         if( !parentNode->supportsMimeData( data ) ) {
0636             return false;
0637         }
0638         else {
0639             // for places the originalModelIndex will be invalid
0640             return parentNode->model()->dropMimeData( data, action, row, column, mapToSubModel( parent ) );
0641         }
0642     }
0643     else if ( row >= 0 ) {
0644         Node *node = d->getRootNode(row);
0645 
0646         if( !node->supportsMimeData( data ) ) {
0647             return false;
0648         }
0649         else if (node->isPlace()) {
0650             // if the node is place, threat it like if it was being dropped on an empty space of the
0651             // original model
0652             return node->model()->dropMimeData(data, action, -1, column, QModelIndex());
0653         }
0654         else {
0655             // if it is not a place (which means the original model is a flat model)
0656             // drop like if it was being dropped in the row/col of the original index
0657             return node->model()->dropMimeData(data, action, node->originalModelIndex.row(), column, QModelIndex());
0658         }
0659     }
0660     else {
0661         return false;
0662     }
0663 }
0664 
0665 
0666 bool K3b::MetaItemModel::removeRows( int row, int count, const QModelIndex& parent )
0667 {
0668     if( parent.isValid() ) {
0669         Node* parentNode = d->nodeForIndex( parent );
0670         return parentNode->model()->removeRows( row, count, mapToSubModel( parent ) );
0671     }
0672     else if( row >= 0 ) {
0673         auto it = d->places.cbegin();
0674         std::advance(it, row);
0675         while (count-- > 0 && it != d->places.cend()) {
0676             it = d->places.erase(it);
0677         }
0678         return true;
0679     }
0680     else {
0681         return false;
0682     }
0683 }
0684 
0685 
0686 QMimeData* K3b::MetaItemModel::mimeData( const QModelIndexList& indexes ) const
0687 {
0688     if ( !indexes.isEmpty() ) {
0689         QModelIndexList origIndexes;
0690         for ( QModelIndexList::const_iterator it = indexes.constBegin();
0691               it != indexes.constEnd(); ++it ) {
0692             QModelIndex sourceIndex = mapToSubModel( *it );
0693             if ( !origIndexes.isEmpty() && sourceIndex.model() != origIndexes.first().model() ) {
0694                 qDebug() << "cannot handle indexes from different submodels yet.";
0695                 return 0;
0696             }
0697             origIndexes.append( sourceIndex );
0698         }
0699         return origIndexes.first().model()->mimeData( origIndexes );
0700     }
0701 
0702     return 0;
0703 }
0704 
0705 
0706 Qt::DropActions K3b::MetaItemModel::supportedDragActions() const
0707 {
0708     Qt::DropActions a = Qt::IgnoreAction;
0709 
0710     for (const auto &place : d->places) {
0711         a |= place.model()->supportedDragActions();
0712     }
0713     return a;
0714 }
0715 
0716 
0717 Qt::DropActions K3b::MetaItemModel::supportedDropActions() const
0718 {
0719     Qt::DropActions a = Qt::IgnoreAction;
0720 
0721     for (const auto &place : d->places) {
0722         a |= place.model()->supportedDropActions();
0723     }
0724     return a;
0725 }
0726 
0727 
0728 void K3b::MetaItemModel::addSubModel( const QString& name, const QIcon& icon, QAbstractItemModel* model, bool flat )
0729 {
0730     const int first = rowCount(QModelIndex());
0731     const int last = first + (flat ? model->rowCount() - 1 : 0);
0732 
0733     if ( first <= last )
0734         beginInsertRows( QModelIndex(), first, last );
0735 
0736     model->setParent( this );
0737 
0738     d->places.emplace_back(model);
0739 
0740     Place& place = d->places.back();
0741     place.name = name;
0742     place.icon = icon;
0743     place.flat = flat;
0744     place.updateChildren();
0745 
0746     d->updatePlaceRows();
0747 
0748     connect( place.model(), SIGNAL(modelAboutToBeReset()),
0749              this, SLOT(slotAboutToBeReset()) );
0750 
0751     connect( place.model(), SIGNAL(modelReset()),
0752              this, SLOT(slotReset()) );
0753 
0754     connect( place.model(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
0755              this, SLOT(slotRowsAboutToBeInserted(QModelIndex,int,int)) );
0756 
0757     connect( place.model(), SIGNAL(rowsInserted(QModelIndex,int,int)),
0758              this, SLOT(slotRowsInserted(QModelIndex,int,int)) );
0759 
0760     connect( place.model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
0761              this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) );
0762 
0763     connect( place.model(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
0764              this, SLOT(slotRowsRemoved(QModelIndex,int,int)) );
0765 
0766     connect( place.model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0767              this, SLOT(slotDataChanged(QModelIndex,QModelIndex)) );
0768 
0769     if ( first <= last )
0770         endInsertRows();
0771 }
0772 
0773 
0774 void K3b::MetaItemModel::removeSubModel( QAbstractItemModel* model )
0775 {
0776     // find the place index
0777     int row = 0;
0778     auto it = d->places.begin();
0779     while ( it != d->places.end() && ( *it ).model() != model ) {
0780         ++it;
0781         ++row;
0782     }
0783 
0784     Q_ASSERT( it != d->places.end() );
0785 
0786     // and simply remove the place from the list
0787     int end = row;
0788 
0789     if ( it->flat ) {
0790         end += it->children.count() - 1;
0791     }
0792 
0793 
0794     // if the model is flat and has no root rows, there is nothing not notify
0795     if ( row <= end )
0796         beginRemoveRows( QModelIndex(), row, end );
0797 
0798     d->places.erase( it );
0799     d->updatePlaceRows();
0800 
0801     if ( row <= end )
0802         endRemoveRows();
0803 
0804     // finally delete the model
0805     delete model;
0806 }
0807 
0808 
0809 void K3b::MetaItemModel::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end )
0810 {
0811     Place* place = d->placeForModel( qobject_cast<QAbstractItemModel*>( sender() ) );
0812     Q_ASSERT( place != 0 );
0813 
0814     QModelIndex newParent;
0815     int targetStart = start, targetEnd = end;
0816 
0817     // ---------- Preparing to insert ----------
0818     if( parent.isValid() ) {
0819         // search node corresponding to 'index'
0820         newParent = mapFromSubModel( parent );
0821     }
0822     else {
0823         if ( place->flat ) {
0824             targetStart += place->row;
0825             targetEnd += place->row;
0826         }
0827         else {
0828             newParent = createIndex( place->row, 0, place );
0829         }
0830     }
0831 
0832     beginInsertRows( newParent, targetStart, targetEnd );
0833 
0834     // ---------- Inserting ----------
0835     Node* parentNode;
0836     if( parent.isValid() )
0837         parentNode = place->createNodeForOriginalIndex( parent );
0838     else
0839         parentNode = place;
0840 
0841     // if the node doesn't have children yet (maybe not yet accessed)
0842     // or if it has less items than the start point of this insertion
0843     // simply load the child nodes
0844     if (start > parentNode->children.count()) {
0845         parentNode->updateChildren();
0846     }
0847     else {
0848         // insert the newly created items in the children list
0849         for( int i = start; i <= end; ++i) {
0850             Node *newChild = new Node();
0851             newChild->parent = parentNode;
0852             newChild->setPlace( parentNode->place() );
0853             newChild->originalModelIndex = QModelIndex();
0854             parentNode->children.insert(i, 1, newChild);
0855         }
0856     }
0857     if( place->flat ) {
0858         d->updatePlaceRows();
0859     }
0860 }
0861 
0862 
0863 void K3b::MetaItemModel::slotRowsInserted( const QModelIndex& parent, int start, int end )
0864 {
0865     Place* place = d->placeForModel( qobject_cast<QAbstractItemModel*>( sender() ) );
0866     Q_ASSERT( place != 0 );
0867 
0868     Node* parentNode;
0869     if( parent.isValid() )
0870         parentNode = place->createNodeForOriginalIndex( parent );
0871     else
0872         parentNode = place;
0873 
0874     // updating original indexes in newly created nodes
0875     for( int i = start; i <= end; ++i) {
0876         Node* child = parentNode->children.at( i );
0877         Q_ASSERT( child != 0 );
0878         child->originalModelIndex = parentNode->model()->index( i, 0, parent );
0879     }
0880 
0881     endInsertRows();
0882 }
0883 
0884 
0885 void K3b::MetaItemModel::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
0886 {
0887     //qDebug();
0888 
0889     Place* place = d->placeForModel( qobject_cast<QAbstractItemModel*>( sender() ) );
0890     Q_ASSERT( place != 0 );
0891 
0892     QModelIndex newParent;
0893     int targetStart = start, targetEnd = end;
0894 
0895     // --------------- Preparing for removing ----------------
0896     if ( parent.isValid() ) {
0897         // search node corresponding to 'index'
0898         newParent = mapFromSubModel( parent );
0899     }
0900     else {
0901         if ( place->flat ) {
0902             targetStart += place->row;
0903             targetEnd += place->row;
0904         }
0905         else
0906             newParent = createIndex( place->row, 0, place );
0907     }
0908 
0909     // --------------- Removing -------------------------------
0910     beginRemoveRows( newParent, targetStart, targetEnd );
0911 
0912     Node* parentNode;
0913 
0914     if ( parent.isValid() )
0915         parentNode = place->createNodeForOriginalIndex( parent );
0916     else
0917         parentNode = place;
0918 
0919     // parentNode->children may be empty if one of
0920     // the previous remove ended up causing a reset
0921     if (!parentNode->children.isEmpty()) {
0922         // remove the contents of pointers
0923         for (int i = start; i <= end; ++i)
0924             delete parentNode->children[i];
0925 
0926         // and remove the pointers themselves
0927         parentNode->children.remove( start, (end - start + 1) );
0928     }
0929 }
0930 
0931 
0932 void K3b::MetaItemModel::slotRowsRemoved( const QModelIndex&, int, int )
0933 {
0934     Place* place = d->placeForModel( qobject_cast<QAbstractItemModel*>( sender() ) );
0935     Q_ASSERT( place != 0 );
0936 
0937     if ( place->flat ) {
0938         d->updatePlaceRows();
0939     }
0940 
0941     endRemoveRows();
0942 }
0943 
0944 
0945 void K3b::MetaItemModel::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
0946 {
0947     //qDebug();
0948 
0949     Q_ASSERT( topLeft.isValid() );
0950     Q_ASSERT( bottomRight.isValid() );
0951 
0952     emit dataChanged( mapFromSubModel( topLeft ), mapFromSubModel( bottomRight ) );
0953 }
0954 
0955 
0956 void K3b::MetaItemModel::slotAboutToBeReset()
0957 {
0958     beginResetModel();
0959 }
0960 
0961 
0962 void K3b::MetaItemModel::slotReset()
0963 {
0964     // clean out any cached nodes
0965     for (auto &place : d->places) {
0966         place.reset();
0967     }
0968     d->updatePlaceRows();
0969 
0970     endResetModel();
0971 }
0972 
0973 #include "moc_k3bmetaitemmodel.cpp"