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