Add KMessageWidget-based inline warning about disabled Nepomuk.
[kdepim.git] / kdgantt2 / kdganttsummaryhandlingproxymodel.cpp
blob7b53c3ae0f70abd0cad05b18d533429a46bd7a08
1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Gantt library.
5 **
6 ** This file may be distributed and/or modified under the terms of the
7 ** GNU General Public License version 2 as published by the Free Software
8 ** Foundation and appearing in the file LICENSE.GPL included in the
9 ** packaging of this file.
11 ** Licensees holding valid commercial KD Gantt licenses may use this file in
12 ** accordance with the KD Gantt Commercial License Agreement provided with
13 ** the Software.
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 ** See http://www.kdab.net/kdgantt for
19 ** information about KD Gantt Commercial License Agreements.
21 ** Contact info@kdab.net if any conditions of this
22 ** licensing are not clear to you.
24 **********************************************************************/
25 #include "kdganttsummaryhandlingproxymodel.h"
26 #include "kdganttsummaryhandlingproxymodel_p.h"
28 #include <QDebug>
30 #include <cassert>
32 using namespace KDGantt;
34 /*!\class KDGantt::SummaryHandlingProxyModel
35 * \brief Proxy model that supports summary gantt items.
37 * This proxy model provides the functionality of summary items.
38 * A summary item is an item with type KDGantt::TypeSummary and
39 * zero or more children in the model that it summarizes.
40 * GraphicsView itself does not dictate any policy for summary items,
41 * instead the logic for making the summary items start and end points
42 * span it's children is provided by this proxy.
44 * The start and end times of a summary is the min/max of the
45 * start/end times of it's children.
47 * \see GraphicsView::setModel
50 typedef ForwardingProxyModel BASE;
52 bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx,
53 QPair<QDateTime,QDateTime>* result ) const
55 //qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
56 QHash<QModelIndex,QPair<QDateTime,QDateTime> >::const_iterator it =
57 cached_summary_items.constFind( idx );
58 if ( it != cached_summary_items.constEnd() ) {
59 *result = *it;
60 return true;
61 } else {
62 return false;
66 void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model,
67 const QModelIndex& sourceIdx ) const
69 QAbstractItemModel* sourceModel = model->sourceModel();
70 const QModelIndex& mainIdx = sourceIdx;
71 QDateTime st;
72 QDateTime et;
74 for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) {
75 QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) );
76 /* The probably results in recursive calls here */
77 QVariant tmpsv = model->data( pdIdx, StartTimeRole );
78 QVariant tmpev = model->data( pdIdx, EndTimeRole );
79 if( !qVariantCanConvert<QDateTime>(tmpsv) ||
80 !qVariantCanConvert<QDateTime>(tmpev) ) {
81 qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
82 continue;
85 // We need to test for empty strings to
86 // avoid a stupid Qt warning
87 if( tmpsv.type() == QVariant::String && qVariantValue<QString>(tmpsv).isEmpty()) continue;
88 if( tmpev.type() == QVariant::String && qVariantValue<QString>(tmpev).isEmpty()) continue;
89 QDateTime tmpst = tmpsv.toDateTime();
90 QDateTime tmpet = tmpev.toDateTime();
91 if ( st.isNull() || st > tmpst ) st = tmpst;
92 if ( et.isNull() || et < tmpet ) et = tmpet;
94 QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole );
95 QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole );
96 if ( qVariantCanConvert<QDateTime>( tmpssv )
97 && !( qVariantCanConvert<QString>( tmpssv ) && qVariantValue<QString>( tmpssv ).isEmpty() )
98 && qVariantValue<QDateTime>( tmpssv ) != st )
99 sourceModel->setData( mainIdx, st, StartTimeRole );
100 if ( qVariantCanConvert<QDateTime>( tmpsev )
101 && !( qVariantCanConvert<QString>( tmpsev ) && qVariantValue<QString>( tmpsev ).isEmpty() )
102 && qVariantValue<QDateTime>( tmpsev ) != et )
103 sourceModel->setData( mainIdx, et, EndTimeRole );
104 cached_summary_items[sourceIdx]=qMakePair( st, et );
107 void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const
109 cached_summary_items.remove( idx );
112 void SummaryHandlingProxyModel::Private::clearCache() const
114 cached_summary_items.clear();
117 /*! Constructor. Creates a new SummaryHandlingProxyModel with
118 * parent \a parent
120 SummaryHandlingProxyModel::SummaryHandlingProxyModel( QObject* parent )
121 : BASE( parent ), _d( new Private )
123 init();
126 #define d d_func()
127 SummaryHandlingProxyModel::~SummaryHandlingProxyModel()
131 void SummaryHandlingProxyModel::init()
135 namespace {
137 // Think this is ugly? Well, it's not from me, it comes from QProxyModel
138 struct KDPrivateModelIndex {
139 int r, c;
140 void *p;
141 const QAbstractItemModel *m;
145 /*! Sets the model to be used as the source model for this proxy.
146 * The proxy does not take ownership of the model.
147 * \see QAbstractProxyModel::setSourceModel
149 void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model )
151 BASE::setSourceModel( model );
152 d->clearCache();
155 void SummaryHandlingProxyModel::sourceModelReset()
157 d->clearCache();
158 BASE::sourceModelReset();
161 void SummaryHandlingProxyModel::sourceLayoutChanged()
163 d->clearCache();
164 BASE::sourceLayoutChanged();
167 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
169 QAbstractItemModel* model = sourceModel();
170 QModelIndex parentIdx = from;
171 do {
172 const QModelIndex& dataIdx = parentIdx;
173 if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
174 //qDebug() << "removing " << parentIdx << "from cache";
175 d->removeFromCache( dataIdx );
176 QModelIndex proxyDataIdx = mapFromSource( dataIdx );
177 emit dataChanged( proxyDataIdx, proxyDataIdx );
179 } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
181 BASE::sourceDataChanged( from, to );
184 void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx,
185 int start,
186 int end )
188 BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
189 d->clearCache();
192 void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx,
193 int start,
194 int end )
196 BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
197 d->clearCache();
200 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
202 BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
203 d->clearCache();
206 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
208 BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
209 d->clearCache();
212 /*! \see QAbstractItemModel::flags */
213 Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const
215 const QModelIndex sidx = mapToSource( idx );
216 const QAbstractItemModel* model = sourceModel();
217 Qt::ItemFlags f = model->flags( sidx );
218 if ( d->isSummary(sidx) ) {
219 f &= !Qt::ItemIsEditable;
221 return f;
224 /*! \see QAbstractItemModel::data */
225 QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
227 //qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
228 const QModelIndex sidx = mapToSource( proxyIndex );
229 const QAbstractItemModel* model = sourceModel();
230 if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
231 //qDebug() << "requested summary";
232 QPair<QDateTime,QDateTime> result;
233 if ( d->cacheLookup( sidx, &result ) ) {
234 //qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
235 switch( role ) {
236 case StartTimeRole: return result.first;
237 case EndTimeRole: return result.second;
238 default: /* fall thru */;
240 } else {
241 d->insertInCache( this, sidx );
242 return data( proxyIndex, role ); /* TODO: Optimise */
245 return model->data( sidx, role );
248 /*! \see QAbstractItemModel::setData */
249 bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
251 QAbstractItemModel* model = sourceModel();
252 if ( role==StartTimeRole || role==EndTimeRole ) {
253 QModelIndex parentIdx = mapToSource( index );
254 do {
255 if ( d->isSummary(parentIdx) ) {
256 //qDebug() << "removing " << parentIdx << "from cache";
257 d->removeFromCache( parentIdx );
258 QModelIndex proxyParentIdx = mapFromSource( parentIdx );
259 emit dataChanged( proxyParentIdx, proxyParentIdx );
261 } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
263 return BASE::setData( index, value, role );
266 #undef d
268 #ifndef KDAB_NO_UNIT_TESTS
270 #include "unittest/test.h"
272 #include <QStandardItemModel>
274 namespace {
275 std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
277 #ifdef QT_NO_STL
278 os << dt.toString().toLatin1().constData();
279 #else
280 os << dt.toString().toStdString();
281 #endif
282 return os;
286 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, SummaryHandlingProxyModel, "test" ) {
287 SummaryHandlingProxyModel model;
288 QStandardItemModel sourceModel;
290 model.setSourceModel( &sourceModel );
292 QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
293 topitem->setData( KDGantt::TypeSummary, KDGantt::ItemTypeRole );
294 sourceModel.appendRow( topitem );
296 QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
297 task1->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
298 QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
299 task2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
300 topitem->appendRow( task1 );
301 topitem->appendRow( task2 );
304 QDateTime startdt = QDateTime::currentDateTime();
305 QDateTime enddt = startdt.addDays( 1 );
308 task1->setData( startdt, KDGantt::StartTimeRole );
309 task1->setData( enddt, KDGantt::EndTimeRole );
310 task2->setData( startdt, KDGantt::StartTimeRole );
311 task2->setData( enddt, KDGantt::EndTimeRole );
313 const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
315 assertEqual( model.data( topidx, KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeSummary );
316 assertEqual( model.data( model.index( 0, 0, topidx ), KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeTask );
318 QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KDGantt::StartTimeRole ).toDateTime();
319 assertEqual( task1startdt, startdt );
321 QDateTime summarystartdt = model.data( topidx, KDGantt::StartTimeRole ).toDateTime();
322 assertEqual( summarystartdt, startdt );
323 assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
324 assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
327 #endif /* KDAB_NO_UNIT_TESTS */
329 #include "moc_kdganttsummaryhandlingproxymodel.cpp"