KD Chart 2
[rev.2.5]
|
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"