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 used under the terms of the GNU General Public
7
** License versions 2.0 or 3.0 as published by the Free Software
8
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
9
** included in the packaging of this file. Alternatively you may (at
10
** your option) use any later version of the GNU General Public
11
** License if such license has been publicly approved by
12
** Klarälvdalens Datakonsult AB (or its successors, if any).
14
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
15
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
16
** A PARTICULAR PURPOSE. Klarälvdalens Datakonsult AB reserves all rights
17
** not expressly granted herein.
19
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22
**********************************************************************/
23
#include "kdganttsummaryhandlingproxymodel.h"
24
#include "kdganttsummaryhandlingproxymodel_p.h"
30
using namespace KDGantt;
32
/*!\class KDGantt::SummaryHandlingProxyModel
33
* \brief Proxy model that supports summary gantt items.
35
* This proxy model provides the functionality of summary items.
36
* A summary item is an item with type KDGantt::TypeSummary and
37
* zero or more children in the model that it summarizes.
38
* GraphicsView itself does not dictate any policy for summary items,
39
* instead the logic for making the summary items start and end points
40
* span it's children is provided by this proxy.
42
* The start and end times of a summary is the min/max of the
43
* start/end times of it's children.
45
* \see GraphicsView::setModel
48
typedef ForwardingProxyModel BASE;
50
bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx,
51
QPair<QDateTime,QDateTime>* result ) const
53
//qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
54
QHash<QModelIndex,QPair<QDateTime,QDateTime> >::const_iterator it =
55
cached_summary_items.constFind( idx );
56
if ( it != cached_summary_items.constEnd() ) {
64
void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model,
65
const QModelIndex& sourceIdx ) const
67
QAbstractItemModel* sourceModel = model->sourceModel();
68
const QModelIndex& mainIdx = sourceIdx;
72
for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) {
73
QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) );
74
/* The probably results in recursive calls here */
75
QVariant tmpsv = model->data( pdIdx, StartTimeRole );
76
QVariant tmpev = model->data( pdIdx, EndTimeRole );
77
if( !qVariantCanConvert<QDateTime>(tmpsv) ||
78
!qVariantCanConvert<QDateTime>(tmpev) ) {
79
qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
83
// check for valid datetimes
84
if( tmpsv.type() == QVariant::DateTime && !qVariantValue<QDateTime>(tmpsv).isValid()) continue;
85
if( tmpev.type() == QVariant::DateTime && !qVariantValue<QDateTime>(tmpev).isValid()) continue;
87
// We need to test for empty strings to
88
// avoid a stupid Qt warning
89
if( tmpsv.type() == QVariant::String && qVariantValue<QString>(tmpsv).isEmpty()) continue;
90
if( tmpev.type() == QVariant::String && qVariantValue<QString>(tmpev).isEmpty()) continue;
91
QDateTime tmpst = tmpsv.toDateTime();
92
QDateTime tmpet = tmpev.toDateTime();
93
if ( st.isNull() || st > tmpst ) st = tmpst;
94
if ( et.isNull() || et < tmpet ) et = tmpet;
96
QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole );
97
QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole );
98
if ( qVariantCanConvert<QDateTime>( tmpssv )
99
&& !( qVariantCanConvert<QString>( tmpssv ) && qVariantValue<QString>( tmpssv ).isEmpty() )
100
&& qVariantValue<QDateTime>( tmpssv ) != st )
101
sourceModel->setData( mainIdx, st, StartTimeRole );
102
if ( qVariantCanConvert<QDateTime>( tmpsev )
103
&& !( qVariantCanConvert<QString>( tmpsev ) && qVariantValue<QString>( tmpsev ).isEmpty() )
104
&& qVariantValue<QDateTime>( tmpsev ) != et )
105
sourceModel->setData( mainIdx, et, EndTimeRole );
106
cached_summary_items[sourceIdx]=qMakePair( st, et );
109
void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const
111
cached_summary_items.remove( idx );
114
void SummaryHandlingProxyModel::Private::clearCache() const
116
cached_summary_items.clear();
119
/*! Constructor. Creates a new SummaryHandlingProxyModel with
122
SummaryHandlingProxyModel::SummaryHandlingProxyModel( QObject* parent )
123
: BASE( parent ), _d( new Private )
129
SummaryHandlingProxyModel::~SummaryHandlingProxyModel()
134
void SummaryHandlingProxyModel::init()
140
// Think this is ugly? Well, it's not from me, it comes from QProxyModel
141
struct KDPrivateModelIndex {
144
const QAbstractItemModel *m;
148
/*! Sets the model to be used as the source model for this proxy.
149
* The proxy does not take ownership of the model.
150
* \see QAbstractProxyModel::setSourceModel
152
void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model )
154
BASE::setSourceModel( model );
158
void SummaryHandlingProxyModel::sourceModelReset()
161
BASE::sourceModelReset();
164
void SummaryHandlingProxyModel::sourceLayoutChanged()
167
BASE::sourceLayoutChanged();
170
void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
172
QAbstractItemModel* model = sourceModel();
173
QModelIndex parentIdx = from;
175
const QModelIndex& dataIdx = parentIdx;
176
if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
177
//qDebug() << "removing " << parentIdx << "from cache";
178
d->removeFromCache( dataIdx );
179
QModelIndex proxyDataIdx = mapFromSource( dataIdx );
180
emit dataChanged( proxyDataIdx, proxyDataIdx );
182
} while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
184
BASE::sourceDataChanged( from, to );
187
void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx,
191
BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
195
void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx,
199
BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
203
void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
205
BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
209
void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
211
BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
215
/*! \see QAbstractItemModel::flags */
216
Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const
218
const QModelIndex sidx = mapToSource( idx );
219
const QAbstractItemModel* model = sourceModel();
220
Qt::ItemFlags f = model->flags( sidx );
221
if ( d->isSummary(sidx) ) {
222
f &= !Qt::ItemIsEditable;
227
/*! \see QAbstractItemModel::data */
228
QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
230
//qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
231
const QModelIndex sidx = mapToSource( proxyIndex );
232
const QAbstractItemModel* model = sourceModel();
233
if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
234
//qDebug() << "requested summary";
235
QPair<QDateTime,QDateTime> result;
236
if ( d->cacheLookup( sidx, &result ) ) {
237
//qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
239
case StartTimeRole: return result.first;
240
case EndTimeRole: return result.second;
241
default: /* fall thru */;
244
d->insertInCache( this, sidx );
245
return data( proxyIndex, role ); /* TODO: Optimise */
248
return model->data( sidx, role );
251
/*! \see QAbstractItemModel::setData */
252
bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
254
QAbstractItemModel* model = sourceModel();
255
if ( role==StartTimeRole || role==EndTimeRole ) {
256
QModelIndex parentIdx = mapToSource( index );
258
if ( d->isSummary(parentIdx) ) {
259
//qDebug() << "removing " << parentIdx << "from cache";
260
d->removeFromCache( parentIdx );
261
QModelIndex proxyParentIdx = mapFromSource( parentIdx );
262
emit dataChanged( proxyParentIdx, proxyParentIdx );
264
} while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
266
return BASE::setData( index, value, role );
271
#ifndef KDAB_NO_UNIT_TESTS
273
#include "unittest/test.h"
275
#include <QStandardItemModel>
278
std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
280
os << dt.toString().toStdString();
285
KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, SummaryHandlingProxyModel, "test" ) {
286
SummaryHandlingProxyModel model;
287
QStandardItemModel sourceModel;
289
model.setSourceModel( &sourceModel );
291
QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
292
topitem->setData( KDGantt::TypeSummary, KDGantt::ItemTypeRole );
293
sourceModel.appendRow( topitem );
295
QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
296
task1->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
297
QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
298
task2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
299
topitem->appendRow( task1 );
300
topitem->appendRow( task2 );
303
QDateTime startdt = QDateTime::currentDateTime();
304
QDateTime enddt = startdt.addDays( 1 );
307
task1->setData( startdt, KDGantt::StartTimeRole );
308
task1->setData( enddt, KDGantt::EndTimeRole );
309
task2->setData( startdt, KDGantt::StartTimeRole );
310
task2->setData( enddt, KDGantt::EndTimeRole );
312
const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
314
assertEqual( model.data( topidx, KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeSummary );
315
assertEqual( model.data( model.index( 0, 0, topidx ), KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeTask );
317
QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KDGantt::StartTimeRole ).toDateTime();
318
assertEqual( task1startdt, startdt );
320
QDateTime summarystartdt = model.data( topidx, KDGantt::StartTimeRole ).toDateTime();
321
assertEqual( summarystartdt, startdt );
322
assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
323
assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
326
#endif /* KDAB_NO_UNIT_TESTS */
328
#include "moc_kdganttsummaryhandlingproxymodel.cpp"