kdganttsummaryhandlingproxymodel.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "kdganttsummaryhandlingproxymodel.h"
00024 #include "kdganttsummaryhandlingproxymodel_p.h"
00025 
00026 #include <QDebug>
00027 
00028 #include <cassert>
00029 
00030 using namespace KDGantt;
00031 
00048 typedef ForwardingProxyModel BASE;
00049 
00050 bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx,
00051                                                       QPair<QDateTime,QDateTime>* result ) const
00052 {
00053     //qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
00054     QHash<QModelIndex,QPair<QDateTime,QDateTime> >::const_iterator it =
00055         cached_summary_items.constFind( idx );
00056     if ( it != cached_summary_items.constEnd() ) {
00057         *result = *it;
00058         return true;
00059     } else {
00060         return false;
00061     }
00062 }
00063 
00064 void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model,
00065                                                         const QModelIndex& sourceIdx ) const
00066 {
00067     QAbstractItemModel* sourceModel = model->sourceModel();
00068     const QModelIndex& mainIdx = sourceIdx;
00069     QDateTime st;
00070     QDateTime et;
00071 
00072     for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) {
00073         QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) );
00074         /* The probably results in recursive calls here */
00075         QVariant tmpsv = model->data( pdIdx, StartTimeRole );
00076         QVariant tmpev = model->data( pdIdx, EndTimeRole );
00077         if( !qVariantCanConvert<QDateTime>(tmpsv) ||
00078             !qVariantCanConvert<QDateTime>(tmpev) ) {
00079             qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
00080             continue;
00081         }
00082 
00083         // check for valid datetimes
00084         if( tmpsv.type() == QVariant::DateTime && !qVariantValue<QDateTime>(tmpsv).isValid()) continue;
00085         if( tmpev.type() == QVariant::DateTime && !qVariantValue<QDateTime>(tmpev).isValid()) continue;
00086 
00087         // We need to test for empty strings to
00088         // avoid a stupid Qt warning
00089         if( tmpsv.type() == QVariant::String && qVariantValue<QString>(tmpsv).isEmpty()) continue;
00090         if( tmpev.type() == QVariant::String && qVariantValue<QString>(tmpev).isEmpty()) continue;
00091         QDateTime tmpst = tmpsv.toDateTime();
00092         QDateTime tmpet = tmpev.toDateTime();
00093         if ( st.isNull() || st > tmpst ) st = tmpst;
00094         if ( et.isNull() || et < tmpet ) et = tmpet;
00095     }
00096     QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole );
00097     QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole );
00098     if ( qVariantCanConvert<QDateTime>( tmpssv )
00099          && !( qVariantCanConvert<QString>( tmpssv ) && qVariantValue<QString>( tmpssv ).isEmpty() )
00100          && qVariantValue<QDateTime>( tmpssv ) != st )
00101         sourceModel->setData( mainIdx, st, StartTimeRole );
00102     if ( qVariantCanConvert<QDateTime>( tmpsev )
00103          && !( qVariantCanConvert<QString>( tmpsev ) && qVariantValue<QString>( tmpsev ).isEmpty() )
00104          && qVariantValue<QDateTime>( tmpsev ) != et )
00105         sourceModel->setData( mainIdx, et, EndTimeRole );
00106     cached_summary_items[sourceIdx]=qMakePair( st, et );
00107 }
00108 
00109 void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const
00110 {
00111     cached_summary_items.remove( idx );
00112 }
00113 
00114 void SummaryHandlingProxyModel::Private::clearCache() const
00115 {
00116     cached_summary_items.clear();
00117 }
00118 
00122 SummaryHandlingProxyModel::SummaryHandlingProxyModel( QObject* parent )
00123     : BASE( parent ), _d( new Private )
00124 {
00125     init();
00126 }
00127 
00128 #define d d_func()
00129 SummaryHandlingProxyModel::~SummaryHandlingProxyModel()
00130 {
00131 }
00132 
00133 void SummaryHandlingProxyModel::init()
00134 {
00135 }
00136 
00137 namespace {
00138 
00139     // Think this is ugly? Well, it's not from me, it comes from QProxyModel
00140     struct KDPrivateModelIndex {
00141         int r, c;
00142         void *p;
00143         const QAbstractItemModel *m;
00144     };
00145 }
00146 
00151 void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model )
00152 {
00153     BASE::setSourceModel( model );
00154     d->clearCache();
00155 }
00156 
00157 void SummaryHandlingProxyModel::sourceModelReset()
00158 {
00159     d->clearCache();
00160     BASE::sourceModelReset();
00161 }
00162 
00163 void SummaryHandlingProxyModel::sourceLayoutChanged()
00164 {
00165     d->clearCache();
00166     BASE::sourceLayoutChanged();
00167 }
00168 
00169 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
00170 {
00171     QAbstractItemModel* model = sourceModel();
00172     QModelIndex parentIdx = from;
00173     do {
00174         const QModelIndex& dataIdx = parentIdx;
00175         if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
00176             //qDebug() << "removing " << parentIdx << "from cache";
00177             d->removeFromCache( dataIdx );
00178             QModelIndex proxyDataIdx = mapFromSource( dataIdx );
00179             emit dataChanged( proxyDataIdx, proxyDataIdx );
00180         }
00181     } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
00182 
00183     BASE::sourceDataChanged( from, to );
00184 }
00185 
00186 void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx,
00187                                                                     int start,
00188                                                                     int end )
00189 {
00190     BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
00191     d->clearCache();
00192 }
00193 
00194 void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx,
00195                                                                     int start,
00196                                                                     int end )
00197 {
00198     BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
00199     d->clearCache();
00200 }
00201 
00202 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
00203 {
00204     BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
00205     d->clearCache();
00206 }
00207 
00208 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
00209 {
00210     BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
00211     d->clearCache();
00212 }
00213 
00215 Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const
00216 {
00217     const QModelIndex sidx = mapToSource( idx );
00218     const QAbstractItemModel* model = sourceModel();
00219     Qt::ItemFlags f = model->flags( sidx );
00220     if ( d->isSummary(sidx) ) {
00221         f &= !Qt::ItemIsEditable;
00222     }
00223     return f;
00224 }
00225 
00227 QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
00228 {
00229   //qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
00230     const QModelIndex sidx = mapToSource( proxyIndex );
00231     const QAbstractItemModel* model = sourceModel();
00232     if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
00233       //qDebug() << "requested summary";
00234         QPair<QDateTime,QDateTime> result;
00235         if ( d->cacheLookup( sidx, &result ) ) {
00236           //qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
00237             switch( role ) {
00238             case StartTimeRole: return result.first;
00239             case EndTimeRole: return result.second;
00240             default: /* fall thru */;
00241             }
00242         } else {
00243             d->insertInCache( this, sidx );
00244             return data( proxyIndex, role ); /* TODO: Optimise */
00245         }
00246     }
00247     return model->data( sidx, role );
00248 }
00249 
00251 bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
00252 {
00253     QAbstractItemModel* model = sourceModel();
00254     if ( role==StartTimeRole || role==EndTimeRole ) {
00255         QModelIndex parentIdx = mapToSource( index );
00256         do {
00257             if ( d->isSummary(parentIdx) ) {
00258               //qDebug() << "removing " << parentIdx << "from cache";
00259                 d->removeFromCache( parentIdx );
00260                 QModelIndex proxyParentIdx = mapFromSource( parentIdx );
00261                 emit dataChanged( proxyParentIdx, proxyParentIdx );
00262             }
00263         } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
00264     }
00265     return BASE::setData( index, value, role );
00266 }
00267 
00268 #undef d
00269 
00270 #ifndef KDAB_NO_UNIT_TESTS
00271 
00272 #include "unittest/test.h"
00273 
00274 #include <QStandardItemModel>
00275 
00276 namespace {
00277     std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
00278     {
00279 #ifdef QT_NO_STL
00280         os << dt.toString().toLatin1().constData();
00281 #else
00282         os << dt.toString().toStdString();
00283 #endif
00284         return os;
00285     }
00286 }
00287 
00288 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, SummaryHandlingProxyModel, "test" ) {
00289     SummaryHandlingProxyModel model;
00290     QStandardItemModel sourceModel;
00291 
00292     model.setSourceModel( &sourceModel );
00293 
00294     QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
00295     topitem->setData( KDGantt::TypeSummary,  KDGantt::ItemTypeRole );
00296     sourceModel.appendRow( topitem );
00297 
00298     QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
00299     task1->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
00300     QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
00301     task2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
00302     topitem->appendRow( task1 );
00303     topitem->appendRow( task2 );
00304 
00305 
00306     QDateTime startdt = QDateTime::currentDateTime();
00307     QDateTime enddt = startdt.addDays( 1 );
00308 
00309 
00310     task1->setData( startdt, KDGantt::StartTimeRole );
00311     task1->setData( enddt, KDGantt::EndTimeRole );
00312     task2->setData( startdt, KDGantt::StartTimeRole );
00313     task2->setData( enddt, KDGantt::EndTimeRole );
00314 
00315     const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
00316 
00317     assertEqual( model.data( topidx, KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeSummary );
00318     assertEqual( model.data( model.index( 0, 0, topidx ), KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeTask );
00319 
00320     QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KDGantt::StartTimeRole ).toDateTime();
00321     assertEqual( task1startdt, startdt );
00322 
00323     QDateTime summarystartdt = model.data( topidx, KDGantt::StartTimeRole ).toDateTime();
00324     assertEqual( summarystartdt, startdt );
00325     assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
00326     assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
00327 }
00328 
00329 #endif /* KDAB_NO_UNIT_TESTS */
00330 
00331 #include "moc_kdganttsummaryhandlingproxymodel.cpp"