mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 10:22:50 +00:00
328 lines
12 KiB
C++
328 lines
12 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 "kdganttsummaryhandlingproxymodel.h"
|
|
#include "kdganttsummaryhandlingproxymodel_p.h"
|
|
|
|
#include <QDebug>
|
|
|
|
#include <cassert>
|
|
|
|
using namespace KDGantt;
|
|
|
|
/*!\class KDGantt::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 KDGantt::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<QDateTime,QDateTime>* result ) const
|
|
{
|
|
//qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
|
|
QHash<QModelIndex,QPair<QDateTime,QDateTime> >::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( !qVariantCanConvert<QDateTime>(tmpsv) ||
|
|
!qVariantCanConvert<QDateTime>(tmpev) ) {
|
|
qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
|
|
continue;
|
|
}
|
|
|
|
// We need to test for empty strings to
|
|
// avoid a stupid Qt warning
|
|
if( tmpsv.type() == QVariant::String && qVariantValue<QString>(tmpsv).isEmpty()) continue;
|
|
if( tmpev.type() == QVariant::String && qVariantValue<QString>(tmpev).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 ( qVariantCanConvert<QDateTime>( tmpssv )
|
|
&& !( qVariantCanConvert<QString>( tmpssv ) && qVariantValue<QString>( tmpssv ).isEmpty() )
|
|
&& qVariantValue<QDateTime>( tmpssv ) != st )
|
|
sourceModel->setData( mainIdx, st, StartTimeRole );
|
|
if ( qVariantCanConvert<QDateTime>( tmpsev )
|
|
&& !( qVariantCanConvert<QString>( tmpsev ) && qVariantValue<QString>( tmpsev ).isEmpty() )
|
|
&& qVariantValue<QDateTime>( tmpsev ) != 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()
|
|
{
|
|
}
|
|
|
|
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("<<proxyIndex<<role<<")";
|
|
const QModelIndex sidx = mapToSource( proxyIndex );
|
|
const QAbstractItemModel* model = sourceModel();
|
|
if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
|
|
//qDebug() << "requested summary";
|
|
QPair<QDateTime,QDateTime> 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: Optimise */
|
|
}
|
|
}
|
|
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 <QStandardItemModel>
|
|
|
|
namespace {
|
|
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( KDGantt, SummaryHandlingProxyModel, "test" ) {
|
|
SummaryHandlingProxyModel model;
|
|
QStandardItemModel sourceModel;
|
|
|
|
model.setSourceModel( &sourceModel );
|
|
|
|
QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
|
|
topitem->setData( KDGantt::TypeSummary, KDGantt::ItemTypeRole );
|
|
sourceModel.appendRow( topitem );
|
|
|
|
QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
|
|
task1->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
|
|
QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
|
|
task2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
|
|
topitem->appendRow( task1 );
|
|
topitem->appendRow( task2 );
|
|
|
|
|
|
QDateTime startdt = QDateTime::currentDateTime();
|
|
QDateTime enddt = startdt.addDays( 1 );
|
|
|
|
|
|
task1->setData( startdt, KDGantt::StartTimeRole );
|
|
task1->setData( enddt, KDGantt::EndTimeRole );
|
|
task2->setData( startdt, KDGantt::StartTimeRole );
|
|
task2->setData( enddt, KDGantt::EndTimeRole );
|
|
|
|
const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
|
|
|
|
assertEqual( model.data( topidx, KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeSummary );
|
|
assertEqual( model.data( model.index( 0, 0, topidx ), KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeTask );
|
|
|
|
QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KDGantt::StartTimeRole ).toDateTime();
|
|
assertEqual( task1startdt, startdt );
|
|
|
|
QDateTime summarystartdt = model.data( topidx, KDGantt::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 */
|
|
|