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