KD Chart 2  [rev.2.5]
kdganttsummaryhandlingproxymodel.cpp
Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2012 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.txt 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     delete _d;
00132 }
00133 
00134 void SummaryHandlingProxyModel::init()
00135 {
00136 }
00137 
00138 namespace {
00139 
00140     // Think this is ugly? Well, it's not from me, it comes from QProxyModel
00141     struct KDPrivateModelIndex {
00142         int r, c;
00143         void *p;
00144         const QAbstractItemModel *m;
00145     };
00146 }
00147 
00152 void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model )
00153 {
00154     BASE::setSourceModel( model );
00155     d->clearCache();
00156 }
00157 
00158 void SummaryHandlingProxyModel::sourceModelReset()
00159 {
00160     d->clearCache();
00161     BASE::sourceModelReset();
00162 }
00163 
00164 void SummaryHandlingProxyModel::sourceLayoutChanged()
00165 {
00166     d->clearCache();
00167     BASE::sourceLayoutChanged();
00168 }
00169 
00170 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
00171 {
00172     QAbstractItemModel* model = sourceModel();
00173     QModelIndex parentIdx = from;
00174     do {
00175         const QModelIndex& dataIdx = parentIdx;
00176         if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
00177             //qDebug() << "removing " << parentIdx << "from cache";
00178             d->removeFromCache( dataIdx );
00179             QModelIndex proxyDataIdx = mapFromSource( dataIdx );
00180             emit dataChanged( proxyDataIdx, proxyDataIdx );
00181         }
00182     } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
00183 
00184     BASE::sourceDataChanged( from, to );
00185 }
00186 
00187 void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx,
00188                                                                     int start,
00189                                                                     int end )
00190 {
00191     BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
00192     d->clearCache();
00193 }
00194 
00195 void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx,
00196                                                                     int start,
00197                                                                     int end )
00198 {
00199     BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
00200     d->clearCache();
00201 }
00202 
00203 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
00204 {
00205     BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
00206     d->clearCache();
00207 }
00208 
00209 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
00210 {
00211     BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
00212     d->clearCache();
00213 }
00214 
00216 Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const
00217 {
00218     const QModelIndex sidx = mapToSource( idx );
00219     const QAbstractItemModel* model = sourceModel();
00220     Qt::ItemFlags f = model->flags( sidx );
00221     if ( d->isSummary(sidx) ) {
00222         f &= !Qt::ItemIsEditable;
00223     }
00224     return f;
00225 }
00226 
00228 QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
00229 {
00230   //qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
00231     const QModelIndex sidx = mapToSource( proxyIndex );
00232     const QAbstractItemModel* model = sourceModel();
00233     if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
00234       //qDebug() << "requested summary";
00235         QPair<QDateTime,QDateTime> result;
00236         if ( d->cacheLookup( sidx, &result ) ) {
00237           //qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
00238             switch( role ) {
00239             case StartTimeRole: return result.first;
00240             case EndTimeRole: return result.second;
00241             default: /* fall thru */;
00242             }
00243         } else {
00244             d->insertInCache( this, sidx );
00245             return data( proxyIndex, role ); /* TODO: Optimise */
00246         }
00247     }
00248     return model->data( sidx, role );
00249 }
00250 
00252 bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
00253 {
00254     QAbstractItemModel* model = sourceModel();
00255     if ( role==StartTimeRole || role==EndTimeRole ) {
00256         QModelIndex parentIdx = mapToSource( index );
00257         do {
00258             if ( d->isSummary(parentIdx) ) {
00259               //qDebug() << "removing " << parentIdx << "from cache";
00260                 d->removeFromCache( parentIdx );
00261                 QModelIndex proxyParentIdx = mapFromSource( parentIdx );
00262                 emit dataChanged( proxyParentIdx, proxyParentIdx );
00263             }
00264         } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
00265     }
00266     return BASE::setData( index, value, role );
00267 }
00268 
00269 #undef d
00270 
00271 #ifndef KDAB_NO_UNIT_TESTS
00272 
00273 #include "unittest/test.h"
00274 
00275 #include <QStandardItemModel>
00276 
00277 static 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 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, SummaryHandlingProxyModel, "test" ) {
00288     SummaryHandlingProxyModel model;
00289     QStandardItemModel sourceModel;
00290 
00291     model.setSourceModel( &sourceModel );
00292 
00293     QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
00294     topitem->setData( KDGantt::TypeSummary,  KDGantt::ItemTypeRole );
00295     sourceModel.appendRow( topitem );
00296 
00297     QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
00298     task1->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
00299     QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
00300     task2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
00301     topitem->appendRow( task1 );
00302     topitem->appendRow( task2 );
00303 
00304 
00305     QDateTime startdt = QDateTime::currentDateTime();
00306     QDateTime enddt = startdt.addDays( 1 );
00307 
00308 
00309     task1->setData( startdt, KDGantt::StartTimeRole );
00310     task1->setData( enddt, KDGantt::EndTimeRole );
00311     task2->setData( startdt, KDGantt::StartTimeRole );
00312     task2->setData( enddt, KDGantt::EndTimeRole );
00313 
00314     const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
00315 
00316     assertEqual( model.data( topidx, KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeSummary );
00317     assertEqual( model.data( model.index( 0, 0, topidx ), KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeTask );
00318 
00319     QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KDGantt::StartTimeRole ).toDateTime();
00320     assertEqual( task1startdt, startdt );
00321 
00322     QDateTime summarystartdt = model.data( topidx, KDGantt::StartTimeRole ).toDateTime();
00323     assertEqual( summarystartdt, startdt );
00324     assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
00325     assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
00326 }
00327 
00328 #endif /* KDAB_NO_UNIT_TESTS */
00329 
00330 #include "moc_kdganttsummaryhandlingproxymodel.cpp"
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/