1/* This file is part of the KDE project
2 Copyright (C) 2007, 2012 Dag Andersen <danders@get2net.dk>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17* Boston, MA 02110-1301, USA.
18*/
19
20#include "kptworkpackagemodel.h"
21
22#include "kptglobal.h"
23#include "kptcommonstrings.h"
24#include "kptflatproxymodel.h"
25#include "kptnodeitemmodel.h"
26#include "kptnode.h"
27#include "kpttask.h"
28#include "kptproject.h"
29#include "kptschedule.h"
30#include "kptdebug.h"
31
32#include <QModelIndex>
33#include <QVariant>
34#include <QRegExp>
35
36namespace KPlato
37{
38
39QVariant WorkPackageModel::nodeName( const WorkPackage *wp, int role ) const
40{
41 switch ( role ) {
42 case Qt::DisplayRole:
43 case Qt::ToolTipRole:
44 return wp->parentTask() ? wp->parentTask()->name() : "";
45 case Qt::EditRole:
46 return wp->parentTask() ? wp->parentTask()->name() : "";
47 case Qt::StatusTipRole:
48 case Qt::WhatsThisRole:
49 return QVariant();
50 }
51 return QVariant();
52}
53
54
55QVariant WorkPackageModel::ownerName( const WorkPackage *wp, int role ) const
56{
57 switch ( role ) {
58 case Qt::DisplayRole:
59 case Qt::ToolTipRole:
60 return wp->ownerName();
61 case Qt::EditRole:
62 return wp->ownerName();
63 case Qt::StatusTipRole:
64 case Qt::WhatsThisRole:
65 return QVariant();
66 }
67 return QVariant();
68}
69
70QVariant WorkPackageModel::transmitionStatus( const WorkPackage *wp, int role ) const
71{
72 switch ( role ) {
73 case Qt::DisplayRole:
74 return wp->transmitionStatusToString( wp->transmitionStatus(), true );
75 case Qt::EditRole:
76 return wp->transmitionStatus();
77 case Qt::ToolTipRole: {
78 int sts = wp->transmitionStatus();
79 if ( sts == WorkPackage::TS_Send ) {
80 return i18n( "Sent to %1 at %2", wp->ownerName(), transmitionTime( wp, Qt::DisplayRole ).toString() );
81 }
82 if ( sts == WorkPackage::TS_Receive ) {
83 return i18n( "Received from %1 at %2", wp->ownerName(), transmitionTime( wp, Qt::DisplayRole ).toString() );
84 }
85 return i18n( "Not available" );
86 }
87 case Qt::StatusTipRole:
88 case Qt::WhatsThisRole:
89 return QVariant();
90 }
91 return QVariant();
92}
93
94QVariant WorkPackageModel::transmitionTime( const WorkPackage *wp, int role ) const
95{
96 if ( ! wp ) {
97 return QVariant();
98 }
99 switch ( role ) {
100 case Qt::DisplayRole:
101 return KGlobal::locale()->formatDateTime( wp->transmitionTime() );
102 case Qt::EditRole:
103 return wp->transmitionTime();
104 case Qt::ToolTipRole: {
105 int sts = wp->transmitionStatus();
106 QString t = KGlobal::locale()->formatDateTime( wp->transmitionTime(), KLocale::LongDate, KLocale::TimeZone );
107 if ( sts == WorkPackage::TS_Send ) {
108 return i18n( "Work package sent at: %1", t );
109 }
110 if ( sts == WorkPackage::TS_Receive ) {
111 return i18n( "Work package transmission received at: %1", t );
112 }
113 return i18n( "Not available" );
114 }
115
116 case Qt::StatusTipRole:
117 case Qt::WhatsThisRole:
118 return QVariant();
119 }
120 return QVariant();
121}
122
123QVariant WorkPackageModel::completion( const WorkPackage *wp, int role ) const
124{
125 switch ( role ) {
126 case Qt::DisplayRole:
127 if ( wp->transmitionStatus() == WorkPackage::TS_Receive ) {
128 return wp->completion().percentFinished();
129 }
130 break;
131 case Qt::EditRole:
132 if ( wp->transmitionStatus() == WorkPackage::TS_Receive ) {
133 return wp->completion().percentFinished();
134 }
135 break;
136 case Qt::ToolTipRole:
137 if ( wp->transmitionStatus() == WorkPackage::TS_Receive ) {
138 return i18n( "Task reported %1% completed", wp->completion().percentFinished() );
139 }
140 break;
141 case Qt::StatusTipRole:
142 case Qt::WhatsThisRole:
143 return QVariant();
144 }
145 return QVariant();
146}
147
148QVariant WorkPackageModel::data( const WorkPackage *wp, int column, int role ) const
149{
150 switch ( column ) {
151 case NodeModel::WPOwnerName:
152 case NodeModel::NodeName: return ownerName( wp, role );
153 case NodeModel::WPTransmitionStatus:
154 case NodeModel::NodeStatus: return transmitionStatus( wp, role );
155 case NodeModel::NodeCompleted: return completion( wp, role );
156 case NodeModel::WPTransmitionTime:
157 case NodeModel::NodeActualStart: return transmitionTime( wp, role );
158
159 default: break;
160 }
161 return QVariant();
162}
163
164//-----------------------------
165bool WPSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
166{
167 if ( sourceModel()->index( source_row, NodeModel::NodeType, source_parent ).data( Qt::EditRole ).toInt() != Node::Type_Task ) {
168 return false;
169 }
170 if ( sourceModel()->index( source_row, NodeModel::NodeStatus, source_parent ).data( Qt::EditRole ).toInt() & Node::State_NotScheduled ) {
171 return false;
172 }
173 return true;
174}
175
176WorkPackageProxyModel::WorkPackageProxyModel( QObject *parent )
177 : QAbstractProxyModel( parent )
178{
179 m_proxies << new WPSortFilterProxyModel( this );
180 m_proxies << new FlatProxyModel( this );
181 m_nodemodel = new NodeItemModel( this );
182 QAbstractProxyModel *p = this;
183 foreach ( QAbstractProxyModel *m, m_proxies ) {
184 p->setSourceModel( m );
185 p = m;
186 }
187 p->setSourceModel( m_nodemodel );
188
189}
190
191Qt::ItemFlags WorkPackageProxyModel::flags(const QModelIndex &index) const
192{
193 if ( isWorkPackageIndex( index ) ) {
194 return Qt::ItemIsEnabled;
195 }
196 return QAbstractProxyModel::flags( index );
197}
198
199void WorkPackageProxyModel::setSourceModel( QAbstractItemModel *model )
200{
201 if ( sourceModel() ) {
202 disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
203 this, SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
204/* disconnect(sourceModel(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
205 this, SLOT(sourceHeaderDataChanged(Qt::Orientation,int,int)));*/
206 disconnect(sourceModel(), SIGNAL(layoutChanged()),
207 this, SIGNAL(layoutChanged()));
208 disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
209 this, SIGNAL(layoutAboutToBeChanged()));
210 disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
211 this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
212 disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
213 this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
214 disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
215 this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
216 disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
217 this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
218/*
219 disconnect(sourceModel(), SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
220 this, SLOT(sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
221 disconnect(sourceModel(), SIGNAL(columnsInserted(QModelIndex,int,int)),
222 this, SLOT(sourceColumnsInserted(QModelIndex,int,int)));
223
224 disconnect(sourceModel(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
225 this, SLOT(sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
226 disconnect(sourceModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)),
227 this, SLOT(sourceColumnsRemoved(QModelIndex,int,int)));
228 */
229 disconnect(sourceModel(), SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()));
230 disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceModelReset()));
231
232 disconnect(sourceModel(), SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
233 this, SLOT(sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
234 disconnect(sourceModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
235 this, SLOT(sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
236/*
237 disconnect(sourceModel(), SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
238 this, SLOT(sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
239 disconnect(sourceModel(), SIGNAL(columnsMoved(QModelIndex&parent,int,int,QModelIndex,int)),
240 this, SLOT(sourceColumnsMoved(QModelIndex&parent,int,int,QModelIndex,int)));*/
241 }
242 QAbstractProxyModel::setSourceModel( model );
243 if ( model ) {
244 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
245 this, SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
246/* connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
247 this, SLOT(sourceHeaderDataChanged(Qt::Orientation,int,int)));*/
248 connect(model, SIGNAL(layoutChanged()),
249 this, SIGNAL(layoutChanged()));
250 connect(model, SIGNAL(layoutAboutToBeChanged()),
251 this, SIGNAL(layoutAboutToBeChanged()));
252 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
253 this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
254 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
255 this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
256 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
257 this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
258 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
259 this, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
260/*
261 connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
262 this, SLOT(sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
263 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
264 this, SLOT(sourceColumnsInserted(QModelIndex,int,int)));
265
266 connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
267 this, SLOT(sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
268 connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
269 this, SLOT(sourceColumnsRemoved(QModelIndex,int,int)));
270 */
271 connect(model, SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()));
272 connect(model, SIGNAL(modelReset()), this, SLOT(sourceModelReset()));
273
274 connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
275 this, SLOT(sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
276 connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
277 this, SLOT(sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
278/*
279 connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
280 this, SLOT(sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
281 connect(model, SIGNAL(columnsMoved(QModelIndex&parent,int,int,QModelIndex,int)),
282 this, SLOT(sourceColumnsMoved(QModelIndex&parent,int,int,QModelIndex,int)));*/
283 }
284}
285
286void WorkPackageProxyModel::sourceDataChanged(const QModelIndex &start, const QModelIndex &end)
287{
288 emit dataChanged( mapFromSource( start ), mapFromSource( end ) );
289}
290
291void WorkPackageProxyModel::sourceModelAboutToBeReset()
292{
293// kDebug(planDbg());
294 beginResetModel();
295 detachTasks();
296}
297
298void WorkPackageProxyModel::sourceModelReset()
299{
300// kDebug(planDbg());
301 attachTasks();
302 for ( int r = 0; r < rowCount(); ++r ) {
303 kDebug(planDbg())<<index( r, 0 ).data();
304 }
305 endResetModel();
306}
307
308void WorkPackageProxyModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end )
309{
310 kDebug(planDbg())<<parent<<start<<end;
311 Q_ASSERT( ! parent.isValid() );
312 beginInsertRows( QModelIndex(), start, end );
313}
314
315void WorkPackageProxyModel::sourceRowsInserted(const QModelIndex &parent, int start, int end)
316{
317 kDebug(planDbg())<<parent<<start<<end<<":"<<rowCount();
318 Q_ASSERT( ! parent.isValid() );
319 for ( int r = start; r <= end; ++r ) {
320 QModelIndex i = index( r, 0 );
321 Task *task = taskFromIndex( i );
322 if ( task ) {
323 attachTasks( task );
324 }
325 }
326 endInsertRows();
327}
328
329void WorkPackageProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end )
330{
331 kDebug(planDbg())<<parent<<start<<end;
332 Q_ASSERT( ! parent.isValid() );
333 beginInsertRows( QModelIndex(), start, end );
334}
335
336void WorkPackageProxyModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
337{
338 kDebug(planDbg())<<parent<<start<<end;
339 Q_ASSERT( ! parent.isValid() );
340 for ( int r = start; r <= end; ++r ) {
341 QModelIndex i = index( r, 0 );
342 Task *task = taskFromIndex( i );
343 if ( task ) {
344 detachTasks( task );
345 }
346 }
347 endInsertRows();
348}
349
350void WorkPackageProxyModel::sourceRowsAboutToBeMoved(const QModelIndex&, int sourceStart, int sourceEnd, const QModelIndex&, int destStart )
351{
352 beginMoveRows( QModelIndex(), sourceStart, sourceEnd, QModelIndex(), destStart );
353}
354
355void WorkPackageProxyModel::sourceRowsMoved(const QModelIndex &, int , int , const QModelIndex &, int )
356{
357 endMoveRows();
358}
359
360bool WorkPackageProxyModel::hasChildren(const QModelIndex &parent) const
361{
362 return rowCount(parent) > 0;
363}
364
365int WorkPackageProxyModel::rowCount( const QModelIndex &parent ) const
366{
367 int rows = 0;
368 if ( ! parent.isValid() ) {
369 rows = sourceModel()->rowCount();
370 } else if ( isTaskIndex( parent ) ) {
371 Task *task = taskFromIndex( parent );
372 rows = task ? task->workPackageLogCount() : 0;
373 }
374// kDebug(planDbg())<<rows;
375 for ( int r = 0; r < rows; ++r ) {
376// kDebug(planDbg())<<r<<index( r, 0 ).data();
377 }
378 return rows;
379}
380
381int WorkPackageProxyModel::columnCount(const QModelIndex &/*parent*/) const
382{
383 return sourceModel()->columnCount();
384}
385
386QModelIndex WorkPackageProxyModel::mapToSource( const QModelIndex &proxyIndex ) const
387{
388 if ( ! proxyIndex.isValid() ) {
389 return QModelIndex();
390 }
391 if ( isWorkPackageIndex( proxyIndex ) ) {
392 // workpackage, not mapped to source model
393 return QModelIndex();
394 }
395 return sourceModel()->index( proxyIndex.row(), proxyIndex.column() );
396}
397
398QModelIndex WorkPackageProxyModel::mapFromSource( const QModelIndex &sourceIndex ) const
399{
400 // index from source model is always a node
401 return createIndex( sourceIndex.row(), sourceIndex.column() );
402}
403
404QModelIndex WorkPackageProxyModel::parent( const QModelIndex &child ) const
405{
406 QModelIndex idx;
407 if ( isWorkPackageIndex( child ) ) {
408 // only work packages have parent
409 idx = m_nodemodel->index( static_cast<Node*>( child.internalPointer() ) );
410 idx = mapFromBaseModel( idx );
411 }
412// kDebug(planDbg())<<child<<idx;
413 return idx;
414}
415
416QModelIndex WorkPackageProxyModel::index(int row, int column, const QModelIndex &parent) const
417{
418 QModelIndex idx;
419 if ( ! parent.isValid() ) {
420 // this should be a node
421 idx = createIndex( row, column );
422 } else if ( isTaskIndex( parent ) ) {
423 // Should be a work package, parent should be a task
424 Task *task = taskFromIndex( parent );
425 if ( task ) {
426 idx = createIndex( row, column, task );
427 }
428 }
429/* if ( ! idx.isValid() ) {
430 kDebug(planDbg())<<"not valid:"<<parent<<row<<column<<idx;
431 } else {
432 kDebug(planDbg())<<parent<<row<<column<<idx;
433 }*/
434 return idx;
435}
436
437QVariant WorkPackageProxyModel::data( const QModelIndex &idx, int role ) const
438{
439 QVariant value;
440 if ( isTaskIndex( idx ) ) {
441 value = mapToSource( idx ).data( role );
442 } else if ( isWorkPackageIndex( idx ) ) {
443 Task *task = taskFromIndex( idx );
444 if ( task ) {
445 value = m_model.data( task->workPackageAt( idx.row() ), idx.column(), role );
446 }
447 }
448// kDebug(planDbg())<<idx<<value;
449 return value;
450}
451
452Task *WorkPackageProxyModel::taskFromIndex( const QModelIndex &idx ) const
453{
454 Task *task = 0;
455 if ( idx.internalPointer() ) {
456 task = static_cast<Task*>( idx.internalPointer() );
457 } else if ( idx.isValid() ) {
458 QVariant obj = data( idx, Role::Object );
459 task = qobject_cast<Task*>( obj.value<QObject*>() );
460 }
461// kDebug(planDbg())<<idx<<task;
462 return task;
463}
464
465QModelIndex WorkPackageProxyModel::indexFromTask(const Node *node) const
466{
467 return mapFromBaseModel( m_nodemodel->index( node ) );
468}
469
470QModelIndex WorkPackageProxyModel::mapFromBaseModel( const QModelIndex &idx ) const
471{
472 if ( ! idx.isValid() ) {
473 return QModelIndex();
474 }
475 QModelIndex in = idx;
476 for ( int i = m_proxies.count() -1; i >= 0; --i ) {
477 in = m_proxies.at( i )->mapFromSource( in );
478 }
479 return mapFromSource( in );
480}
481void WorkPackageProxyModel::setProject( Project *project )
482{
483 kDebug(planDbg())<<project;
484 m_nodemodel->setProject( project );
485}
486
487void WorkPackageProxyModel::setScheduleManager(ScheduleManager *sm)
488{
489 kDebug(planDbg())<<sm;
490 m_nodemodel->setScheduleManager( sm );
491}
492
493NodeItemModel *WorkPackageProxyModel::baseModel() const
494{
495 return m_nodemodel;
496}
497
498void WorkPackageProxyModel::detachTasks( Task *task )
499{
500 if ( task ) {
501 disconnect(task, SIGNAL(workPackageToBeAdded(Node*,int)), this, SLOT(workPackageToBeAdded(Node*,int)));
502 disconnect(task, SIGNAL(workPackageAdded(Node*)), this, SLOT(workPackageAdded(Node*)));
503 disconnect(task, SIGNAL(workPackageToBeRemoved(Node*,int)), this, SLOT(workPackageToBeRemoved(Node*,int)));
504 disconnect(task, SIGNAL(workPackageRemoved(Node*)), this, SLOT(workPackageRemoved(Node*)));
505// kDebug(planDbg())<<task;
506 } else {
507 for ( int r = 0; r < rowCount(); ++r ) {
508 Task *t = taskFromIndex( index( r, 0 ) );
509 if ( t ) {
510 detachTasks( t );
511 }
512 }
513 }
514}
515
516void WorkPackageProxyModel::attachTasks( Task *task )
517{
518 if ( task ) {
519 connect(task, SIGNAL(workPackageToBeAdded(Node*,int)), this, SLOT(workPackageToBeAdded(Node*,int)));
520 connect(task, SIGNAL(workPackageAdded(Node*)), this, SLOT(workPackageAdded(Node*)));
521 connect(task, SIGNAL(workPackageToBeRemoved(Node*,int)), this, SLOT(workPackageToBeRemoved(Node*,int)));
522 connect(task, SIGNAL(workPackageRemoved(Node*)), this, SLOT(workPackageRemoved(Node*)));
523// kDebug(planDbg())<<task;
524 } else {
525 for ( int r = 0; r < rowCount(); ++r ) {
526 Task *t = taskFromIndex( index( r, 0 ) );
527 if ( t ) {
528 attachTasks( t );
529 }
530 }
531 }
532}
533
534void WorkPackageProxyModel::workPackageToBeAdded(Node *node, int row )
535{
536 QModelIndex idx = indexFromTask( node );
537 kDebug(planDbg())<<node<<row<<idx;
538 beginInsertRows( idx, row, row );
539}
540
541void WorkPackageProxyModel::workPackageAdded(Node *)
542{
543 endInsertRows();
544}
545
546void WorkPackageProxyModel::workPackageToBeRemoved(Node *node, int row)
547{
548 QModelIndex idx = indexFromTask( node );
549 beginRemoveRows( idx, row, row );
550}
551
552void WorkPackageProxyModel::workPackageRemoved(Node *)
553{
554 endRemoveRows();
555}
556
557} //namespace KPlato
558
559#include "kptworkpackagemodel.moc"
560