1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
4 ** This file is part of the KD Gantt library.
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
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"
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() ) {
66 void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel
* model
,
67 const QModelIndex
& sourceIdx
) const
69 QAbstractItemModel
* sourceModel
= model
->sourceModel();
70 const QModelIndex
& mainIdx
= sourceIdx
;
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";
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
120 SummaryHandlingProxyModel::SummaryHandlingProxyModel( QObject
* parent
)
121 : BASE( parent
), _d( new Private
)
127 SummaryHandlingProxyModel::~SummaryHandlingProxyModel()
131 void SummaryHandlingProxyModel::init()
137 // Think this is ugly? Well, it's not from me, it comes from QProxyModel
138 struct KDPrivateModelIndex
{
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
);
155 void SummaryHandlingProxyModel::sourceModelReset()
158 BASE::sourceModelReset();
161 void SummaryHandlingProxyModel::sourceLayoutChanged()
164 BASE::sourceLayoutChanged();
167 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex
& from
, const QModelIndex
& to
)
169 QAbstractItemModel
* model
= sourceModel();
170 QModelIndex parentIdx
= from
;
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
,
188 BASE::sourceColumnsAboutToBeInserted( parentIdx
, start
, end
);
192 void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex
& parentIdx
,
196 BASE::sourceColumnsAboutToBeRemoved( parentIdx
, start
, end
);
200 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex
& parentIdx
, int start
, int end
)
202 BASE::sourceRowsAboutToBeInserted( parentIdx
, start
, end
);
206 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex
& parentIdx
, int start
, int end
)
208 BASE::sourceRowsAboutToBeRemoved( parentIdx
, start
, end
);
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
;
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;
236 case StartTimeRole
: return result
.first
;
237 case EndTimeRole
: return result
.second
;
238 default: /* fall thru */;
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
);
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
);
268 #ifndef KDAB_NO_UNIT_TESTS
270 #include "unittest/test.h"
272 #include <QStandardItemModel>
275 std::ostream
& operator<<( std::ostream
& os
, const QDateTime
& dt
)
278 os
<< dt
.toString().toLatin1().constData();
280 os
<< dt
.toString().toStdString();
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"