File indexing completed on 2024-05-19 15:27:31

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