diff --git a/src/KGantt/kganttsummaryhandlingproxymodel.cpp b/src/KGantt/kganttsummaryhandlingproxymodel.cpp
index 68163e5..b092f90 100644
--- a/src/KGantt/kganttsummaryhandlingproxymodel.cpp
+++ b/src/KGantt/kganttsummaryhandlingproxymodel.cpp
@@ -1,327 +1,317 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "kganttsummaryhandlingproxymodel.h"
#include "kganttsummaryhandlingproxymodel_p.h"
#include
#include
using namespace KGantt;
/*!\class KGantt::SummaryHandlingProxyModel
* \brief Proxy model that supports summary gantt items.
*
* This proxy model provides the functionality of summary items.
* A summary item is an item with type KGantt::TypeSummary and
* zero or more children in the model that it summarizes.
* GraphicsView itself does not dictate any policy for summary items,
* instead the logic for making the summary items start and end points
* span it's children is provided by this proxy.
*
* The start and end times of a summary is the min/max of the
* start/end times of it's children.
*
* \see GraphicsView::setModel
*/
typedef ForwardingProxyModel BASE;
bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx,
QPair* result ) const
{
//qDebug() << "cacheLookup("< >::const_iterator it =
cached_summary_items.constFind( idx );
if ( it != cached_summary_items.constEnd() ) {
*result = *it;
return true;
} else {
return false;
}
}
void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model,
const QModelIndex& sourceIdx ) const
{
QAbstractItemModel* sourceModel = model->sourceModel();
const QModelIndex& mainIdx = sourceIdx;
QDateTime st;
QDateTime et;
for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) {
QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) );
/* The probably results in recursive calls here */
QVariant tmpsv = model->data( pdIdx, StartTimeRole );
QVariant tmpev = model->data( pdIdx, EndTimeRole );
if ( !tmpsv.canConvert( QVariant::DateTime ) ||
!tmpev.canConvert( QVariant::DateTime ) ) {
qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
continue;
}
// check for valid datetimes
if ( tmpsv.type() == QVariant::DateTime && !tmpsv.value().isValid()) continue;
if ( tmpev.type() == QVariant::DateTime && !tmpev.value().isValid()) continue;
// We need to test for empty strings to
// avoid a stupid Qt warning
if ( tmpsv.type() == QVariant::String && tmpsv.value().isEmpty()) continue;
if ( tmpev.type() == QVariant::String && tmpev.value().isEmpty()) continue;
QDateTime tmpst = tmpsv.toDateTime();
QDateTime tmpet = tmpev.toDateTime();
if ( st.isNull() || st > tmpst ) st = tmpst;
if ( et.isNull() || et < tmpet ) et = tmpet;
}
QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole );
QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole );
if ( tmpssv.canConvert( QVariant::DateTime )
&& !( tmpssv.canConvert( QVariant::String ) && tmpssv.toString().isEmpty() )
&& tmpssv.toDateTime() != st )
sourceModel->setData( mainIdx, st, StartTimeRole );
if ( tmpsev.canConvert( QVariant::DateTime )
&& !( tmpsev.canConvert( QVariant::String ) && tmpsev.toString().isEmpty() )
&& tmpsev.toDateTime() != et )
sourceModel->setData( mainIdx, et, EndTimeRole );
cached_summary_items[sourceIdx]=qMakePair( st, et );
}
void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const
{
cached_summary_items.remove( idx );
}
void SummaryHandlingProxyModel::Private::clearCache() const
{
cached_summary_items.clear();
}
/*! Constructor. Creates a new SummaryHandlingProxyModel with
* parent \a parent
*/
SummaryHandlingProxyModel::SummaryHandlingProxyModel( QObject* parent )
: BASE( parent ), _d( new Private )
{
init();
}
#define d d_func()
SummaryHandlingProxyModel::~SummaryHandlingProxyModel()
{
delete _d;
}
void SummaryHandlingProxyModel::init()
{
}
-namespace {
-
- // Think this is ugly? Well, it's not from me, it comes from QProxyModel
- struct KDPrivateModelIndex {
- int r, c;
- void *p;
- const QAbstractItemModel *m;
- };
-}
-
/*! Sets the model to be used as the source model for this proxy.
* The proxy does not take ownership of the model.
* \see QAbstractProxyModel::setSourceModel
*/
void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model )
{
BASE::setSourceModel( model );
d->clearCache();
}
void SummaryHandlingProxyModel::sourceModelReset()
{
d->clearCache();
BASE::sourceModelReset();
}
void SummaryHandlingProxyModel::sourceLayoutChanged()
{
d->clearCache();
BASE::sourceLayoutChanged();
}
void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
{
QAbstractItemModel* model = sourceModel();
QModelIndex parentIdx = from;
do {
const QModelIndex& dataIdx = parentIdx;
if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
//qDebug() << "removing " << parentIdx << "from cache";
d->removeFromCache( dataIdx );
QModelIndex proxyDataIdx = mapFromSource( dataIdx );
emit dataChanged( proxyDataIdx, proxyDataIdx );
}
} while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
BASE::sourceDataChanged( from, to );
}
void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx,
int start,
int end )
{
BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
d->clearCache();
}
void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx,
int start,
int end )
{
BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
d->clearCache();
}
void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
{
BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
d->clearCache();
}
void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
{
BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
d->clearCache();
}
/*! \see QAbstractItemModel::flags */
Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const
{
const QModelIndex sidx = mapToSource( idx );
const QAbstractItemModel* model = sourceModel();
Qt::ItemFlags f = model->flags( sidx );
if ( d->isSummary(sidx) ) {
f &= ~Qt::ItemIsEditable;
}
return f;
}
/*! \see QAbstractItemModel::data */
QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
{
//qDebug() << "SummaryHandlingProxyModel::data("<isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
//qDebug() << "requested summary";
QPair result;
if ( d->cacheLookup( sidx, &result ) ) {
//qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
switch ( role ) {
case StartTimeRole: return result.first;
case EndTimeRole: return result.second;
default: /* fall thru */;
}
} else {
d->insertInCache( this, sidx );
return data( proxyIndex, role ); /* TODO: Optimize */
}
}
return model->data( sidx, role );
}
/*! \see QAbstractItemModel::setData */
bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
{
QAbstractItemModel* model = sourceModel();
if ( role==StartTimeRole || role==EndTimeRole ) {
QModelIndex parentIdx = mapToSource( index );
do {
if ( d->isSummary(parentIdx) ) {
//qDebug() << "removing " << parentIdx << "from cache";
d->removeFromCache( parentIdx );
QModelIndex proxyParentIdx = mapFromSource( parentIdx );
emit dataChanged( proxyParentIdx, proxyParentIdx );
}
} while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
}
return BASE::setData( index, value, role );
}
#undef d
#ifndef KDAB_NO_UNIT_TESTS
#include "unittest/test.h"
#include
static std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
{
#ifdef QT_NO_STL
os << dt.toString().toLatin1().constData();
#else
os << dt.toString().toStdString();
#endif
return os;
}
KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, SummaryHandlingProxyModel, "test" ) {
SummaryHandlingProxyModel model;
QStandardItemModel sourceModel;
model.setSourceModel( &sourceModel );
QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
topitem->setData( KGantt::TypeSummary, KGantt::ItemTypeRole );
sourceModel.appendRow( topitem );
QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
task1->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
task2->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
topitem->appendRow( task1 );
topitem->appendRow( task2 );
QDateTime startdt = QDateTime::currentDateTime();
QDateTime enddt = startdt.addDays( 1 );
task1->setData( startdt, KGantt::StartTimeRole );
task1->setData( enddt, KGantt::EndTimeRole );
task2->setData( startdt, KGantt::StartTimeRole );
task2->setData( enddt, KGantt::EndTimeRole );
const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
assertEqual( model.data( topidx, KGantt::ItemTypeRole ).toInt(), KGantt::TypeSummary );
assertEqual( model.data( model.index( 0, 0, topidx ), KGantt::ItemTypeRole ).toInt(), KGantt::TypeTask );
QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KGantt::StartTimeRole ).toDateTime();
assertEqual( task1startdt, startdt );
QDateTime summarystartdt = model.data( topidx, KGantt::StartTimeRole ).toDateTime();
assertEqual( summarystartdt, startdt );
assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
}
#endif /* KDAB_NO_UNIT_TESTS */
#include "moc_kganttsummaryhandlingproxymodel.cpp"