File indexing completed on 2024-05-12 04:20:39

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KGantt library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "kganttconstraintmodel.h"
0010 #include "kganttconstraintmodel_p.h"
0011 
0012 
0013 #include <cassert>
0014 #include <algorithm>
0015 #include <functional>
0016 
0017 using namespace KGantt;
0018 
0019 
0020 
0021 ConstraintModel::Private::Private()
0022 {
0023 }
0024 
0025 void ConstraintModel::Private::addConstraintToIndex( const QModelIndex& idx, const Constraint& c )
0026 {
0027     IndexType::iterator it = indexMap.find(idx);
0028     while (it != indexMap.end() && it.key() == idx) {
0029         // Check if we already have this
0030         if ( *it == c ) return;
0031         ++it;
0032     }
0033 
0034     indexMap.insert( idx, c );
0035 }
0036 
0037 void ConstraintModel::Private::removeConstraintFromIndex( const QModelIndex& idx,  const Constraint& c )
0038 {
0039     IndexType::iterator it = indexMap.find(idx);
0040     while (it != indexMap.end() && it.key() == idx) {
0041         if ( c.compareIndexes(*it) ) {
0042             it =indexMap.erase( it );
0043         } else {
0044             ++it;
0045         }
0046     }
0047 }
0048 
0049 
0050 ConstraintModel::ConstraintModel( QObject* parent )
0051     : QObject( parent ), _d( new Private )
0052 {
0053     init();
0054 }
0055 
0056 
0057 ConstraintModel::ConstraintModel( Private* d_ptr, QObject* parent )
0058     : QObject( parent ), _d( d_ptr )
0059 {
0060     init();
0061 }
0062 
0063 
0064 ConstraintModel::~ConstraintModel()
0065 {
0066     delete _d;
0067 }
0068 
0069 #define d d_func()
0070 
0071 void ConstraintModel::init()
0072 {
0073 }
0074 
0075 namespace {
0076     struct compare_constraint_indexes_to {
0077         compare_constraint_indexes_to( const Constraint& c )
0078             : m_c( c ) {
0079         }
0080         bool operator()( const Constraint& c ) const
0081         {
0082             return m_c.compareIndexes( c );
0083         }
0084 
0085         const Constraint& m_c;
0086     };
0087 }
0088 
0089 void ConstraintModel::addConstraint( const Constraint& c )
0090 {
0091     //qDebug() << "ConstraintModel::addConstraint("<<c<<") (this="<<this<<") items=" << d->constraints.size();
0092     QList<Constraint>::iterator it = std::find_if ( d->constraints.begin(),
0093                                                     d->constraints.end(),
0094                                                     compare_constraint_indexes_to(c) );
0095 
0096     if ( it == d->constraints.end() ) {
0097         d->constraints.push_back( c );
0098         d->addConstraintToIndex( c.startIndex(), c );
0099         d->addConstraintToIndex( c.endIndex(), c );
0100         Q_EMIT constraintAdded( c );
0101     } else if ( ( *it ).dataMap() != c.dataMap() || ( *it ).type() != c.type() || ( *it ).relationType() != c.relationType() ) {
0102         Constraint tmp( *it ); // save to avoid re-entrancy issues
0103         removeConstraint( tmp );
0104         d->constraints.push_back( c );
0105         d->addConstraintToIndex( c.startIndex(), c );
0106         d->addConstraintToIndex( c.endIndex(), c );
0107         Q_EMIT constraintAdded( c );
0108     }
0109 }
0110 
0111 bool ConstraintModel::removeConstraint( const Constraint& c )
0112 {
0113     bool rc = false;
0114 
0115     for (int i = 0; i < d->constraints.count(); i++)
0116     {
0117         if (c.compareIndexes(d->constraints.at(i)))
0118         {
0119             d->constraints.removeAt(i);
0120             rc = true;
0121         }
0122     }
0123 
0124     if ( rc ) {
0125         d->removeConstraintFromIndex( c.startIndex(), c );
0126         d->removeConstraintFromIndex( c.endIndex(), c );
0127         Q_EMIT constraintRemoved( c );
0128     }
0129 
0130     return rc;
0131 }
0132 
0133 void ConstraintModel::clear()
0134 {
0135     const QList<Constraint> lst = constraints();
0136     for ( const Constraint& c : lst ) {
0137         removeConstraint( c );
0138     }
0139 }
0140 
0141 void ConstraintModel::cleanup()
0142 {
0143 #if 0
0144     QSet<Constraint> orphans;
0145     for ( const Constraint& c : qAsConst(d->constraints) ) {
0146         if ( !c.startIndex().isValid() || !c.endIndex().isValid() ) orphans.insert( c );
0147     }
0148     //qDebug() << "Constraint::cleanup() found" << orphans << "orphans";
0149     d->constraints.subtract( orphans );
0150 #endif
0151 }
0152 
0153 QList<Constraint> ConstraintModel::constraints() const
0154 {
0155     //return d->constraints.toList();
0156     return d->constraints;
0157 }
0158 
0159 QList<Constraint> ConstraintModel::constraintsForIndex( const QModelIndex& idx ) const
0160 {
0161     // TODO: @Steffen: Please comment on this assert, it's long and not obvious (Johannes)
0162     // TODO: Afaics indexMap is not used anymore, so remove it (danders)
0163     //assert( !idx.isValid() || d->indexMap.isEmpty() || !d->indexMap.keys().front().model() || idx.model() == d->indexMap.keys().front().model() );
0164     if ( !idx.isValid() ) {
0165         // Because of a Qt bug we need to treat this as a special case
0166         QSet<Constraint> result;
0167         for ( const Constraint& c : qAsConst(d->constraints) ) {
0168             if ( !c.startIndex().isValid() || !c.endIndex().isValid() ) result.insert( c );
0169         }
0170         return result.values();
0171     } else {
0172         QList<Constraint> result;
0173         for ( const Constraint& c : qAsConst(d->constraints) ) {
0174             if ( c.startIndex() == idx || c.endIndex() == idx ) result.push_back( c );
0175         }
0176         return result;
0177     }
0178 
0179     //return d->indexMap.values( idx );
0180 }
0181 
0182 bool ConstraintModel::hasConstraint( const Constraint& c ) const
0183 {
0184     /*
0185     // Because of a Qt bug we have to search like this
0186     for ( Constraint c2 : qAsConst(d->constraints) ) {
0187         if ( c==c2 ) return true;
0188     }
0189     return false;
0190     */
0191     bool hc = false;
0192 
0193     for (int i = 0; i < d->constraints.count(); i++)
0194         if (c.compareIndexes(d->constraints.at(i)))
0195             hc = true;
0196 
0197     return hc;
0198 }
0199 
0200 #ifndef QT_NO_DEBUG_STREAM
0201 
0202 QDebug operator<<( QDebug dbg, const KGantt::ConstraintModel& model )
0203 {
0204     dbg << "KGantt::ConstraintModel[ " << static_cast<const QObject*>( &model ) << ": [\n";
0205     const auto constraints = model.constraints();
0206     for ( const Constraint& c : constraints ) {
0207         dbg << "\t" << c << "\n";
0208     }
0209     dbg << "]\n";
0210     return dbg;
0211 }
0212 
0213 #endif /* QT_NO_DEBUG_STREAM */
0214 
0215 #undef d
0216 
0217 #ifndef KDAB_NO_UNIT_TESTS
0218 
0219 #include <QStandardItemModel>
0220 
0221 #include "unittest/test.h"
0222 
0223 std::ostream& operator<<( std::ostream& os, const QModelIndex& idx )
0224 {
0225     QString str;
0226     QDebug( &str )<<idx;
0227 #ifdef QT_NO_STL
0228     os<<str.toLatin1().constData();
0229 #else
0230     os<<str.toStdString();
0231 #endif
0232     return os;
0233 }
0234 
0235 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, ConstraintModel, "test" )
0236 {
0237     QStandardItemModel dummyModel( 100, 100 );
0238     ConstraintModel model;
0239 
0240     QModelIndex invalidIndex;
0241     assertEqual( invalidIndex, invalidIndex );
0242 
0243     assertEqual( model.constraints().count(), 0 );
0244 
0245     model.addConstraint( Constraint( QModelIndex(), QModelIndex() ) );
0246     assertEqual( model.constraints().count(), 1 );
0247 
0248     model.addConstraint( Constraint( QModelIndex(), QModelIndex() ) );
0249     assertEqual( model.constraints().count(), 1 );
0250 
0251     QPersistentModelIndex idx1 = dummyModel.index( 7, 17, QModelIndex() );
0252     QPersistentModelIndex idx2 = dummyModel.index( 42, 17, QModelIndex() );
0253 
0254     model.addConstraint( Constraint( idx1, idx2 ) );
0255     assertEqual( model.constraints().count(), 2 );
0256     assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
0257 
0258     assertEqual( model.constraintsForIndex( QModelIndex() ).count(), 1 );
0259 
0260     assertEqual( model.constraints().count(), 2 );
0261     model.removeConstraint( Constraint( QModelIndex(), QModelIndex() ) );
0262     assertEqual( model.constraints().count(), 1 );
0263     assertFalse( model.hasConstraint( Constraint( QModelIndex(), QModelIndex() ) ) );
0264 
0265     model.removeConstraint( Constraint( QModelIndex(), QModelIndex() ) );
0266     assertEqual( model.constraints().count(), 1 );
0267 
0268     model.removeConstraint( Constraint( idx1, idx2 ) );
0269     assertEqual( model.constraints().count(), 0 );
0270     assertFalse( model.hasConstraint( Constraint( idx1, idx2 ) ) );
0271 
0272     model.addConstraint( Constraint( idx1, idx2 ) );
0273     assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
0274     dummyModel.removeRow( 8 );
0275     assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
0276     dummyModel.removeRow( 7 );
0277     assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
0278 }
0279 
0280 #endif /* KDAB_NO_UNIT_TESTS */
0281 
0282 #include "moc_kganttconstraintmodel.cpp"