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"