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 | |
41 | namespace KPlato |
42 | { |
43 | |
44 | |
45 | TaskStatusItemModel::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 | |
72 | TaskStatusItemModel::~TaskStatusItemModel() |
73 | { |
74 | } |
75 | |
76 | void TaskStatusItemModel::slotAboutToBeReset() |
77 | { |
78 | kDebug(planDbg()); |
79 | clear(); |
80 | } |
81 | |
82 | void TaskStatusItemModel::slotReset() |
83 | { |
84 | kDebug(planDbg()); |
85 | refresh(); |
86 | } |
87 | |
88 | void TaskStatusItemModel::slotNodeToBeInserted( Node *, int ) |
89 | { |
90 | //kDebug(planDbg())<<node->name(); |
91 | clear(); |
92 | } |
93 | |
94 | void TaskStatusItemModel::slotNodeInserted( Node * /*node*/ ) |
95 | { |
96 | //kDebug(planDbg())<<node->getParent->name()<<"-->"<<node->name(); |
97 | refresh(); |
98 | } |
99 | |
100 | void TaskStatusItemModel::slotNodeToBeRemoved( Node * /*node*/ ) |
101 | { |
102 | //kDebug(planDbg())<<node->name(); |
103 | clear(); |
104 | } |
105 | |
106 | void TaskStatusItemModel::slotNodeRemoved( Node * /*node*/ ) |
107 | { |
108 | //kDebug(planDbg())<<node->name(); |
109 | refresh(); |
110 | } |
111 | |
112 | void 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 | |
121 | void TaskStatusItemModel::slotNodeMoved( Node * /*node*/ ) |
122 | { |
123 | //kDebug(planDbg())<<node->name(); |
124 | refresh(); |
125 | } |
126 | |
127 | void 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 | |
160 | void 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 | |
173 | void 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 | |
189 | void 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 | |
208 | void 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 | |
243 | Qt::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 | |
287 | QModelIndex 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 | |
314 | QModelIndex 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 | |
339 | QModelIndex 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 | |
353 | QModelIndex 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 | |
366 | QVariant 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 | |
381 | bool 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 | |
434 | bool 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 | |
447 | bool 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 | |
460 | bool 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 | |
488 | bool 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 | |
516 | QVariant 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 | |
556 | bool 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 | |
578 | QVariant TaskStatusItemModel::( 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 | |
593 | QVariant TaskStatusItemModel::alignment( int column ) const |
594 | { |
595 | return m_nodemodel.headerData( column, Qt::TextAlignmentRole ); |
596 | } |
597 | |
598 | QAbstractItemDelegate *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 | |
609 | int TaskStatusItemModel::columnCount( const QModelIndex & ) const |
610 | { |
611 | return m_nodemodel.propertyCount(); |
612 | } |
613 | |
614 | int 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 | |
629 | Qt::DropActions TaskStatusItemModel::supportedDropActions() const |
630 | { |
631 | return Qt::CopyAction | Qt::MoveAction; |
632 | } |
633 | |
634 | |
635 | QStringList TaskStatusItemModel::mimeTypes() const |
636 | { |
637 | return QStringList(); |
638 | } |
639 | |
640 | QMimeData *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 | |
660 | bool TaskStatusItemModel::dropAllowed( Node *, const QMimeData * ) |
661 | { |
662 | return false; |
663 | } |
664 | |
665 | bool TaskStatusItemModel::dropMimeData( const QMimeData *, Qt::DropAction , int , int , const QModelIndex & ) |
666 | { |
667 | return false; |
668 | } |
669 | |
670 | NodeMap *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 | |
681 | Node *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 | |
694 | TaskStatusItemModel::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 | |
716 | void 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 | |
750 | void 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 | |
760 | void TaskStatusItemModel::slotLayoutChanged() |
761 | { |
762 | //kDebug(planDbg())<<node->name(); |
763 | emit layoutAboutToBeChanged(); |
764 | emit layoutChanged(); |
765 | } |
766 | |
767 | int 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 | |