kde-playground/kdepim/kdgantt2/kdganttconstraintmodel.cpp
2015-04-14 21:49:29 +00:00

285 lines
8.2 KiB
C++

/****************************************************************************
** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KD Gantt library.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid commercial KD Gantt licenses may use this file in
** accordance with the KD Gantt Commercial License Agreement provided with
** the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.kdab.net/kdgantt for
** information about KD Gantt Commercial License Agreements.
**
** Contact info@kdab.net if any conditions of this
** licensing are not clear to you.
**
**********************************************************************/
#include "kdganttconstraintmodel.h"
#include "kdganttconstraintmodel_p.h"
#include <QDebug>
#include <cassert>
using namespace KDGantt;
/*!\class KDGantt::ConstraintModel
*\ingroup KDGantt
* The ConstraintModel keeps track of the
* interdependencies between gantt items in
* a View.
*
*/
ConstraintModel::Private::Private()
{
}
void ConstraintModel::Private::addConstraintToIndex( const QModelIndex& idx, const Constraint& c )
{
IndexType::iterator it = indexMap.find(idx);
while (it != indexMap.end() && it.key() == idx) {
// Check if we already have this
if ( *it == c ) return;
++it;
}
indexMap.insert( idx, c );
}
void ConstraintModel::Private::removeConstraintFromIndex( const QModelIndex& idx, const Constraint& c )
{
IndexType::iterator it = indexMap.find(idx);
while (it != indexMap.end() && it.key() == idx) {
if ( *it == c ) {
it =indexMap.erase( it );
} else {
++it;
}
}
}
/*! Constructor. Creates an empty ConstraintModel with parent \a parent
*/
ConstraintModel::ConstraintModel( QObject* parent )
: QObject( parent ), _d( new Private )
{
init();
}
/*!\internal*/
ConstraintModel::ConstraintModel( Private* d_ptr, QObject* parent )
: QObject( parent ), _d( d_ptr )
{
init();
}
/*! Destroys this ConstraintModel */
ConstraintModel::~ConstraintModel()
{
delete _d;
}
#define d d_func()
void ConstraintModel::init()
{
}
/*! Adds the constraint \a c to this ConstraintModel
* If the Constraint \a c is already in this ConstraintModel,
* nothing happens.
*/
void ConstraintModel::addConstraint( const Constraint& c )
{
//int size = d->constraints.size();
bool hasConstraint = d->constraints.contains( c );
//d->constraints.insert( c );
//if ( size != d->constraints.size() ) {
if ( !hasConstraint ) {
d->constraints.push_back( c );
d->addConstraintToIndex( c.startIndex(), c );
d->addConstraintToIndex( c.endIndex(), c );
emit constraintAdded( c );
}
}
/*! Removes the Constraint \a c from this
* ConstraintModel. If \a c was found and removed,
* the signal constraintRemoved(const Constraint&) is emitted.
*
* \returns If \a c was found and removed, it returns true,
* otherwise it returns false.
*/
bool ConstraintModel::removeConstraint( const Constraint& c )
{
//qDebug() << "ConstraintModel::removeConstraint("<<c<<") from "<< d->constraints;
bool rc = d->constraints.removeAll( c );
//bool rc = d->constraints.remove( c );
if ( rc ) {
d->removeConstraintFromIndex( c.startIndex(), c );
d->removeConstraintFromIndex( c.endIndex(), c );
emit constraintRemoved( c );
}
return rc;
}
/*! Removes all Constraints from this model
* The signal constraintRemoved(const Constraint&) is emitted
* for every Constraint that is removed.
*/
void ConstraintModel::clear()
{
QList<Constraint> lst = constraints();
Q_FOREACH( const Constraint& c, lst ) {
removeConstraint( c );
}
}
/*! Not used */
void ConstraintModel::cleanup()
{
#if 0
QSet<Constraint> orphans;
Q_FOREACH( const Constraint& c, d->constraints ) {
if ( !c.startIndex().isValid() || !c.endIndex().isValid() ) orphans.insert( c );
}
//qDebug() << "Constraint::cleanup() found" << orphans << "orphans";
d->constraints.subtract( orphans );
#endif
}
/*! \returns A list of all Constraints in this
* ConstraintModel.
*/
QList<Constraint> ConstraintModel::constraints() const
{
//return d->constraints.toList();
return d->constraints;
}
/*! \returns A list of all Constraints in this ConstraintModel
* that have an endpoint at \a idx.
*/
QList<Constraint> ConstraintModel::constraintsForIndex( const QModelIndex& idx ) const
{
assert( !idx.isValid() || d->indexMap.isEmpty() || !d->indexMap.keys().front().model() || idx.model() == d->indexMap.keys().front().model() );
if ( !idx.isValid() ) {
// Because of a Qt bug we need to treat this as a special case
QSet<Constraint> result;
Q_FOREACH( Constraint c, d->constraints ) {
if ( !c.startIndex().isValid() || !c.endIndex().isValid() ) result.insert( c );
}
return result.toList();
} else {
QList<Constraint> result;
Q_FOREACH( Constraint c, d->constraints ) {
if ( c.startIndex() == idx || c.endIndex() == idx ) result.push_back( c );
}
return result;
}
//return d->indexMap.values( idx );
}
/*! Returns true if a Constraint with start \a s and end \a e
* exists, otherwise false.
*/
bool ConstraintModel::hasConstraint( const Constraint& c ) const
{
/*
// Because of a Qt bug we have to search like this
Q_FOREACH( Constraint c2, d->constraints ) {
if ( c==c2 ) return true;
}
return false;
*/
return d->constraints.contains( c );
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<( QDebug dbg, const KDGantt::ConstraintModel& model )
{
dbg << "KDGantt::ConstraintModel[ " << static_cast<const QObject*>( &model ) << ":"
<< model.constraints() << "]";
return dbg;
}
#endif /* QT_NO_DEBUG_STREAM */
#undef d
#ifndef KDAB_NO_UNIT_TESTS
#include <QStandardItemModel>
#include "unittest/test.h"
std::ostream& operator<<( std::ostream& os, const QModelIndex& idx )
{
QString str;
QDebug( &str )<<idx;
#ifdef QT_NO_STL
os<<str.toLatin1().constData();
#else
os<<str.toStdString();
#endif
return os;
}
KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, ConstraintModel, "test" )
{
QStandardItemModel dummyModel( 100, 100 );
ConstraintModel model;
QModelIndex invalidIndex;
assertEqual( invalidIndex, invalidIndex );
assertEqual( model.constraints().count(), 0 );
model.addConstraint( Constraint( QModelIndex(), QModelIndex() ) );
assertEqual( model.constraints().count(), 1 );
model.addConstraint( Constraint( QModelIndex(), QModelIndex() ) );
assertEqual( model.constraints().count(), 1 );
QPersistentModelIndex idx1 = dummyModel.index( 7, 17, QModelIndex() );
QPersistentModelIndex idx2 = dummyModel.index( 42, 17, QModelIndex() );
model.addConstraint( Constraint( idx1, idx2 ) );
assertEqual( model.constraints().count(), 2 );
assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
assertEqual( model.constraintsForIndex( QModelIndex() ).count(), 1 );
assertEqual( model.constraints().count(), 2 );
model.removeConstraint( Constraint( QModelIndex(), QModelIndex() ) );
assertEqual( model.constraints().count(), 1 );
assertFalse( model.hasConstraint( Constraint( QModelIndex(), QModelIndex() ) ) );
model.removeConstraint( Constraint( QModelIndex(), QModelIndex() ) );
assertEqual( model.constraints().count(), 1 );
model.removeConstraint( Constraint( idx1, idx2 ) );
assertEqual( model.constraints().count(), 0 );
assertFalse( model.hasConstraint( Constraint( idx1, idx2 ) ) );
model.addConstraint( Constraint( idx1, idx2 ) );
assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
dummyModel.removeRow( 8 );
assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
dummyModel.removeRow( 7 );
assertTrue( model.hasConstraint( Constraint( idx1, idx2 ) ) );
}
#endif /* KDAB_NO_UNIT_TESTS */