1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
4 ** This file is part of the KD Gantt library.
6 ** This file may be distributed and/or modified under the terms of the
7 ** GNU General Public License version 2 as published by the Free Software
8 ** Foundation and appearing in the file LICENSE.GPL included in the
9 ** packaging of this file.
11 ** Licensees holding valid commercial KD Gantt licenses may use this file in
12 ** accordance with the KD Gantt Commercial License Agreement provided with
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 ** See http://www.kdab.net/kdgantt for
19 ** information about KD Gantt Commercial License Agreements.
21 ** Contact info@kdab.net if any conditions of this
22 ** licensing are not clear to you.
24 **********************************************************************/
25 #include "kdganttconstraintmodel.h"
26 #include "kdganttconstraintmodel_p.h"
32 using namespace KDGantt
;
34 /*!\class KDGantt::ConstraintModel
36 * The ConstraintModel keeps track of the
37 * interdependencies between gantt items in
42 ConstraintModel::Private::Private()
46 void ConstraintModel::Private::addConstraintToIndex( const QModelIndex
& idx
, const Constraint
& c
)
48 IndexType::iterator it
= indexMap
.find(idx
);
49 while (it
!= indexMap
.end() && it
.key() == idx
) {
50 // Check if we already have this
51 if ( *it
== c
) return;
55 indexMap
.insert( idx
, c
);
58 void ConstraintModel::Private::removeConstraintFromIndex( const QModelIndex
& idx
, const Constraint
& c
)
60 IndexType::iterator it
= indexMap
.find(idx
);
61 while (it
!= indexMap
.end() && it
.key() == idx
) {
63 it
=indexMap
.erase( it
);
70 /*! Constructor. Creates an empty ConstraintModel with parent \a parent
72 ConstraintModel::ConstraintModel( QObject
* parent
)
73 : QObject( parent
), _d( new Private
)
79 ConstraintModel::ConstraintModel( Private
* d_ptr
, QObject
* parent
)
80 : QObject( parent
), _d( d_ptr
)
85 /*! Destroys this ConstraintModel */
86 ConstraintModel::~ConstraintModel()
93 void ConstraintModel::init()
97 /*! Adds the constraint \a c to this ConstraintModel
98 * If the Constraint \a c is already in this ConstraintModel,
101 void ConstraintModel::addConstraint( const Constraint
& c
)
103 //int size = d->constraints.size();
104 bool hasConstraint
= d
->constraints
.contains( c
);
105 //d->constraints.insert( c );
106 //if ( size != d->constraints.size() ) {
107 if ( !hasConstraint
) {
108 d
->constraints
.push_back( c
);
109 d
->addConstraintToIndex( c
.startIndex(), c
);
110 d
->addConstraintToIndex( c
.endIndex(), c
);
111 emit
constraintAdded( c
);
115 /*! Removes the Constraint \a c from this
116 * ConstraintModel. If \a c was found and removed,
117 * the signal constraintRemoved(const Constraint&) is emitted.
119 * \returns If \a c was found and removed, it returns true,
120 * otherwise it returns false.
122 bool ConstraintModel::removeConstraint( const Constraint
& c
)
124 //qDebug() << "ConstraintModel::removeConstraint("<<c<<") from "<< d->constraints;
125 bool rc
= d
->constraints
.removeAll( c
);
126 //bool rc = d->constraints.remove( c );
128 d
->removeConstraintFromIndex( c
.startIndex(), c
);
129 d
->removeConstraintFromIndex( c
.endIndex(), c
);
130 emit
constraintRemoved( c
);
135 /*! Removes all Constraints from this model
136 * The signal constraintRemoved(const Constraint&) is emitted
137 * for every Constraint that is removed.
139 void ConstraintModel::clear()
141 QList
<Constraint
> lst
= constraints();
142 Q_FOREACH( const Constraint
& c
, lst
) {
143 removeConstraint( c
);
148 void ConstraintModel::cleanup()
151 QSet
<Constraint
> orphans
;
152 Q_FOREACH( const Constraint
& c
, d
->constraints
) {
153 if ( !c
.startIndex().isValid() || !c
.endIndex().isValid() ) orphans
.insert( c
);
155 //qDebug() << "Constraint::cleanup() found" << orphans << "orphans";
156 d
->constraints
.subtract( orphans
);
160 /*! \returns A list of all Constraints in this
163 QList
<Constraint
> ConstraintModel::constraints() const
165 //return d->constraints.toList();
166 return d
->constraints
;
169 /*! \returns A list of all Constraints in this ConstraintModel
170 * that have an endpoint at \a idx.
172 QList
<Constraint
> ConstraintModel::constraintsForIndex( const QModelIndex
& idx
) const
174 assert( !idx
.isValid() || d
->indexMap
.isEmpty() || !d
->indexMap
.keys().front().model() || idx
.model() == d
->indexMap
.keys().front().model() );
175 if ( !idx
.isValid() ) {
176 // Because of a Qt bug we need to treat this as a special case
177 QSet
<Constraint
> result
;
178 Q_FOREACH( Constraint c
, d
->constraints
) {
179 if ( !c
.startIndex().isValid() || !c
.endIndex().isValid() ) result
.insert( c
);
181 return result
.toList();
183 QList
<Constraint
> result
;
184 Q_FOREACH( Constraint c
, d
->constraints
) {
185 if ( c
.startIndex() == idx
|| c
.endIndex() == idx
) result
.push_back( c
);
190 //return d->indexMap.values( idx );
193 /*! Returns true if a Constraint with start \a s and end \a e
194 * exists, otherwise false.
196 bool ConstraintModel::hasConstraint( const Constraint
& c
) const
199 // Because of a Qt bug we have to search like this
200 Q_FOREACH( Constraint c2, d->constraints ) {
201 if ( c==c2 ) return true;
205 return d
->constraints
.contains( c
);
208 #ifndef QT_NO_DEBUG_STREAM
210 QDebug
operator<<( QDebug dbg
, const KDGantt::ConstraintModel
& model
)
212 dbg
<< "KDGantt::ConstraintModel[ " << static_cast<const QObject
*>( &model
) << ":"
213 << model
.constraints() << "]";
217 #endif /* QT_NO_DEBUG_STREAM */
221 #ifndef KDAB_NO_UNIT_TESTS
223 #include <QStandardItemModel>
225 #include "unittest/test.h"
227 std::ostream
& operator<<( std::ostream
& os
, const QModelIndex
& idx
)
232 os
<<str
.toLatin1().constData();
234 os
<<str
.toStdString();
239 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt
, ConstraintModel
, "test" )
241 QStandardItemModel
dummyModel( 100, 100 );
242 ConstraintModel model
;
244 QModelIndex invalidIndex
;
245 assertEqual( invalidIndex
, invalidIndex
);
247 assertEqual( model
.constraints().count(), 0 );
249 model
.addConstraint( Constraint( QModelIndex(), QModelIndex() ) );
250 assertEqual( model
.constraints().count(), 1 );
252 model
.addConstraint( Constraint( QModelIndex(), QModelIndex() ) );
253 assertEqual( model
.constraints().count(), 1 );
255 QPersistentModelIndex idx1
= dummyModel
.index( 7, 17, QModelIndex() );
256 QPersistentModelIndex idx2
= dummyModel
.index( 42, 17, QModelIndex() );
258 model
.addConstraint( Constraint( idx1
, idx2
) );
259 assertEqual( model
.constraints().count(), 2 );
260 assertTrue( model
.hasConstraint( Constraint( idx1
, idx2
) ) );
262 assertEqual( model
.constraintsForIndex( QModelIndex() ).count(), 1 );
264 assertEqual( model
.constraints().count(), 2 );
265 model
.removeConstraint( Constraint( QModelIndex(), QModelIndex() ) );
266 assertEqual( model
.constraints().count(), 1 );
267 assertFalse( model
.hasConstraint( Constraint( QModelIndex(), QModelIndex() ) ) );
269 model
.removeConstraint( Constraint( QModelIndex(), QModelIndex() ) );
270 assertEqual( model
.constraints().count(), 1 );
272 model
.removeConstraint( Constraint( idx1
, idx2
) );
273 assertEqual( model
.constraints().count(), 0 );
274 assertFalse( model
.hasConstraint( Constraint( idx1
, idx2
) ) );
276 model
.addConstraint( Constraint( idx1
, idx2
) );
277 assertTrue( model
.hasConstraint( Constraint( idx1
, idx2
) ) );
278 dummyModel
.removeRow( 8 );
279 assertTrue( model
.hasConstraint( Constraint( idx1
, idx2
) ) );
280 dummyModel
.removeRow( 7 );
281 assertTrue( model
.hasConstraint( Constraint( idx1
, idx2
) ) );
284 #endif /* KDAB_NO_UNIT_TESTS */
286 #include "moc_kdganttconstraintmodel.cpp"