diff --git a/src/workpackage/taskworkpackagemodel.cpp b/src/workpackage/taskworkpackagemodel.cpp index 7fde4bc5..64ce6ad2 100644 --- a/src/workpackage/taskworkpackagemodel.cpp +++ b/src/workpackage/taskworkpackagemodel.cpp @@ -1,714 +1,722 @@ /* This file is part of the KDE project Copyright (C) 2009, 2011, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "taskworkpackagemodel.h" #include "part.h" #include "workpackage.h" #include "kptglobal.h" #include "kptresource.h" #include "kptproject.h" #include "kpttask.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kpttaskcompletedelegate.h" +#include + #include #include #include #include #include #include "debugarea.h" using namespace KPlato; namespace KPlatoWork { TaskWorkPackageModel::TaskWorkPackageModel( Part *part, QObject *parent ) : ItemModelBase( parent ), m_part( part ) { connect( part, &Part::workPackageAdded, this, &TaskWorkPackageModel::addWorkPackage ); connect( part, &Part::workPackageRemoved, this, &TaskWorkPackageModel::removeWorkPackage ); } Qt::ItemFlags TaskWorkPackageModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); flags &= ~( Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled ); Node *n = nodeForIndex( index ); if ( n == 0 ) { return flags; } if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) { return flags; } Task *t = static_cast( n ); if ( ! t->completion().isStarted() ) { switch ( index.column() ) { case NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeCompleted: flags |= Qt::ItemIsEditable; break; default: break; } } else if ( ! t->completion().isFinished() ) { // task is running switch ( index.column() ) { case NodeActualFinish: case NodeCompleted: case NodeRemainingEffort: case NodeActualEffort: flags |= Qt::ItemIsEditable; break; default: break; } } return flags; } void TaskWorkPackageModel::slotNodeToBeInserted( Node *parent, int row ) { //debugPlanWork<name()<<"; "<parentNode()->name()<<"-->"<name(); endInsertRows(); } void TaskWorkPackageModel::slotNodeToBeRemoved( Node *node ) { //debugPlanWork<name(); int row = indexForNode( node ).row(); beginRemoveRows( indexForNode( node->parentNode() ), row, row ); } void TaskWorkPackageModel::slotNodeRemoved( Node */*node*/ ) { //debugPlanWork<name(); endRemoveRows(); } void TaskWorkPackageModel::slotNodeChanged( Node *node ) { if ( node == 0 || node->type() == Node::Type_Project ) { return; } int row = indexForNode( node ).row(); debugPlanWork<name()<parentNode() ), createIndex( row, columnCount()-1, node->parentNode() ) ); } void TaskWorkPackageModel::slotDocumentAdded( Node *node, Document */*doc*/, int row ) { QModelIndex parent = indexForNode( node ); if ( parent.isValid() ) { beginInsertRows( parent, row, row ); endInsertRows(); } } void TaskWorkPackageModel::slotDocumentRemoved( Node *node, Document */*doc*/, int row ) { QModelIndex parent = indexForNode( node ); if ( parent.isValid() ) { beginRemoveRows( parent, row, row ); endRemoveRows(); } } void TaskWorkPackageModel::slotDocumentChanged( Node *node, Document */*doc*/, int row ) { QModelIndex parent = indexForNode( node ); if ( parent.isValid() ) { emit dataChanged( index( row, 0, parent ), index( row, columnCount( parent ), parent ) ); } } void TaskWorkPackageModel::addWorkPackage( WorkPackage *package, int row ) { beginInsertRows( QModelIndex(), row, row ); Project *project = package->project(); endInsertRows(); if ( project ) { connect( project, &KPlato::Project::nodeChanged, this, &TaskWorkPackageModel::slotNodeChanged ); connect( project, &KPlato::Project::nodeToBeAdded, this, &TaskWorkPackageModel::slotNodeToBeInserted ); connect( project, &KPlato::Project::nodeToBeRemoved, this, &TaskWorkPackageModel::slotNodeToBeRemoved ); connect( project, &KPlato::Project::nodeAdded, this, &TaskWorkPackageModel::slotNodeInserted ); connect( project, &KPlato::Project::nodeRemoved, this, &TaskWorkPackageModel::slotNodeRemoved ); connect(project, &KPlato::Project::documentAdded, this, &TaskWorkPackageModel::slotDocumentAdded); connect(project, &KPlato::Project::documentRemoved, this, &TaskWorkPackageModel::slotDocumentRemoved); connect(project, &KPlato::Project::documentChanged, this, &TaskWorkPackageModel::slotDocumentChanged); } } void TaskWorkPackageModel::removeWorkPackage( WorkPackage *package, int row ) { beginRemoveRows( QModelIndex(), row, row ); Project *project = package->project(); debugPlanWork<project(); if ( project ) { disconnect( project, &KPlato::Project::nodeChanged, this, &TaskWorkPackageModel::slotNodeChanged ); disconnect( project, &KPlato::Project::nodeToBeAdded, this, &TaskWorkPackageModel::slotNodeToBeInserted ); disconnect( project, &KPlato::Project::nodeToBeRemoved, this, &TaskWorkPackageModel::slotNodeToBeRemoved ); disconnect( project, &KPlato::Project::nodeAdded, this, &TaskWorkPackageModel::slotNodeInserted ); disconnect( project, &KPlato::Project::nodeRemoved, this, &TaskWorkPackageModel::slotNodeRemoved ); disconnect(project, &KPlato::Project::documentAdded, this, &TaskWorkPackageModel::slotDocumentAdded); disconnect(project, &KPlato::Project::documentRemoved, this, &TaskWorkPackageModel::slotDocumentRemoved); disconnect(project, &KPlato::Project::documentChanged, this, &TaskWorkPackageModel::slotDocumentChanged); } endRemoveRows(); } QVariant TaskWorkPackageModel::name( const Resource *r, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return r->name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant TaskWorkPackageModel::email( const Resource *r, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return r->email(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant TaskWorkPackageModel::projectName( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: { const Node *proj = node->projectNode(); return proj == 0 ? QVariant() : proj->name(); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant TaskWorkPackageModel::projectManager( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: { const Node *proj = node->projectNode(); return proj == 0 ? QVariant() : proj->leader(); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } int TaskWorkPackageModel::rowCount( const QModelIndex &parent ) const { if ( ! parent.isValid() ) { //debugPlanWork<workPackageCount(); return m_part->workPackageCount(); // == no of nodes (1 node pr wp) } Node *n = nodeForIndex( parent ); if ( n ) { //debugPlanWork<documents().count(); return n->documents().count(); } //debugPlanWork<%2%3", wp->wbsCode(), n->name(), nodeData(n, NodeDescription, Qt::DisplayRole).toString()); } + if (role == Qt::DecorationRole && index.column() == NodeName) { + WorkPackage *wp = workPackage(index.row()); + if (wp->task()->completion().isFinished()) { + return koIcon("checkmark"); + } + } return nodeData( n, index.column(), role ); } Document *doc = documentForIndex( index ); if ( doc ) { return documentData( doc, index.column(), role ); } return QVariant(); } QVariant TaskWorkPackageModel::actualStart( Node *n, int role ) const { QVariant v = m_nodemodel.startedTime( n, role ); if ( role == Qt::EditRole && ! v.toDateTime().isValid() ) { v = QDateTime::currentDateTime(); } return v; } QVariant TaskWorkPackageModel::actualFinish( Node *n, int role ) const { QVariant v = m_nodemodel.finishedTime( n, role ); if ( role == Qt::EditRole && ! v.toDateTime().isValid() ) { v = QDateTime::currentDateTime(); } return v; } QVariant TaskWorkPackageModel::plannedEffort( Node *n, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: { Duration v = n->plannedEffort( CURRENTSCHEDULE, ECCT_EffortWork ); return v.format(); } default: break; } return QVariant(); } QVariant TaskWorkPackageModel::status( Node *n, int role ) const { return m_nodemodel.status( n, role ); } QVariant TaskWorkPackageModel::nodeData( Node *n, int column, int role ) const { if ( role >= Qt::UserRole ) { // debugPlanWork<name()<type() ) { case Node::Type_Task: return KGantt::TypeTask; default: break; } break; case KGantt::StartTimeRole: debugPlanWork<name()<<"start:"<startTime(); return m_nodemodel.data( n, NodeModel::NodeStartTime, Qt::EditRole ); case KGantt::EndTimeRole: debugPlanWork<name()<<"end:"<endTime(); return m_nodemodel.data( n, NodeModel::NodeEndTime, Qt::EditRole ); default: break; } } switch ( column ) { case NodeName: return m_nodemodel.data( n, NodeModel::NodeName, role ); case NodeType: return m_nodemodel.data( n, NodeModel::NodeType, role ); case NodeResponsible: return m_nodemodel.data( n, NodeModel::NodeResponsible, role ); case NodeDescription: return m_nodemodel.data( n, NodeModel::NodeDescription, role ); // After scheduling case NodeStartTime: return m_nodemodel.data( n, NodeModel::NodeStartTime, role ); case NodeEndTime: return m_nodemodel.data( n, NodeModel::NodeEndTime, role ); case NodeAssignments: return m_nodemodel.data( n, NodeModel::NodeAssignments, role ); // Completion case NodeCompleted: return m_nodemodel.data( n, NodeModel::NodeCompleted, role ); case NodeActualEffort: return m_nodemodel.data( n, NodeModel::NodeActualEffort, role ); case NodeRemainingEffort: return m_nodemodel.data( n, NodeModel::NodeRemainingEffort, role ); case NodePlannedEffort: return plannedEffort( n, role ); case NodeActualStart: return actualStart( n, role ); case NodeStarted: return m_nodemodel.data( n, NodeModel::NodeStarted, role ); case NodeActualFinish: return actualFinish( n, role ); case NodeFinished: return m_nodemodel.data( n, NodeModel::NodeFinished, role ); case NodeStatus: return status( n, role ); case NodeStatusNote: return m_nodemodel.data( n, NodeModel::NodeStatusNote, role ); case ProjectName: return projectName( n, role ); case ProjectManager: return projectManager( n, role ); default: //debugPlanWork<<"Invalid column number: "<url().fileName()<name(); case NodeType: return doc->typeToString( doc->type(), true ); case NodeStatusNote: return doc->status(); default: return ""; } } else if ( role == Qt::ToolTipRole ) { switch ( column ) { case NodeName: return doc->typeToString( doc->type(), true ); default: break; } } return QVariant(); } bool TaskWorkPackageModel::setCompletion( Node *node, const QVariant &value, int role ) { if ( role != Qt::EditRole ) { return false; } if ( node->type() == Node::Type_Task ) { Completion &c = static_cast( node )->completion(); QDate date = qMax( c.entryDate(), QDate::currentDate() ); QDateTime dt( date, QTime::currentTime() ); // xgettext: no-c-format MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) ); if ( ! c.isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); } m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) ); if ( value.toInt() == 100 ) { m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); } bool newentry = c.entryDate() < date; emit executeCommand( m ); // also adds a new entry if necessary if ( newentry ) { // new entry so calculate used/remaining based on completion Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlanWork<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); cmd->execute(); m->addCommand( cmd ); } else if ( c.isFinished() && c.remainingEffort() != 0 ) { ModifyCompletionRemainingEffortCmd *cmd = new ModifyCompletionRemainingEffortCmd( c, date, Duration::zeroDuration ); cmd->execute(); m->addCommand( cmd ); } return true; } if ( node->type() == Node::Type_Milestone ) { Completion &c = static_cast( node )->completion(); if ( value.toInt() > 0 ) { QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Set finished" ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) ); emit executeCommand( m ); // also adds a new entry if necessary return true; } return false; } return false; } bool TaskWorkPackageModel::setRemainingEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); emit executeCommand( new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ) ); return true; } return false; } bool TaskWorkPackageModel::setActualEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); emit executeCommand( new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ) ); return true; } return false; } bool TaskWorkPackageModel::setStartedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return false; } MacroCommand *m = new MacroCommand( kundo2_noi18n(headerData( NodeModel::NodeActualStart, Qt::Horizontal, Qt::DisplayRole ).toString()) ); //FIXME: proper description when string freeze is lifted if ( ! t->completion().isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); } m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } emit executeCommand( m ); return true; } } return false; } bool TaskWorkPackageModel::setFinishedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return false; } MacroCommand *m = new MacroCommand( kundo2_noi18n(headerData( NodeModel::NodeActualFinish, Qt::Horizontal, Qt::DisplayRole ).toString()) ); //FIXME: proper description when string freeze is lifted if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); if ( t->completion().percentFinished() < 100 ) { QDate lastdate = t->completion().entryDate(); if ( ! lastdate.isValid() || lastdate < value.toDate() ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } else { Completion::Entry *e = new Completion::Entry( *( t->completion().entry( lastdate ) ) ); e->percentFinished = 100; m->addCommand( new ModifyCompletionEntryCmd( t->completion(), lastdate, e ) ); } } } m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); } emit executeCommand( m ); return true; } } return false; } bool TaskWorkPackageModel::setData( const QModelIndex &index, const QVariant &value, int role ) { if ( ! index.isValid() ) { return ItemModelBase::setData( index, value, role ); } switch ( index.column() ) { case NodeCompleted: return setCompletion( nodeForIndex( index ), value, role ); case NodeRemainingEffort: return setRemainingEffort( nodeForIndex( index ), value, role ); case NodeActualEffort: return setActualEffort( nodeForIndex( index ), value, role ); case NodeActualStart: return setStartedTime( nodeForIndex( index ), value, role ); case NodeActualFinish: return setFinishedTime( nodeForIndex( index ), value, role ); default: break; } return false; } QVariant TaskWorkPackageModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Vertical ) { return section; } if ( role == Qt::DisplayRole ) { switch ( section ) { case NodeName: return i18n( "Name" ); case NodeType: return i18n( "Type" ); case NodeResponsible: return i18n( "Responsible" ); case NodeDescription: return i18n( "Description" ); // After scheduling case NodeStartTime: return i18n( "Planned Start" ); case NodeEndTime: return i18n( "Planned Finish" ); case NodeAssignments: return i18n( "Resource Assignments" ); // Completion case NodeCompleted: return i18n( "Completion" ); case NodeActualEffort: return i18n( "Actual Effort" ); case NodeRemainingEffort: return i18n( "Remaining Effort" ); case NodePlannedEffort: return i18n( "Planned Effort" ); case NodeActualStart: return i18n( "Actual Start" ); case NodeStarted: return i18n( "Started" ); case NodeActualFinish: return i18n( "Actual Finish" ); case NodeFinished: return i18n( "Finished" ); case NodeStatus: return i18nc( "@title:column", "Status" ); case NodeStatusNote: return i18n( "Note" ); case ProjectName: return i18n( "Project Name" ); case ProjectManager: return i18n( "Project Manager" ); default: //debugPlanWork<<"Invalid column number: "<node()->name(); return wp->node(); } return 0; } Document *TaskWorkPackageModel::documentForIndex( const QModelIndex &index ) const { if ( index.isValid() ) { Node *parent = ptrToNode( index ); if ( parent && index.row() < parent->documents().count() ) { //debugPlanWork<name(); return parent->documents().value( index.row() ); } } return 0; } QModelIndex TaskWorkPackageModel::indexForNode( Node *node ) const { WorkPackage *p = m_part->workPackage( node ); if ( p == 0 ) { return QModelIndex(); } return createIndex( m_part->indexOf( p ), 0, p ); } WorkPackage *TaskWorkPackageModel::workPackage( int index ) const { return m_part->workPackage( index ); } QAbstractItemDelegate *TaskWorkPackageModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeActualEffort: return new DurationSpinBoxDelegate( parent ); case NodeActualStart: return new DateTimeCalendarDelegate( parent ); case NodeActualFinish: return new DateTimeCalendarDelegate( parent ); default: break; } return 0; } WorkPackage *TaskWorkPackageModel::ptrToWorkPackage( const QModelIndex &idx ) const { return qobject_cast( static_cast( idx.internalPointer() ) ); } Node *TaskWorkPackageModel::ptrToNode( const QModelIndex &idx ) const { return qobject_cast( static_cast( idx.internalPointer() ) ); } bool TaskWorkPackageModel::isNode( const QModelIndex &idx ) const { // a node index: ptr is WorkPackage* return qobject_cast( static_cast( idx.internalPointer() ) ) != 0; } bool TaskWorkPackageModel::isDocument( const QModelIndex &idx ) const { // a document index: ptr is Node* return qobject_cast( static_cast( idx.internalPointer() ) ) != 0; } } //namespace KPlato