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 "kpttaskstatusmodel.h"
21
22#include "kptglobal.h"
23#include "kptcommonstrings.h"
24#include "kptitemmodelbase.h"
25#include "kpttaskcompletedelegate.h"
26#include "kptcommand.h"
27#include "kptnode.h"
28#include "kptproject.h"
29#include "kpttask.h"
30#include "kptnodeitemmodel.h"
31#include "kptdebug.h"
32
33#include <QAbstractItemModel>
34#include <QMimeData>
35#include <QModelIndex>
36
37#include <kglobal.h>
38#include <klocale.h>
39
40
41namespace KPlato
42{
43
44
45TaskStatusItemModel::TaskStatusItemModel( QObject *parent )
46 : ItemModelBase( parent ),
47 m_period( 7 ),
48 m_periodType( UseCurrentDate ),
49 m_weekday( Qt::Friday )
50
51{
52 m_topNames << i18n( "Not Started" );
53 m_topTips << i18n( "Tasks that should have been started" );
54 m_top.append(&m_notstarted );
55
56 m_topNames << i18n( "Running" );
57 m_topTips << i18n( "Tasks that are running" );
58 m_top.append(&m_running );
59
60 m_topNames << i18n( "Finished" );
61 m_topTips << i18n( "Tasks that have finished during this period" );
62 m_top.append(&m_finished );
63
64 m_topNames << i18n( "Next Period" );
65 m_topTips << i18n( "Tasks that are scheduled to start next period" );
66 m_top.append(&m_upcoming );
67
68/* connect( this, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()) );
69 connect( this, SIGNAL(modelReset()), SLOT(slotReset()) );*/
70}
71
72TaskStatusItemModel::~TaskStatusItemModel()
73{
74}
75
76void TaskStatusItemModel::slotAboutToBeReset()
77{
78 kDebug(planDbg());
79 clear();
80}
81
82void TaskStatusItemModel::slotReset()
83{
84 kDebug(planDbg());
85 refresh();
86}
87
88void TaskStatusItemModel::slotNodeToBeInserted( Node *, int )
89{
90 //kDebug(planDbg())<<node->name();
91 clear();
92}
93
94void TaskStatusItemModel::slotNodeInserted( Node * /*node*/ )
95{
96 //kDebug(planDbg())<<node->getParent->name()<<"-->"<<node->name();
97 refresh();
98}
99
100void TaskStatusItemModel::slotNodeToBeRemoved( Node * /*node*/ )
101{
102 //kDebug(planDbg())<<node->name();
103 clear();
104}
105
106void TaskStatusItemModel::slotNodeRemoved( Node * /*node*/ )
107{
108 //kDebug(planDbg())<<node->name();
109 refresh();
110}
111
112void TaskStatusItemModel::slotNodeToBeMoved(Node *node, int pos, Node *newParent, int newPos)
113{
114 Q_UNUSED( node );
115 Q_UNUSED( pos );
116 Q_UNUSED( newParent );
117 Q_UNUSED( newPos );
118 clear();
119}
120
121void TaskStatusItemModel::slotNodeMoved( Node * /*node*/ )
122{
123 //kDebug(planDbg())<<node->name();
124 refresh();
125}
126
127void TaskStatusItemModel::setProject( Project *project )
128{
129 clear();
130 if ( m_project ) {
131 disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged()) );
132 disconnect( m_project, SIGNAL(wbsDefinitionChanged()), this, SLOT(slotWbsDefinitionChanged()) );
133 disconnect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
134 disconnect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) );
135 disconnect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
136 disconnect(m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotNodeToBeMoved(Node*,int,Node*,int)));
137
138 disconnect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) );
139 disconnect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) );
140 disconnect(m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotNodeMoved(Node*)));
141 }
142 m_project = project;
143 m_nodemodel.setProject( project );
144 if ( project ) {
145 connect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged()) );
146 connect( m_project, SIGNAL(wbsDefinitionChanged()), this, SLOT(slotWbsDefinitionChanged()) );
147 connect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
148 connect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) );
149 connect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
150 connect(m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotNodeToBeMoved(Node*,int,Node*,int)));
151
152 connect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) );
153 connect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) );
154 connect(m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotNodeMoved(Node*)));
155
156 }
157 reset();
158}
159
160void TaskStatusItemModel::setScheduleManager( ScheduleManager *sm )
161{
162 clear();
163 if ( m_nodemodel.manager() ) {
164 }
165 m_nodemodel.setManager( sm );
166 ItemModelBase::setScheduleManager( sm );
167 if ( sm ) {
168 }
169 reset();
170 refresh();
171}
172
173void TaskStatusItemModel::clear()
174{
175 foreach ( NodeMap *l, m_top ) {
176 int c = l->count();
177 if ( c > 0 ) {
178 //FIXME: gives error msg:
179 // Can't select indexes from different model or with different parents
180 QModelIndex i = index( l );
181 kDebug(planDbg())<<i<<0<<c-1;
182 beginRemoveRows( i, 0, c-1 );
183 l->clear();
184 endRemoveRows();
185 }
186 }
187}
188
189void TaskStatusItemModel::setNow()
190{
191 switch ( m_periodType ) {
192 case UseWeekday: {
193 QDate date = QDate::currentDate();
194 int wd = date.dayOfWeek();
195 date = date.addDays( m_weekday - wd );
196 if ( wd < m_weekday ) {
197 date = date.addDays( -7 );
198 }
199 m_nodemodel.setNow( date );
200 break; }
201 case UseCurrentDate: m_nodemodel.setNow( QDate::currentDate() ); break;
202 default:
203 m_nodemodel.setNow( QDate::currentDate() );
204 break;
205 }
206}
207
208void TaskStatusItemModel::refresh()
209{
210 clear();
211 if ( m_project == 0 ) {
212 return;
213 }
214 m_id = m_nodemodel.id();
215 if ( m_id == -1 ) {
216 return;
217 }
218 setNow();
219 const QDate begin = m_nodemodel.now().addDays( -m_period );
220 const QDate end = m_nodemodel.now().addDays( m_period );
221
222 foreach( Node* n, m_project->allNodes() ) {
223 if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) {
224 continue;
225 }
226 Task *task = static_cast<Task*>( n );
227 const TaskStatus status = taskStatus(task, begin, end);
228 if (status != TaskUnknownStatus) {
229 m_top.at(status)->insert(task->wbsCode(), task);
230 }
231 }
232 foreach ( NodeMap *l, m_top ) {
233 int c = l->count();
234 if ( c > 0 ) {
235 kDebug(planDbg())<<index(l)<<0<<c-1;
236 beginInsertRows( index( l ), 0, c-1 );
237 endInsertRows();
238 }
239 }
240 emit layoutChanged(); //HACK to get views updated
241}
242
243Qt::ItemFlags TaskStatusItemModel::flags( const QModelIndex &index ) const
244{
245 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
246 flags &= ~( Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
247 Node *n = node( index );
248 if ( ! m_readWrite || n == 0 || m_id == -1 || ! n->isScheduled( m_id ) ) {
249 return flags;
250 }
251 if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) {
252 return flags;
253 }
254 Task *t = static_cast<Task*>( n );
255 if ( ! t->completion().isStarted() ) {
256 switch ( index.column() ) {
257 case NodeModel::NodeActualStart:
258 flags |= Qt::ItemIsEditable;
259 break;
260 case NodeModel::NodeCompleted:
261 if ( t->state() & Node::State_ReadyToStart ) {
262 flags |= Qt::ItemIsEditable;
263 }
264 break;
265 default: break;
266 }
267 } else if ( ! t->completion().isFinished() ) {
268 // task is running
269 switch ( index.column() ) {
270 case NodeModel::NodeActualFinish:
271 case NodeModel::NodeCompleted:
272 case NodeModel::NodeRemainingEffort:
273 flags |= Qt::ItemIsEditable;
274 break;
275 case NodeModel::NodeActualEffort:
276 if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) {
277 flags |= Qt::ItemIsEditable;
278 }
279 break;
280 default: break;
281 }
282 }
283 return flags;
284}
285
286
287QModelIndex TaskStatusItemModel::parent( const QModelIndex &index ) const
288{
289 if ( !index.isValid() ) {
290 return QModelIndex();
291 }
292 //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column();
293 int row = m_top.indexOf( static_cast<NodeMap*>( index.internalPointer() ) );
294 if ( row != -1 ) {
295 return QModelIndex(); // top level has no parent
296 }
297 Node *n = node( index );
298 if ( n == 0 ) {
299 return QModelIndex();
300 }
301 NodeMap *lst = 0;
302 foreach ( NodeMap *l, m_top ) {
303 if ( l->values().indexOf( n ) != -1 ) {
304 lst = l;
305 break;
306 }
307 }
308 if ( lst == 0 ) {
309 return QModelIndex();
310 }
311 return createIndex( m_top.indexOf( lst ), 0, lst );
312}
313
314QModelIndex TaskStatusItemModel::index( int row, int column, const QModelIndex &parent ) const
315{
316 //kDebug(planDbg())<<row<<column<<parent;
317 if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
318 return QModelIndex();
319 }
320 if ( ! parent.isValid() ) {
321 if ( row >= m_top.count() ) {
322 return QModelIndex();
323 }
324 return createIndex(row, column, m_top.value( row ) );
325 }
326 NodeMap *l = list( parent );
327 if ( l == 0 ) {
328 return QModelIndex();
329 }
330 if ( row >= rowCount( parent ) ) {
331 kWarning()<<"Row >= rowCount, Qt4.4 asks, so we need to handle it"<<parent<<row<<column;
332 return QModelIndex();
333 }
334 QModelIndex i = createIndex(row, column, l->values().value( row ) );
335 Q_ASSERT( i.internalPointer() != 0 );
336 return i;
337}
338
339QModelIndex TaskStatusItemModel::index( const Node *node ) const
340{
341 if ( m_project == 0 || node == 0 ) {
342 return QModelIndex();
343 }
344 foreach( NodeMap *l, m_top ) {
345 int row = l->values().indexOf( const_cast<Node*>( node ) );
346 if ( row != -1 ) {
347 return createIndex( row, 0, const_cast<Node*>( node ) );
348 }
349 }
350 return QModelIndex();
351}
352
353QModelIndex TaskStatusItemModel::index( const NodeMap *lst ) const
354{
355 if ( m_project == 0 || lst == 0 ) {
356 return QModelIndex();
357 }
358 NodeMap *l = const_cast<NodeMap*>( lst );
359 int row = m_top.indexOf( l );
360 if ( row == -1 ) {
361 return QModelIndex();
362 }
363 return createIndex( row, 0, l );
364}
365
366QVariant TaskStatusItemModel::name( int row, int role ) const
367{
368 switch ( role ) {
369 case Qt::DisplayRole:
370 case Qt::EditRole:
371 return m_topNames.value( row );
372 case Qt::ToolTipRole:
373 return m_topTips.value( row );
374 case Qt::StatusTipRole:
375 case Qt::WhatsThisRole:
376 return QVariant();
377 }
378 return QVariant();
379}
380
381bool TaskStatusItemModel::setCompletion( Node *node, const QVariant &value, int role )
382{
383 if ( role != Qt::EditRole ) {
384 return false;
385 }
386 if ( node->type() == Node::Type_Task ) {
387 Completion &c = static_cast<Task*>( node )->completion();
388 QDateTime dt = QDateTime::currentDateTime();
389 QDate date = dt.date();
390 // xgettext: no-c-format
391 MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) );
392 if ( ! c.isStarted() ) {
393 m->addCommand( new ModifyCompletionStartedCmd( c, true ) );
394 m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) );
395 }
396 m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) );
397 if ( value.toInt() == 100 ) {
398 m->addCommand( new ModifyCompletionFinishedCmd( c, true ) );
399 m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) );
400 }
401 emit executeCommand( m ); // also adds a new entry if necessary
402 if ( c.entrymode() == Completion::EnterCompleted ) {
403 Duration planned = static_cast<Task*>( node )->plannedEffort( m_nodemodel.id() );
404 Duration actual = ( planned * value.toInt() ) / 100;
405 kDebug(planDbg())<<planned.toString()<<value.toInt()<<actual.toString();
406 NamedCommand *cmd = new ModifyCompletionActualEffortCmd( c, date, actual );
407 cmd->execute();
408 m->addCommand( cmd );
409 cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual );
410 cmd->execute();
411 m->addCommand( cmd );
412 }
413 return true;
414 }
415 if ( node->type() == Node::Type_Milestone ) {
416 Completion &c = static_cast<Task*>( node )->completion();
417 if ( value.toInt() > 0 ) {
418 QDateTime dt = QDateTime::currentDateTime();
419 QDate date = dt.date();
420 MacroCommand *m = new MacroCommand( kundo2_i18n( "Set finished" ) );
421 m->addCommand( new ModifyCompletionStartedCmd( c, true ) );
422 m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) );
423 m->addCommand( new ModifyCompletionFinishedCmd( c, true ) );
424 m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) );
425 m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) );
426 emit executeCommand( m ); // also adds a new entry if necessary
427 return true;
428 }
429 return false;
430 }
431 return false;
432}
433
434bool TaskStatusItemModel::setRemainingEffort( Node *node, const QVariant &value, int role )
435{
436 if ( role == Qt::EditRole && node->type() == Node::Type_Task ) {
437 Task *t = static_cast<Task*>( node );
438 double d( value.toList()[0].toDouble() );
439 Duration::Unit unit = static_cast<Duration::Unit>( value.toList()[1].toInt() );
440 Duration dur( d, unit );
441 emit executeCommand( new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ) );
442 return true;
443 }
444 return false;
445}
446
447bool TaskStatusItemModel::setActualEffort( Node *node, const QVariant &value, int role )
448{
449 if ( role == Qt::EditRole && node->type() == Node::Type_Task ) {
450 Task *t = static_cast<Task*>( node );
451 double d( value.toList()[0].toDouble() );
452 Duration::Unit unit = static_cast<Duration::Unit>( value.toList()[1].toInt() );
453 Duration dur( d, unit );
454 emit executeCommand( new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ) );
455 return true;
456 }
457 return false;
458}
459
460bool TaskStatusItemModel::setStartedTime( Node *node, const QVariant &value, int role )
461{
462 switch ( role ) {
463 case Qt::EditRole: {
464 Task *t = qobject_cast<Task*>( node );
465 if ( t == 0 ) {
466 return false;
467 }
468 MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual start time" ) );
469 if ( ! t->completion().isStarted() ) {
470 m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) );
471 }
472 m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) );
473 if ( t->type() == Node::Type_Milestone ) {
474 m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) );
475 m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) );
476 if ( t->completion().percentFinished() < 100 ) {
477 Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration );
478 m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) );
479 }
480 }
481 emit executeCommand( m );
482 return true;
483 }
484 }
485 return false;
486}
487
488bool TaskStatusItemModel::setFinishedTime( Node *node, const QVariant &value, int role )
489{
490 switch ( role ) {
491 case Qt::EditRole: {
492 Task *t = qobject_cast<Task*>( node );
493 if ( t == 0 ) {
494 return false;
495 }
496 MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual finish time" ) );
497 if ( ! t->completion().isFinished() ) {
498 m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) );
499 if ( t->completion().percentFinished() < 100 ) {
500 Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration );
501 m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) );
502 }
503 }
504 m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) );
505 if ( t->type() == Node::Type_Milestone ) {
506 m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) );
507 m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) );
508 }
509 emit executeCommand( m );
510 return true;
511 }
512 }
513 return false;
514}
515
516QVariant TaskStatusItemModel::data( const QModelIndex &index, int role ) const
517{
518 QVariant result;
519 if ( ! index.isValid() ) {
520 return result;
521 }
522 if ( role == Qt::TextAlignmentRole ) {
523 return alignment( index.column() );
524 }
525 Node *n = node( index );
526 if ( n == 0 ) {
527 switch ( index.column() ) {
528 case NodeModel::NodeName: return name( index.row(), role );
529 default: break;
530 }
531 return QVariant();
532 }
533 result = m_nodemodel.data( n, index.column(), role );
534 if ( role == Qt::DisplayRole ) {
535 switch ( index.column() ) {
536 case NodeModel::NodeActualStart:
537 if ( ! result.isValid() ) {
538 return m_nodemodel.data( n, NodeModel::NodeStatus, role );
539 }
540 break;
541 }
542 } else if ( role == Qt::EditRole ) {
543 switch ( index.column() ) {
544 case NodeModel::NodeActualStart:
545 case NodeModel::NodeActualFinish:
546 if ( ! result.isValid() ) {
547 return QDateTime::currentDateTime();
548 }
549 break;
550 }
551 }
552 return result;
553}
554
555
556bool TaskStatusItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
557{
558 if ( ! index.isValid() ) {
559 return ItemModelBase::setData( index, value, role );
560 }
561 switch ( index.column() ) {
562 case NodeModel::NodeCompleted:
563 return setCompletion( node( index ), value, role );
564 case NodeModel::NodeRemainingEffort:
565 return setRemainingEffort( node( index ), value, role );
566 case NodeModel::NodeActualEffort:
567 return setActualEffort( node( index ), value, role );
568 case NodeModel::NodeActualStart:
569 return setStartedTime( node( index ), value, role );
570 case NodeModel::NodeActualFinish:
571 return setFinishedTime( node( index ), value, role );
572 default:
573 break;
574 }
575 return false;
576}
577
578QVariant TaskStatusItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
579{
580 if ( orientation == Qt::Horizontal ) {
581 if ( role == Qt::DisplayRole ) {
582 return m_nodemodel.headerData( section, role );
583 } else if ( role == Qt::TextAlignmentRole ) {
584 return alignment( section );
585 }
586 }
587 if ( role == Qt::ToolTipRole ) {
588 return m_nodemodel.headerData( section, role );
589 }
590 return ItemModelBase::headerData(section, orientation, role);
591}
592
593QVariant TaskStatusItemModel::alignment( int column ) const
594{
595 return m_nodemodel.headerData( column, Qt::TextAlignmentRole );
596}
597
598QAbstractItemDelegate *TaskStatusItemModel::createDelegate( int column, QWidget *parent ) const
599{
600 switch ( column ) {
601 case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent );
602 case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent );
603 case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent );
604 default: return 0;
605 }
606 return 0;
607}
608
609int TaskStatusItemModel::columnCount( const QModelIndex & ) const
610{
611 return m_nodemodel.propertyCount();
612}
613
614int TaskStatusItemModel::rowCount( const QModelIndex &parent ) const
615{
616 if ( ! parent.isValid() ) {
617 //kDebug(planDbg())<<"top="<<m_top.count()<<m_top;
618 return m_top.count();
619 }
620 NodeMap *l = list( parent );
621 if ( l ) {
622 //kDebug(planDbg())<<"list"<<parent.row()<<":"<<l->count()<<l<<m_topNames.value( parent.row() );
623 return l->count();
624 }
625 //kDebug(planDbg())<<"node"<<parent.row();
626 return 0; // nodes don't have children
627}
628
629Qt::DropActions TaskStatusItemModel::supportedDropActions() const
630{
631 return Qt::CopyAction | Qt::MoveAction;
632}
633
634
635QStringList TaskStatusItemModel::mimeTypes() const
636{
637 return QStringList();
638}
639
640QMimeData *TaskStatusItemModel::mimeData( const QModelIndexList & indexes ) const
641{
642 QMimeData *m = new QMimeData();
643 QByteArray encodedData;
644 QDataStream stream(&encodedData, QIODevice::WriteOnly);
645 QList<int> rows;
646 foreach (const QModelIndex &index, indexes) {
647 if ( index.isValid() && !rows.contains( index.row() ) ) {
648 //kDebug(planDbg())<<index.row();
649 Node *n = node( index );
650 if ( n ) {
651 rows << index.row();
652 stream << n->id();
653 }
654 }
655 }
656 m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData);
657 return m;
658}
659
660bool TaskStatusItemModel::dropAllowed( Node *, const QMimeData * )
661{
662 return false;
663}
664
665bool TaskStatusItemModel::dropMimeData( const QMimeData *, Qt::DropAction , int , int , const QModelIndex & )
666{
667 return false;
668}
669
670NodeMap *TaskStatusItemModel::list( const QModelIndex &index ) const
671{
672 if ( index.isValid() ) {
673 Q_ASSERT( index.internalPointer() );
674 if ( m_top.contains( static_cast<NodeMap*>( index.internalPointer() ) ) ) {
675 return static_cast<NodeMap*>( index.internalPointer() );
676 }
677 }
678 return 0;
679}
680
681Node *TaskStatusItemModel::node( const QModelIndex &index ) const
682{
683 if ( index.isValid() ) {
684 foreach ( NodeMap *l, m_top ) {
685 int row = l->values().indexOf( static_cast<Node*>( index.internalPointer() ) );
686 if ( row != -1 ) {
687 return static_cast<Node*>( index.internalPointer() );
688 }
689 }
690 }
691 return 0;
692}
693
694TaskStatusItemModel::TaskStatus TaskStatusItemModel::taskStatus(const Task *task,
695 const QDate &begin, const QDate &end)
696{
697 TaskStatus result = TaskUnknownStatus;
698
699 const Completion &completion = task->completion();
700 if (completion.isFinished()) {
701 if (completion.finishTime().date() > begin) {
702 result = TaskFinished;
703 }
704 } else if (completion.isStarted()) {
705 result = TaskRunning;
706 } else if (task->startTime(m_id).date() < m_nodemodel.now()) {
707 // should have been started
708 result = TaskNotStarted;
709 } else if (task->startTime(m_id).date() <= end) {
710 // start next period
711 result = TaskUpcoming;
712 }
713 return result;
714}
715
716void TaskStatusItemModel::slotNodeChanged( Node *node )
717{
718 kDebug(planDbg());
719 if (node == 0 || node->type() == Node::Type_Project ||
720 (node->type() != Node::Type_Task && node->type() != Node::Type_Milestone)) {
721 return;
722 }
723
724 Task *task = static_cast<Task*>(node);
725
726 const QDate begin = m_nodemodel.now().addDays( -m_period );
727 const QDate end = m_nodemodel.now().addDays( m_period );
728
729 const TaskStatus status = taskStatus(task, begin, end);
730
731 int row = -1;
732 if (status != TaskUnknownStatus) {
733 // find the row of the task
734 const QString wbs = node->wbsCode();
735 // TODO: not enough to just check the result of indexOf? wbs not unique?
736 if (m_top.at(status)->value(wbs) == node ) {
737 row = m_top.at(status)->keys().indexOf(wbs);
738 }
739 }
740
741 if (row >= 0) {
742 // task in old group, just changed values
743 emit dataChanged(createIndex(row, 0, node), createIndex(row, columnCount() - 1, node));
744 } else {
745 // task is new or changed groups
746 refresh();
747 }
748}
749
750void TaskStatusItemModel::slotWbsDefinitionChanged()
751{
752 kDebug(planDbg());
753 foreach ( NodeMap *l, m_top ) {
754 for ( int row = 0; row < l->count(); ++row ) {
755 emit dataChanged( createIndex( row, NodeModel::NodeWBSCode, l->values().value( row ) ), createIndex( row, NodeModel::NodeWBSCode, l->values().value( row ) ) );
756 }
757 }
758}
759
760void TaskStatusItemModel::slotLayoutChanged()
761{
762 //kDebug(planDbg())<<node->name();
763 emit layoutAboutToBeChanged();
764 emit layoutChanged();
765}
766
767int TaskStatusItemModel::sortRole( int column ) const
768{
769 switch ( column ) {
770 case NodeModel::NodeStartTime:
771 case NodeModel::NodeEndTime:
772 case NodeModel::NodeActualStart:
773 case NodeModel::NodeActualFinish:
774 case NodeModel::NodeEarlyStart:
775 case NodeModel::NodeEarlyFinish:
776 case NodeModel::NodeLateStart:
777 case NodeModel::NodeLateFinish:
778 case NodeModel::NodeConstraintStart:
779 case NodeModel::NodeConstraintEnd:
780 return Qt::EditRole;
781 default:
782 break;
783 }
784 return Qt::DisplayRole;
785}
786
787} // namespace KPlato
788
789#include "kpttaskstatusmodel.moc"
790