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"