File indexing completed on 2024-05-12 04:20:45
0001 /* 0002 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. 0003 * 0004 * This file is part of the KGantt library. 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "kganttsummaryhandlingproxymodel.h" 0010 #include "kganttsummaryhandlingproxymodel_p.h" 0011 0012 #include <QDebug> 0013 0014 #include <cassert> 0015 0016 using namespace KGantt; 0017 0018 0019 0020 typedef ForwardingProxyModel BASE; 0021 0022 bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx, 0023 QPair<QDateTime,QDateTime>* result ) const 0024 { 0025 //qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items"; 0026 QHash<QModelIndex,QPair<QDateTime,QDateTime> >::const_iterator it = 0027 cached_summary_items.constFind( idx ); 0028 if ( it != cached_summary_items.constEnd() ) { 0029 *result = *it; 0030 return true; 0031 } else { 0032 return false; 0033 } 0034 } 0035 0036 void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model, 0037 const QModelIndex& sourceIdx ) const 0038 { 0039 QAbstractItemModel* sourceModel = model->sourceModel(); 0040 const QModelIndex& mainIdx = sourceIdx; 0041 QDateTime st; 0042 QDateTime et; 0043 0044 for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) { 0045 QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) ); 0046 /* The probably results in recursive calls here */ 0047 QVariant tmpsv = model->data( pdIdx, StartTimeRole ); 0048 QVariant tmpev = model->data( pdIdx, EndTimeRole ); 0049 if ( !tmpsv.canConvert( QVariant::DateTime ) || 0050 !tmpev.canConvert( QVariant::DateTime ) ) { 0051 qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime"; 0052 continue; 0053 } 0054 0055 // check for valid datetimes 0056 if ( tmpsv.type() == QVariant::DateTime && !tmpsv.value<QDateTime>().isValid()) continue; 0057 if ( tmpev.type() == QVariant::DateTime && !tmpev.value<QDateTime>().isValid()) continue; 0058 0059 // We need to test for empty strings to 0060 // avoid a stupid Qt warning 0061 if ( tmpsv.type() == QVariant::String && tmpsv.value<QString>().isEmpty()) continue; 0062 if ( tmpev.type() == QVariant::String && tmpev.value<QString>().isEmpty()) continue; 0063 QDateTime tmpst = tmpsv.toDateTime(); 0064 QDateTime tmpet = tmpev.toDateTime(); 0065 if ( st.isNull() || st > tmpst ) st = tmpst; 0066 if ( et.isNull() || et < tmpet ) et = tmpet; 0067 } 0068 QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole ); 0069 QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole ); 0070 if ( tmpssv.canConvert( QVariant::DateTime ) 0071 && !( tmpssv.canConvert( QVariant::String ) && tmpssv.toString().isEmpty() ) 0072 && tmpssv.toDateTime() != st ) 0073 sourceModel->setData( mainIdx, st, StartTimeRole ); 0074 if ( tmpsev.canConvert( QVariant::DateTime ) 0075 && !( tmpsev.canConvert( QVariant::String ) && tmpsev.toString().isEmpty() ) 0076 && tmpsev.toDateTime() != et ) 0077 sourceModel->setData( mainIdx, et, EndTimeRole ); 0078 cached_summary_items[sourceIdx]=qMakePair( st, et ); 0079 } 0080 0081 void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const 0082 { 0083 cached_summary_items.remove( idx ); 0084 } 0085 0086 void SummaryHandlingProxyModel::Private::clearCache() const 0087 { 0088 cached_summary_items.clear(); 0089 } 0090 0091 0092 SummaryHandlingProxyModel::SummaryHandlingProxyModel( QObject* parent ) 0093 : BASE( parent ), _d( new Private ) 0094 { 0095 init(); 0096 } 0097 0098 #define d d_func() 0099 SummaryHandlingProxyModel::~SummaryHandlingProxyModel() 0100 { 0101 delete _d; 0102 } 0103 0104 void SummaryHandlingProxyModel::init() 0105 { 0106 } 0107 0108 0109 void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model ) 0110 { 0111 BASE::setSourceModel( model ); 0112 d->clearCache(); 0113 } 0114 0115 void SummaryHandlingProxyModel::sourceModelReset() 0116 { 0117 d->clearCache(); 0118 BASE::sourceModelReset(); 0119 } 0120 0121 void SummaryHandlingProxyModel::sourceLayoutChanged() 0122 { 0123 d->clearCache(); 0124 BASE::sourceLayoutChanged(); 0125 } 0126 0127 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to ) 0128 { 0129 QAbstractItemModel* model = sourceModel(); 0130 QModelIndex parentIdx = from; 0131 do { 0132 const QModelIndex& dataIdx = parentIdx; 0133 if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) { 0134 //qDebug() << "removing " << parentIdx << "from cache"; 0135 d->removeFromCache( dataIdx ); 0136 QModelIndex proxyDataIdx = mapFromSource( dataIdx ); 0137 Q_EMIT dataChanged( proxyDataIdx, proxyDataIdx ); 0138 } 0139 } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() ); 0140 0141 BASE::sourceDataChanged( from, to ); 0142 } 0143 0144 void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx, 0145 int start, 0146 int end ) 0147 { 0148 BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end ); 0149 d->clearCache(); 0150 } 0151 0152 void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx, 0153 int start, 0154 int end ) 0155 { 0156 BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end ); 0157 d->clearCache(); 0158 } 0159 0160 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end ) 0161 { 0162 BASE::sourceRowsAboutToBeInserted( parentIdx, start, end ); 0163 d->clearCache(); 0164 } 0165 0166 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end ) 0167 { 0168 BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end ); 0169 d->clearCache(); 0170 } 0171 0172 0173 Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const 0174 { 0175 const QModelIndex sidx = mapToSource( idx ); 0176 const QAbstractItemModel* model = sourceModel(); 0177 Qt::ItemFlags f = model->flags( sidx ); 0178 if ( d->isSummary(sidx) ) { 0179 f &= ~Qt::ItemIsEditable; 0180 } 0181 return f; 0182 } 0183 0184 0185 QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const 0186 { 0187 //qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")"; 0188 const QModelIndex sidx = mapToSource( proxyIndex ); 0189 const QAbstractItemModel* model = sourceModel(); 0190 if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) { 0191 //qDebug() << "requested summary"; 0192 QPair<QDateTime,QDateTime> result; 0193 if ( d->cacheLookup( sidx, &result ) ) { 0194 //qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role; 0195 switch ( role ) { 0196 case StartTimeRole: return result.first; 0197 case EndTimeRole: return result.second; 0198 default: /* fall thru */; 0199 } 0200 } else { 0201 d->insertInCache( this, sidx ); 0202 return data( proxyIndex, role ); /* TODO: Optimize */ 0203 } 0204 } 0205 return model->data( sidx, role ); 0206 } 0207 0208 0209 bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role ) 0210 { 0211 QAbstractItemModel* model = sourceModel(); 0212 if ( role==StartTimeRole || role==EndTimeRole ) { 0213 QModelIndex parentIdx = mapToSource( index ); 0214 do { 0215 if ( d->isSummary(parentIdx) ) { 0216 //qDebug() << "removing " << parentIdx << "from cache"; 0217 d->removeFromCache( parentIdx ); 0218 QModelIndex proxyParentIdx = mapFromSource( parentIdx ); 0219 Q_EMIT dataChanged( proxyParentIdx, proxyParentIdx ); 0220 } 0221 } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() ); 0222 } 0223 return BASE::setData( index, value, role ); 0224 } 0225 0226 #undef d 0227 0228 #ifndef KDAB_NO_UNIT_TESTS 0229 0230 #include "unittest/test.h" 0231 0232 #include <QStandardItemModel> 0233 0234 static std::ostream& operator<<( std::ostream& os, const QDateTime& dt ) 0235 { 0236 #ifdef QT_NO_STL 0237 os << dt.toString().toLatin1().constData(); 0238 #else 0239 os << dt.toString().toStdString(); 0240 #endif 0241 return os; 0242 } 0243 0244 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, SummaryHandlingProxyModel, "test" ) { 0245 SummaryHandlingProxyModel model; 0246 QStandardItemModel sourceModel; 0247 0248 model.setSourceModel( &sourceModel ); 0249 0250 QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) ); 0251 topitem->setData( KGantt::TypeSummary, KGantt::ItemTypeRole ); 0252 sourceModel.appendRow( topitem ); 0253 0254 QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) ); 0255 task1->setData( KGantt::TypeTask, KGantt::ItemTypeRole ); 0256 QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) ); 0257 task2->setData( KGantt::TypeTask, KGantt::ItemTypeRole ); 0258 topitem->appendRow( task1 ); 0259 topitem->appendRow( task2 ); 0260 0261 0262 QDateTime startdt = QDateTime::currentDateTime(); 0263 QDateTime enddt = startdt.addDays( 1 ); 0264 0265 0266 task1->setData( startdt, KGantt::StartTimeRole ); 0267 task1->setData( enddt, KGantt::EndTimeRole ); 0268 task2->setData( startdt, KGantt::StartTimeRole ); 0269 task2->setData( enddt, KGantt::EndTimeRole ); 0270 0271 const QModelIndex topidx = model.index( 0, 0, QModelIndex() ); 0272 0273 assertEqual( model.data( topidx, KGantt::ItemTypeRole ).toInt(), KGantt::TypeSummary ); 0274 assertEqual( model.data( model.index( 0, 0, topidx ), KGantt::ItemTypeRole ).toInt(), KGantt::TypeTask ); 0275 0276 QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KGantt::StartTimeRole ).toDateTime(); 0277 assertEqual( task1startdt, startdt ); 0278 0279 QDateTime summarystartdt = model.data( topidx, KGantt::StartTimeRole ).toDateTime(); 0280 assertEqual( summarystartdt, startdt ); 0281 assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable ); 0282 assertFalse( model.flags( topidx ) & Qt::ItemIsEditable ); 0283 } 0284 0285 #endif /* KDAB_NO_UNIT_TESTS */ 0286 0287 #include "moc_kganttsummaryhandlingproxymodel.cpp"