diff --git a/src/libs/kernel/kpttask.cpp b/src/libs/kernel/kpttask.cpp index 7472f674..a358f50f 100644 --- a/src/libs/kernel/kpttask.cpp +++ b/src/libs/kernel/kpttask.cpp @@ -1,3856 +1,3856 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard 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 "kpttask.h" #include "kptappointment.h" #include "kptproject.h" #include "kptduration.h" #include "kptrelation.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include #include #include namespace KPlato { Task::Task(Node *parent) : Node(parent), m_resource(), m_workPackage( this ) { //debugPlan<<"("<setOptimisticRatio(-10); m_estimate->setPessimisticRatio(20); m_estimate->setParentNode( this ); if (m_parent) m_leader = m_parent->leader(); } Task::Task(const Task &task, Node *parent) : Node(task, parent), m_resource(), m_workPackage( this ) { //debugPlan<<"("<setParentNode( this ); } Task::~Task() { while (!m_resource.isEmpty()) { delete m_resource.takeFirst(); } while (!m_parentProxyRelations.isEmpty()) { delete m_parentProxyRelations.takeFirst(); } while (!m_childProxyRelations.isEmpty()) { delete m_childProxyRelations.takeFirst(); } } int Task::type() const { if ( numChildren() > 0) { return Node::Type_Summarytask; } else if ( m_constraint == Node::FixedInterval ) { if ( m_constraintEndTime == m_constraintStartTime ) { return Node::Type_Milestone; } } else if ( m_estimate->expectedEstimate() == 0.0 ) { return Node::Type_Milestone; } return Node::Type_Task; } Duration *Task::getRandomDuration() { return 0L; } ResourceGroupRequest *Task::resourceGroupRequest(const ResourceGroup *group) const { return m_requests.find(group); } void Task::clearResourceRequests() { m_requests.clear(); changed( this ); } void Task::addRequest(ResourceGroup *group, int numResources) { addRequest(new ResourceGroupRequest(group, numResources)); } void Task::addRequest(ResourceGroupRequest *request) { //debugPlan<group()<group()->id()<group()->name(); m_requests.addRequest(request); changed( this ); } void Task::takeRequest(ResourceGroupRequest *request) { //debugPlan< Task::requestedResources() const { return m_requests.requestedResources(); } bool Task::containsRequest( const QString &identity ) const { return m_requests.contains( identity ); } ResourceRequest *Task::resourceRequest( const QString &name ) const { return m_requests.resourceRequest( name ); } QStringList Task::assignedNameList( long id) const { Schedule *s = schedule( id ); if ( s == 0 ) { return QStringList(); } return s->resourceNameList(); } void Task::makeAppointments() { if (m_currentSchedule == 0) return; if (type() == Node::Type_Task) { //debugPlan<startTime<<","<endTime<<";"<duration.toString(); m_requests.makeAppointments(m_currentSchedule); //debugPlan<startTime<<","<endTime<<";"<duration.toString(); } else if (type() == Node::Type_Summarytask) { foreach (Node *n, m_nodes) { n->makeAppointments(); } } else if (type() == Node::Type_Milestone) { //debugPlan<<"Milestone not implemented"; // Well, shouldn't have resources anyway... } } void Task::copySchedule() { if ( m_currentSchedule == 0 || type() != Node::Type_Task ) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast( findSchedule( id ) ); if ( ns == 0 ) { return; } if ( type() == Node::Type_Task ) { copyAppointments( ns->startTime, ns->endTime ); } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; m_currentSchedule->endTime = ns->endTime; m_currentSchedule->lateFinish = ns->lateFinish; m_currentSchedule->duration = ns->duration; // TODO: status flags, etc //debugPlan; } void Task::copyAppointments() { copyAppointments( DateTime(), m_currentSchedule->startTime ); } void Task::copyAppointments( const DateTime &start, const DateTime &end ) { if ( m_currentSchedule == 0 || type() != Node::Type_Task ) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast( findSchedule( id ) ); if ( ns == 0 ) { return; } DateTime st = start.isValid() ? start : ns->startTime; DateTime et = end.isValid() ? end : ns->endTime; //debugPlan<calculationMode(); foreach ( const Appointment *a, ns->appointments() ) { Resource *r = a->resource() == 0 ? 0 : a->resource()->resource(); if ( r == 0 ) { errorPlan<<"No resource"; continue; } AppointmentIntervalList lst = a->intervals( st, et ); if ( lst.isEmpty() ) { //debugPlan<<"No intervals to copy from"<appointments() ) { if ( c->resource()->resource() == r ) { //debugPlan<<"Found current appointment to"<resource()->resource()->name()<add( curr ); curr->setNode( m_currentSchedule ); //debugPlan<<"Created new appointment"<( r->findSchedule( m_currentSchedule->id() ) ); if ( rs == 0 ) { rs = r->createSchedule( m_currentSchedule->parent() ); rs->setId( m_currentSchedule->id() ); rs->setName( m_currentSchedule->name() ); rs->setType( m_currentSchedule->type() ); //debugPlan<<"Resource schedule not found, id="<id(); } rs->setCalculationMode( m_currentSchedule->calculationMode() ); if ( ! rs->appointments().contains( curr ) ) { //debugPlan<<"add to resource"<add( curr ); curr->setResource( rs ); } Appointment app; app.setIntervals( lst ); //foreach ( AppointmentInterval *i, curr->intervals() ) { debugPlan<startTime().toString()<endTime().toString(); } curr->merge( app ); //debugPlan<<"Appointments added"; } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; } void Task::calcResourceOverbooked() { if (m_currentSchedule) m_currentSchedule->calcResourceOverbooked(); } bool Task::load(KoXmlElement &element, XMLLoaderObject &status ) { QString s; bool ok = false; m_id = element.attribute(QStringLiteral("id")); setName( element.attribute(QStringLiteral("name")) ); m_leader = element.attribute(QStringLiteral("leader")); m_description = element.attribute(QStringLiteral("description")); //debugPlan<load(e)) { if (!project.addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if (e.tagName() == QLatin1String("task")) { if (status.loadTaskChildren()) { // Load the task Task *child = new Task(this); if (child->load(e, status)) { if (!status.project().addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } else if (e.tagName() == QLatin1String("resource")) { // TODO: Load the resource (projects don't have resources yet) } else if (e.tagName() == QLatin1String("estimate") || ( /*status.version() < "0.6" &&*/ e.tagName() == QLatin1String("effort") ) ) { // Load the estimate m_estimate->load(e, status); } else if (e.tagName() == QLatin1String("resourcegroup-request")) { // Load the resource request // Handle multiple requests to same group gracefully (Not really allowed) ResourceGroupRequest *r = m_requests.findGroupRequestById( e.attribute(QStringLiteral("group-id")) ); if ( r ) { warnPlan<<"Multiple requests to same group, loading into existing group"; if ( ! r->load( e, status ) ) { errorPlan<<"Failed to load resource request"; } } else { r = new ResourceGroupRequest(); if (r->load(e, status)) { addRequest(r); } else { errorPlan<<"Failed to load resource request"; delete r; } } } else if (e.tagName() == QLatin1String("workpackage")) { m_workPackage.loadXML( e, status ); } else if (e.tagName() == QLatin1String("progress")) { completion().loadXML( e, status ); } else if (e.tagName() == QLatin1String("schedules")) { KoXmlNode n = e.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == QLatin1String("schedule")) { NodeSchedule *sch = new NodeSchedule(); if (sch->loadXML(el, status)) { sch->setNode(this); addSchedule(sch); } else { errorPlan<<"Failed to load schedule"; delete sch; } } } } else if (e.tagName() == QLatin1String("documents")) { m_documents.load( e, status ); } else if (e.tagName() == QLatin1String("workpackage-log")) { KoXmlNode n = e.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == QLatin1String("workpackage")) { WorkPackage *wp = new WorkPackage( this ); if ( wp->loadLoggedXML( el, status ) ) { m_packageLog << wp; } else { errorPlan<<"Failed to load logged workpackage"; delete wp; } } } } } //debugPlan<save(me); m_documents.save( me ); if ( ! m_requests.isEmpty() ) { m_requests.save(me); } if (context.saveAll(this)) { if (!m_schedules.isEmpty()) { QDomElement schs = me.ownerDocument().createElement(QStringLiteral("schedules")); me.appendChild(schs); foreach (const Schedule *s, m_schedules) { if (!s->isDeleted()) { s->saveXML(schs); } } } completion().saveXML( me ); m_workPackage.saveXML(me); // The workpackage log if (!m_packageLog.isEmpty()) { QDomElement log = me.ownerDocument().createElement(QStringLiteral("workpackage-log")); me.appendChild(log); foreach (const WorkPackage *wp, m_packageLog) { wp->saveLoggedXML( log ); } } } if (context.saveChildren(this)) { for (int i=0; isave(me, context); } } } void Task::saveAppointments(QDomElement &element, long id) const { //debugPlan<save(me); completion().saveXML( me ); if ( m_schedules.contains( id ) && ! m_schedules[ id ]->isDeleted() ) { QDomElement schs = me.ownerDocument().createElement(QStringLiteral("schedules")); me.appendChild(schs); m_schedules[ id ]->saveXML( schs ); } m_documents.save( me ); // TODO: copying documents } bool Task::isStarted() const { return completion().isStarted(); } EffortCostMap Task::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( start, end, id, typ ); } return ec; } Schedule *s = schedule( id ); if ( s ) { return s->plannedEffortCostPrDay( start, end, typ ); } return EffortCostMap(); } EffortCostMap Task::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( resource, start, end, id, typ ); } return ec; } Schedule *s = schedule( id ); if ( s ) { return s->plannedEffortCostPrDay( resource, start, end, typ ); } return EffortCostMap(); } EffortCostMap Task::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( start, end, id, typ ); } return ec; } switch ( completion().entrymode() ) { case Completion::FollowPlan: return plannedEffortCostPrDay( start, end, id, typ ); default: return completion().effortCostPrDay( start, end, id ); } return EffortCostMap(); } EffortCostMap Task::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( resource, start, end, id, typ ); } return ec; } switch ( completion().entrymode() ) { case Completion::FollowPlan: return plannedEffortCostPrDay( resource, start, end, id, typ ); default: return completion().effortCostPrDay( resource, start, end ); } return EffortCostMap(); } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort( const Resource *resource, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( resource, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( resource, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort( long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date Duration Task::plannedEffort( const Resource *resource, QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( resource, date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( resource, date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date Duration Task::plannedEffort(QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date Duration Task::plannedEffortTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo( date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffortTo( date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date Duration Task::plannedEffortTo( const Resource *resource, QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo( resource, date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffortTo( resource, date, typ ); } return eff; } // Returns the total actual effort for this task (or subtasks) Duration Task::actualEffort() const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort(); } } return completion().actualEffort(); } // Returns the total actual effort for this task (or subtasks) on date Duration Task::actualEffort( QDate date ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort( date ); } return eff; } return completion().actualEffort( date ); } // Returns the total actual effort for this task (or subtasks) to date Duration Task::actualEffortTo( QDate date ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffortTo( date ); } return eff; } return completion().actualEffortTo( date ); } EffortCost Task::plannedCost( long id, EffortCostCalculationType typ ) const { //debugPlan; if (type() == Node::Type_Summarytask) { return Node::plannedCost( id, typ ); } EffortCost c; Schedule *s = schedule( id ); if ( s ) { c = s->plannedCost( typ ); } return c; } double Task::plannedCostTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; double c = 0; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { c += n->plannedCostTo( date, id, typ ); } return c; } Schedule *s = schedule( id ); if ( s == 0 ) { return c; } c = s->plannedCostTo( date, typ ); if ( date >= s->startTime.date() ) { c += m_startupCost; } if ( date >= s->endTime.date() ) { c += m_shutdownCost; } return c; } EffortCost Task::actualCostTo( long int id, QDate date ) const { //debugPlan; EffortCostMap ecm = acwp( id ); return EffortCost( ecm.effortTo( date ), ecm.costTo( date ) ); } double Task::bcws( QDate date, long id ) const { //debugPlan; double c = plannedCostTo( date, id ); //debugPlan<bcwsPrDayCache( typ ); if ( ! cache.cached ) { EffortCostMap ec = s->bcwsPrDay( typ ); if ( typ != ECCT_Work ) { if ( m_startupCost > 0.0 ) { ec.add( s->startTime.date(), Duration::zeroDuration, m_startupCost ); } if ( m_shutdownCost > 0.0 ) { ec.add( s->endTime.date(), Duration::zeroDuration, m_shutdownCost ); } cache.effortcostmap = ec; cache.cached = true; } } return cache.effortcostmap; } EffortCostMap Task::bcwpPrDay( long int id, EffortCostCalculationType typ ) { //debugPlan; if ( type() == Node::Type_Summarytask ) { return Node::bcwpPrDay( id, typ ); } Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache cache = s->bcwpPrDayCache( typ ); if ( ! cache.cached ) { // do not use bcws cache, it includes startup/shutdown cost EffortCostMap e = s->plannedEffortCostPrDay( s->appointmentStartTime().date(), s->appointmentEndTime().date(), typ ); if ( completion().isStarted() && ! e.isEmpty() ) { // calculate bcwp on bases of bcws *without* startup/shutdown cost double totEff = e.totalEffort().toDouble( Duration::Unit_h ); double totCost = e.totalCost(); QDate sd = completion().entries().keys().value( 0 ); if ( ! sd.isValid() || e.startDate() < sd ) { sd = e.startDate(); } QDate ed = qMax( e.endDate(), completion().entryDate() ); for ( QDate d = sd; d <= ed; d = d.addDays( 1 ) ) { double p = (double)(completion().percentFinished( d )) / 100.0; EffortCost ec = e.days()[ d ]; ec.setBcwpEffort( totEff * p ); ec.setBcwpCost( totCost * p ); e.insert( d, ec ); } } if ( typ != ECCT_Work ) { // add bcws startup/shutdown cost if ( m_startupCost > 0.0 ) { e.add( s->startTime.date(), Duration::zeroDuration, m_startupCost ); } if ( m_shutdownCost > 0.0 ) { e.add( s->endTime.date(), Duration::zeroDuration, m_shutdownCost ); } // add bcwp startup/shutdown cost if ( m_shutdownCost > 0.0 && completion().finishIsValid() ) { QDate finish = completion().finishTime().date(); e.addBcwpCost( finish, m_shutdownCost ); debugPlan<<"addBcwpCost:"< finish ) { e.addBcwpCost( date, m_shutdownCost ); debugPlan<<"addBcwpCost:"< 0.0 && completion().startIsValid() ) { QDate start = completion().startTime().date(); e.addBcwpCost( start, m_startupCost ); // bcwp is cumulative so add to all entries after start for ( EffortCostDayMap::const_iterator it = e.days().constBegin(); it != e.days().constEnd(); ++it ) { const QDate date = it.key(); if ( date > start ) { e.addBcwpCost( date, m_startupCost ); } } } } cache.effortcostmap = e; cache.cached = true; } return cache.effortcostmap; } Duration Task::budgetedWorkPerformed( QDate date, long id ) const { //debugPlan; Duration e; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed( date, id ); } return e; } e = plannedEffort( id ) * (double)completion().percentFinished( date ) / 100.0; //debugPlan<budgetedCostPerformed( date, id ); } return c; } c = plannedCost( id ).cost() * (double)completion().percentFinished( date ) / 100.0; if ( completion().isStarted() && date >= completion().startTime().date() ) { c += m_startupCost; } if ( completion().isFinished() && date >= completion().finishTime().date() ) { c += m_shutdownCost; } //debugPlan<acwpCache( typ ); if ( ! ec.cached ) { //debugPlan<= completion().startTime().date() ) { c.add( Duration::zeroDuration, m_startupCost ); } if ( completion().isFinished() && date >= completion().finishTime().date() ) { c.add( Duration::zeroDuration, m_shutdownCost ); } return c; } double Task::schedulePerformanceIndex( QDate date, long id ) const { //debugPlan; double r = 1.0; double s = bcws( date, id ); double p = bcwp( date, id ); if ( s > 0.0 ) { r = p / s; } return r; } double Task::effortPerformanceIndex( QDate date, long id ) const { //debugPlan; double r = 1.0; Duration a, b; if ( m_estimate->type() == Estimate::Type_Effort ) { Duration b = budgetedWorkPerformed( date, id ); if ( b == Duration::zeroDuration ) { return r; } Duration a = actualEffortTo( date ); if ( b == Duration::zeroDuration ) { return 1.0; } r = b.toDouble() / a.toDouble(); } else if ( m_estimate->type() == Estimate::Type_Duration ) { //TODO } return r; } //FIXME Handle summarytasks double Task::costPerformanceIndex( long int id, QDate date, bool *error ) const { double res = 0.0; double ac = actualCostTo( id, date ).cost(); bool e = ( ac == 0.0 || completion().percentFinished() == 0 ); if (error) { *error = e; } if (!e) { res = ( plannedCostTo( date, id ) * completion().percentFinished() ) / ( 100 * ac ); } return res; } void Task::initiateCalculation(MainSchedule &sch) { //debugPlan< &list, int use) { DateTime time; // do them forward foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->parent()->calculateForward(use); // early finish switch (r->type()) { case Relation::StartStart: // I can't start earlier than my predesseccor t = r->parent()->earlyStart() + r->lag(); break; case Relation::FinishFinish: { // I can't finish earlier than my predeccessor, so // I can't start earlier than it's (earlyfinish+lag)- my duration t += r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState( Schedule::OBS_Allow ); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("FinishFinish: get duration to calculate early finish") ); #endif t -= duration(t, use, true); m_currentSchedule->setAllowOverbookingState( obs ); break; } default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<earlyFinish; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::CalculateForward ); //cs->logDebug( "calculateForward: earlyStart=" + cs->earlyStart.toString() ); // calculate all predecessors if (!dependParentNodes().isEmpty()) { DateTime time = calculatePredeccessors(dependParentNodes(), use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug( QString( "calculate forward: early start moved to: %1" ).arg( cs->earlyStart.toString() ) ); } } if (!m_parentProxyRelations.isEmpty()) { DateTime time = calculatePredeccessors(m_parentProxyRelations, use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug( QString( "calculate forward: early start moved to: %1" ).arg( cs->earlyStart.toString() ) ); } } m_calculateForwardRun = true; //cs->logDebug( "calculateForward: earlyStart=" + cs->earlyStart.toString() ); return calculateEarlyFinish( use ); } DateTime Task::calculateEarlyFinish(int use) { //debugPlan<usePert(); cs->setCalculationMode( Schedule::CalculateForward ); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug( QStringLiteral( "Start calculate forward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Calculate early finish " ) ); //debugPlan<<"------>"<earlyStart; if (type() == Node::Type_Task) { m_durationForward = m_estimate->value(use, pert); switch (constraint()) { case Node::ASAP: case Node::ALAP: { //debugPlan<earlyStart; cs->earlyStart = workTimeAfter( cs->earlyStart ); m_durationForward = duration(cs->earlyStart, use, false); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + m_earlyFinish.toString() ); #endif if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationForward = duration(cs->earlyStart, use, false); cs->setAllowOverbookingState( obs ); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString() ); #endif } break; } case Node::MustFinishOn: { cs->earlyStart = workTimeAfter( cs->earlyStart ); m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"MustFinishOn:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->earlyFinish = qMax( cs->earlyFinish, m_constraintEndTime ); if ( !cs->allowOverbooking() ) { cs->endTime = cs->earlyFinish; cs->startTime = cs->earlyFinish - duration( cs->earlyFinish, use, true ); makeAppointments(); } m_earlyFinish = cs->earlyFinish; m_durationForward = m_earlyFinish - cs->earlyStart; break; } case Node::FinishNotLater: { m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"FinishNotLater:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = cs->earlyFinish; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MSO/SNE:"<earlyStart; cs->logDebug( constraintToString() + ": " + m_constraintStartTime.toString() + ' ' + cs->earlyStart.toString() ); cs->earlyStart = workTimeAfter( qMax( cs->earlyStart, m_constraintStartTime ) ); if ( cs->earlyStart < m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_durationForward = duration( cs->earlyStart, use, false ); m_earlyFinish = cs->earlyStart + m_durationForward; if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationForward = duration(cs->startTime, use, false); cs->setAllowOverbookingState( obs ); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("MSO/SNE earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString() ); #endif } break; } case Node::FixedInterval: { if ( cs->earlyStart > m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } //cs->earlyStart = m_constraintStartTime; m_durationForward = m_constraintEndTime - m_constraintStartTime; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if ( !cs->allowOverbooking() ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } } } else if (type() == Node::Type_Milestone) { m_durationForward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<earlyStart; //cs->logDebug( QString( "%1: %2, early start: %3" ).arg( constraintToString() ).arg( m_constraintEndTime.toString() ).arg( cs->earlyStart.toString() ) ); if ( cs->earlyStart < m_constraintEndTime ) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if ( cs->earlyStart > m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<earlyStart; if ( cs->earlyStart > m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<earlyStart; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintStartTime - cs->earlyStart; } if ( cs->earlyStart > m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::StartNotEarlier: //debugPlan<<"StartNotEarlier:"<earlyStart; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintStartTime - cs->earlyStart; } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FixedInterval: m_earlyFinish = cs->earlyStart + m_durationForward; break; default: m_earlyFinish = cs->earlyStart + m_durationForward; break; } //debugPlan<insertForwardNode( this ); cs->earlyFinish = cs->earlyStart + m_durationForward; foreach ( const Appointment *a, cs->appointments( Schedule::CalculateForward ) ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo( i18n( "Early finish calculated: %1", locale.toString(cs->earlyFinish, QLocale::ShortFormat) ) ); cs->incProgress(); #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "Finished calculate forward: %1 ms" ).arg( timer.elapsed() ) ); #endif return m_earlyFinish; } DateTime Task::calculateSuccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->child()->calculateBackward(use); switch (r->type()) { case Relation::StartStart: { // I must start before my successor, so // I can't finish later than it's (starttime-lag) + my duration t -= r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState( Schedule::OBS_Allow ); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("StartStart: get duration to calculate late start") ); #endif t += duration(t, use, false); m_currentSchedule->setAllowOverbookingState( obs ); break; } case Relation::FinishFinish: // My successor cannot finish before me, so // I can't finish later than it's latest finish - lag t = r->child()->lateFinish() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } //debugPlan<lateStart; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::CalculateBackward ); //cs->lateFinish = projectNode()->constraintEndTime(); // calculate all successors if (!dependChildNodes().isEmpty()) { DateTime time = calculateSuccessors(dependChildNodes(), use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } if (!m_childProxyRelations.isEmpty()) { DateTime time = calculateSuccessors(m_childProxyRelations, use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } m_calculateBackwardRun = true; return calculateLateStart( use ); } DateTime Task::calculateLateStart(int use) { //debugPlan<lateStart; } bool pert = cs->usePert(); cs->setCalculationMode( Schedule::CalculateBackward ); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug( QStringLiteral( "Start calculate backward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Calculate late start" ) ); cs->logDebug( QStringLiteral( "%1: late finish= %2" ).arg( constraintToString() ).arg( cs->lateFinish.toString() ) ); //debugPlan<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + cs->lateStart.toString() ); #endif if ( !cs->allowOverbooking() ) { cs->startTime = cs->lateStart; cs->endTime = cs->lateFinish; makeAppointments(); // calculate wo checking bookings = latest start possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationBackward = duration(cs->lateFinish, use, true); cs->setAllowOverbookingState( obs ); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP latest start possible: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + (cs->lateFinish-m_durationBackward).toString() ); #endif } break; case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MustStartOn:"<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; if ( cs->lateStart < m_constraintStartTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } else { cs->lateStart = qMax( cs->earlyStart, m_constraintStartTime ); } if ( !cs->allowOverbooking() ) { if ( constraint() == MustStartOn ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintStartTime + duration( m_constraintStartTime, use, false ); } else { cs->startTime = qMax( cs->lateStart, m_constraintStartTime ); cs->endTime = qMax( cs->lateFinish, cs->startTime ); // safety } makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } case Node::MustFinishOn: case Node::FinishNotLater: //debugPlan<<"MustFinishOn:"<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); cs->endTime = cs->lateFinish; if ( cs->lateFinish < m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } else { cs->endTime = qMax( cs->earlyFinish, m_constraintEndTime ); } m_durationBackward = duration(cs->endTime, use, true); cs->startTime = cs->endTime - m_durationBackward; if ( !cs->allowOverbooking() ) { makeAppointments(); } m_durationBackward = cs->lateFinish - cs->startTime; cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FixedInterval: { //cs->lateFinish = m_constraintEndTime; if ( cs->lateFinish < m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_durationBackward = m_constraintEndTime - m_constraintStartTime; if ( cs->lateFinish > m_constraintEndTime ) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } if ( !cs->allowOverbooking() ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } } } else if (type() == Node::Type_Milestone) { m_durationBackward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<lateFinish; if ( m_constraintEndTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if ( m_constraintEndTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<lateFinish; if ( m_constraintEndTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if ( m_constraintEndTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<lateFinish; if ( m_constraintStartTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } else if ( m_constraintStartTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; //cs->logDebug( QString( "%1: constraint:%2, start=%3, finish=%4" ).arg( constraintToString() ).arg( m_constraintStartTime.toString() ).arg( cs->lateStart.toString() ).arg( cs->lateFinish.toString() ) ); break; case Node::StartNotEarlier: //debugPlan<<"MustStartOn:"<lateFinish; if ( m_constraintStartTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish; break; case Node::FixedInterval: cs->lateStart = cs->lateFinish - m_durationBackward; break; default: cs->lateStart = cs->lateFinish - m_durationBackward; break; } //debugPlan<lateFinish; } else if (type() == Node::Type_Summarytask) { warnPlan<<"Summarytasks should not be calculated here: "<insertBackwardNode( this ); cs->lateStart = cs->lateFinish - m_durationBackward; foreach ( const Appointment *a, cs->appointments( Schedule::CalculateBackward ) ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo( i18n( "Late start calculated: %1", locale.toString(cs->lateStart, QLocale::ShortFormat) ) ); cs->incProgress(); #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "Finished calculate backward: %1 ms" ).arg( timer.elapsed() ) ); #endif return cs->lateStart; } DateTime Task::schedulePredeccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } // schedule the predecessors DateTime earliest = r->parent()->earlyStart(); DateTime t = r->parent()->scheduleForward(earliest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my predesseccor t = r->parent()->startTime() + r->lag(); break; case Relation::FinishFinish: // I can't end before my predecessor, so // I can't start before it's endtime - my duration #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("FinishFinish: get duration to calculate earliest start") ); #endif t -= duration(t + r->lag(), use, true); break; default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<endTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; //cs->logDebug( QString( "Schedule forward (early start: %1)" ).arg( cs->earlyStart.toString() ) ); cs->setCalculationMode( Schedule::Scheduling ); DateTime startTime = earliest > cs->earlyStart ? earliest : cs->earlyStart; // First, calculate all my own predecessors DateTime time = schedulePredeccessors(dependParentNodes(), use); if ( time > startTime ) { startTime = time; //debugPlan<earlyStart.toString() ) ); cs->startTime = cs->earlyStart; } QTime timer; timer.start(); cs->logInfo( i18n( "Start schedule forward: %1 ", constraintToString( true ) ) ); QLocale locale; cs->logInfo( i18n( "Schedule from start %1", locale.toString(cs->startTime, QLocale::ShortFormat) ) ); //debugPlan<startTime<<"earliest:"<earlyStart; if ( false/*useCalculateForwardAppointments*/ && m_estimate->type() == Estimate::Type_Effort && ! cs->allowOverbooking() && cs->hasAppointments( Schedule::CalculateForward ) ) { #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString() ); #endif cs->copyAppointments( Schedule::CalculateForward, Schedule::Scheduling ); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); } cs->startTime = cs->appointmentStartTime(); cs->endTime = cs->appointmentEndTime(); Q_ASSERT( cs->startTime.isValid() ); Q_ASSERT( cs->endTime.isValid() ); cs->duration = cs->endTime - cs->startTime; if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); return cs->endTime; } cs->startTime = workTimeAfter( cs->startTime, cs ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString() ); #endif cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } break; case Node::ALAP: // cs->startTime calculated above //debugPlan<startTime<endTime<<" latest="<lateFinish; cs->endTime = workTimeBefore( cs->lateFinish, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<endTime = workTimeBefore( cs->earlyFinish, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; makeAppointments(); } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if ( cs->recalculate() && completion().isStarted() ) { cs->earlyStart = cs->startTime = completion().startTime(); } break; case Node::StartNotEarlier: // cs->startTime calculated above //debugPlan<<"StartNotEarlier:"<startTime<lateStart; cs->startTime = workTimeAfter( qMax( cs->startTime, m_constraintStartTime ), cs ); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::FinishNotLater: // cs->startTime calculated above //debugPlan<<"FinishNotLater:"<startTime; cs->startTime = workTimeAfter( cs->startTime, cs ); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->endTime > m_constraintEndTime) { //warnPlan<<"cs->endTime > m_constraintEndTime"; cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::MustStartOn: // Always try to put it on time cs->startTime = workTimeAfter( m_constraintStartTime, cs ); //debugPlan<<"MustStartOn="<startTime; cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "%1: Schedule from %2 to %3" ).arg( constraintToString() ).arg( cs->startTime.toString() ).arg( cs->endTime.toString() ) ); #endif makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (m_constraintStartTime < cs->startTime ) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::MustFinishOn: // Just try to schedule on time cs->endTime = workTimeBefore( m_constraintEndTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<<"MustFinishOn:"<lateFinish<<":"<startTime<endTime; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if ( cs->endTime != m_constraintEndTime ) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::FixedInterval: { // cs->startTime calculated above //debugPlan<<"FixedInterval="<startTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if ( m_constraintStartTime >= cs->earlyStart ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; } else { cs->startTime = cs->earlyStart; cs->endTime = cs->startTime + cs->duration; cs->constraintError = true; } if ( m_constraintStartTime < cs->startTime ) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->workStartTime = m_constraintStartTime; cs->workEndTime = m_constraintEndTime; //debugPlan<<"FixedInterval="<startTime<<","<endTime; makeAppointments(); break; } default: break; } if ( m_estimate->type() == Estimate::Type_Effort ) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = ( m_estimate->value( use, cs->usePert() ) - cs->plannedEffort() ) > ( 5 * 60000 ); if ( cs->effortNotMet ) { cs->logError( i18n( "Effort not met. Estimate: %1, planned: %2", estimate()->value( use, cs->usePert() ).toHours(), cs->plannedEffort().toHours() ) ); } } } else if (type() == Node::Type_Milestone) { if ( cs->recalculate() && completion().isFinished() ) { cs->startTime = completion().startTime(); cs->endTime = completion().finishTime(); m_visitedForward = true; return cs->endTime; } switch (m_constraint) { case Node::ASAP: { cs->endTime = cs->startTime; // TODO check, do we need to check succeccors earliestStart? cs->positiveFloat = cs->lateFinish - cs->endTime; break; } case Node::ALAP: { cs->startTime = qMax( cs->lateFinish, cs->earlyFinish ); cs->endTime = cs->startTime; cs->positiveFloat = Duration::zeroDuration; break; } case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { //debugPlan<<"MustStartOn:"<startTime; DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "%1: constraint time=%2, start time=%3" ).arg( constraintToString() ).arg( contime.toString() ).arg( cs->startTime.toString() ) ); #endif if ( cs->startTime < contime ) { if ( contime <= cs->lateFinish || contime <= cs->earlyFinish ) { cs->startTime = contime; } } cs->negativeFloat = cs->startTime > contime ? cs->startTime - contime : contime - cs->startTime; if ( cs->negativeFloat != 0 ) { cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: if ( cs->startTime < m_constraintStartTime ) { if ( m_constraintStartTime <= cs->lateFinish || m_constraintStartTime <= cs->earlyFinish ) { cs->startTime = m_constraintStartTime; } } if ( cs->startTime < m_constraintStartTime ) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: //debugPlan<startTime; if (cs->startTime > m_constraintEndTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; //debugPlan<startTime<<","<endTime; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->endTime = cs->startTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime<<" :"<endTime<<""<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime() ) { cs->logError( i18n( "Failed to schedule within project target time" ) ); } foreach ( const Appointment *a, cs->appointments() ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } if ( cs->startTime < cs->earlyStart ) { cs->logWarning( i18n( "Starting earlier than early start" ) ); } if ( cs->endTime > cs->lateFinish ) { cs->logWarning( i18n( "Finishing later than late finish" ) ); } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); m_visitedForward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); cs->logInfo( i18n( "Finished schedule forward: %1 ms", timer.elapsed() ) ); return cs->endTime; } DateTime Task::scheduleSuccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<child()->name(); continue; } // get the successors starttime DateTime latest = r->child()->lateFinish(); DateTime t = r->child()->scheduleBackward(latest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my successor, so // I can't finish later than it's starttime + my duration #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("StartStart: get duration to calculate late finish") ); #endif t += duration(t - r->lag(), use, false); break; case Relation::FinishFinish: t = r->child()->endTime() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } return time; } DateTime Task::scheduleBackward(const DateTime &latest, int use) { if ( m_scheduleBackwardRun ) { return m_currentSchedule->startTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::Scheduling ); DateTime endTime = latest < cs->lateFinish ? latest : cs->lateFinish; // First, calculate all my own successors DateTime time = scheduleSuccessors(dependChildNodes(), use); if (time.isValid() && time < endTime) { endTime = time; } // Then my parents time = scheduleSuccessors(m_childProxyRelations, use); if (time.isValid() && time < endTime) { endTime = time; } if ( ! m_visitedBackward ) { cs->endTime = endTime; } m_scheduleBackwardRun = true; return scheduleFromEndTime( use ); } DateTime Task::scheduleFromEndTime(int use) { //debugPlan<setCalculationMode( Schedule::Scheduling ); bool pert = cs->usePert(); if (m_visitedBackward) { return cs->startTime; } cs->notScheduled = false; if ( !cs->endTime.isValid() ) { cs->endTime = cs->lateFinish; } #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug( QStringLiteral( "Start schedule backward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Schedule from end time: %1", cs->endTime.toString() ) ); if (type() == Node::Type_Task) { cs->duration = m_estimate->value(use, pert); switch (m_constraint) { case Node::ASAP: { // cs->endTime calculated above //debugPlan<duration = duration( cs->endTime, use, true ); e = cs->endTime; cs->startTime = e - cs->duration; } if ( e > cs->lateFinish ) { cs->schedulingError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to schedule within late finish.", constraintToString() ) ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: late finish=" + cs->lateFinish.toString() + " end time=" + e.toString() ); #endif } else if ( e > cs->endTime ) { cs->schedulingError = true; cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to schedule within successors start time", constraintToString() ) ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: succ. start=" + cs->endTime.toString() + " end time=" + e.toString() ); #endif } if ( cs->lateFinish > e ) { DateTime w = workTimeBefore( cs->lateFinish ); if ( w > e ) { cs->positiveFloat = w - e; } #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: positiveFloat=" + cs->positiveFloat.toString() ); #endif } cs->endTime = e; makeAppointments(); break; } case Node::ALAP: { // cs->endTime calculated above //debugPlan<logDebug( "ALAP: earlyStart=" + cs->earlyStart.toString() + " cs->startTime=" + cs->startTime.toString() ); #endif } else if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; #ifndef PLAN_NLOGDEBUG cs->logDebug( "ALAP: positiveFloat=" + cs->positiveFloat.toString() ); #endif } //debugPlan<endTime; cs->endTime = workTimeBefore( cs->endTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if ( cs->startTime < m_constraintStartTime ) { //warnPlan<<"m_constraintStartTime > cs->lateStart"; cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::FinishNotLater: // cs->endTime calculated above //debugPlan<<"FinishNotLater:"<endTime; if (cs->endTime > m_constraintEndTime) { cs->endTime = qMax( qMin( m_constraintEndTime, cs->lateFinish ), cs->earlyFinish ); } cs->endTime = workTimeBefore( cs->endTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if ( cs->endTime > m_constraintEndTime ) { cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::MustStartOn: // Just try to schedule on time //debugPlan<<"MustStartOn="<startTime.toString(); cs->startTime = workTimeAfter( m_constraintStartTime, cs ); cs->duration = duration(cs->startTime, use, false); if ( cs->endTime >= cs->startTime + cs->duration ) { cs->endTime = cs->startTime + cs->duration; } else { cs->endTime = workTimeBefore( cs->endTime ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; } if (m_constraintStartTime != cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::MustFinishOn: // Just try to schedule on time //debugPlan<endTime<earlyFinish; cs->endTime = workTimeBefore( m_constraintEndTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime ) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); //warnPlan<<"m_constraintEndTime > cs->endTime"; } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::FixedInterval: { // cs->endTime calculated above //debugPlan<endTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if ( cs->endTime > m_constraintEndTime ) { cs->endTime = qMax( m_constraintEndTime, cs->earlyFinish ); } cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->workStartTime = workTimeAfter( cs->startTime ); cs->workEndTime = workTimeBefore( cs->endTime ); makeAppointments(); if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; } default: break; } m_requests.reserve(cs->startTime, cs->duration); if ( m_estimate->type() == Estimate::Type_Effort ) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = ( m_estimate->value( use, cs->usePert() ) - cs->plannedEffort() ) > ( 5 * 60000 ); if ( cs->effortNotMet ) { cs->logError( i18n( "Effort not met. Estimate: %1, planned: %2", estimate()->value( use, cs->usePert() ).toHours(), cs->plannedEffort().toHours() ) ); } } } else if (type() == Node::Type_Milestone) { switch (m_constraint) { case Node::ASAP: if ( cs->endTime < cs->earlyStart ) { cs->schedulingError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to schedule after early start.", constraintToString() ) ); cs->endTime = cs->earlyStart; } else { cs->positiveFloat = cs->lateFinish - cs->endTime; } //cs->endTime = cs->earlyStart; FIXME need to follow predeccessors. Defer scheduling? cs->startTime = cs->endTime; break; case Node::ALAP: cs->startTime = cs->endTime; cs->positiveFloat = cs->lateFinish - cs->endTime; break; case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; if ( contime < cs->earlyStart ) { if ( cs->earlyStart < cs->endTime ) { cs->endTime = cs->earlyStart; } } else if ( contime < cs->endTime ) { cs->endTime = contime; } cs->negativeFloat = cs->endTime > contime ? cs->endTime - contime : contime - cs->endTime; if ( cs->negativeFloat != 0 ) { cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->startTime = cs->endTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: cs->startTime = cs->endTime; if ( m_constraintStartTime > cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: if ( m_constraintEndTime < cs->earlyStart ) { if ( cs->earlyStart < cs->endTime ) { cs->endTime = cs->earlyStart; } } else if ( m_constraintEndTime < cs->endTime ) { cs->endTime = m_constraintEndTime; } if ( m_constraintEndTime > cs->endTime ) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->startTime = cs->endTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->startTime = cs->endTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime() ) { cs->logError( i18n( "Failed to schedule within project target time" ) ); } foreach ( const Appointment *a, cs->appointments() ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat ), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } if ( cs->startTime < cs->earlyStart ) { cs->logWarning( i18n( "Starting earlier than early start" ) ); } if ( cs->endTime > cs->lateFinish ) { cs->logWarning( i18n( "Finishing later than late finish" ) ); } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); m_visitedBackward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "Finished schedule backward: %1 ms" ).arg( timer.elapsed() ) ); #endif return cs->startTime; } void Task::adjustSummarytask() { if (m_currentSchedule == 0) return; if (type() == Type_Summarytask) { DateTime start = m_currentSchedule->lateFinish; DateTime end = m_currentSchedule->earlyStart; foreach (Node *n, m_nodes) { n->adjustSummarytask(); if (n->startTime() < start) start = n->startTime(); if (n->endTime() > end) end = n->endTime(); } m_currentSchedule->startTime = start; m_currentSchedule->endTime = end; m_currentSchedule->duration = end - start; m_currentSchedule->notScheduled = false; //debugPlan<name<<":"<startTime.toString()<<" :"<endTime.toString(); } } Duration Task::duration(const DateTime &time, int use, bool backward) { //debugPlan; // TODO: handle risc if (m_currentSchedule == 0) { errorPlan<<"No current schedule"; return Duration::zeroDuration; } if (!time.isValid()) { #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("Calculate duration: Start time is not valid") ); #endif return Duration::zeroDuration; } //debugPlan< calcDuration"<<(backward?"(B)":"(F)")<resourceNotAvailable = true; dur = effort; //??? } return dur; } if (m_estimate->type() == Estimate::Type_Duration) { return length( time, dur, backward ); } errorPlan<<"Unsupported estimate type: "<type(); return dur; } Duration Task::length(const DateTime &time, KPlato::Duration duration, bool backward) { return length( time, duration, m_currentSchedule, backward ); } Duration Task::length(const DateTime &time, KPlato::Duration duration, Schedule *sch, bool backward) { //debugPlan<<"--->"<<(backward?"(B)":"(F)")<logDebug( QStringLiteral("Calculate length: estimate == 0") ); #else Q_UNUSED(sch) #endif return l; } Calendar *cal = m_estimate->calendar(); if ( cal == 0) { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculate length: No calendar, return estimate " + duration.toString() ); #endif return duration; } #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculate length from: " + time.toString() ); #endif DateTime logtime = time; bool sts=true; bool match = false; DateTime start = time; int inc = backward ? -1 : 1; DateTime end = start; Duration l1; int nDays = backward ? projectNode()->constraintStartTime().daysTo( time ) : time.daysTo( projectNode()->constraintEndTime() ); for (int i=0; !match && i <= nDays; ++i) { // days end = end.addDays(inc); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); //debugPlan<<"["<logDebug( "Days: duration " + logtime.toString() + " - " + end.toString() + " = " + l.toString() + " (" + (duration - l).toString() + ')' ); #endif logtime = start; for (int i=0; !match && i < 24; ++i) { // hours end = end.addSecs(inc*60*60); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { end = start; break; } //debugPlan<<"duration(h)["<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(s)["<logDebug( "Seconds: duration " + logtime.toString() + " - " + end.toString() + " l " + l.toString() + " (" + (duration - l).toString() + ')' ); #endif for (int i=0; !match && i < 1000; ++i) { //milliseconds end.setTime(end.time().addMSecs(inc)); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Got more than asked for, should not happen! Want: " + duration.toString(Duration::Format_Hour) + " got: " + l.toString(Duration::Format_Hour) ); #endif break; } //debugPlan<<"duration(ms)["<logError( i18n( "Could not match work duration. Want: %1 got: %2", l.toString( Duration::Format_i18nHour ), duration.toString( Duration::Format_i18nHour ) ) ); } DateTime t = end; if (l != Duration::zeroDuration) { if ( backward ) { if ( end < projectNode()->constraintEndTime() ) { t = cal->firstAvailableAfter(end, projectNode()->constraintEndTime()); } } else { if ( end > projectNode()->constraintStartTime() ) { t = cal->firstAvailableBefore(end, projectNode()->constraintStartTime()); } } #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Moved end to work: " + end.toString() + " -> " + t.toString() ); #endif } end = t.isValid() ? t : time; //debugPlan<<"<---"<<(backward?"(B)":"(F)")<time ? end-time : time-end; if ( match ) { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculated length: " + time.toString() + " - " + end.toString() + " = " + l.toString() ); #endif } return l; } void Task::clearProxyRelations() { m_parentProxyRelations.clear(); m_childProxyRelations.clear(); } void Task::addParentProxyRelations( const QList &list ) { //debugPlan<addParentProxyRelations(list); n->addParentProxyRelations(dependParentNodes()); } } else { // add 'this' as child relation to the relations parent //debugPlan<parent()->addChildProxyRelation(this, r); // add a parent relation to myself addParentProxyRelation(r->parent(), r); } } } void Task::addChildProxyRelations( const QList &list) { //debugPlan<addChildProxyRelations(list); n->addChildProxyRelations(dependChildNodes()); } } else { // add 'this' as parent relation to the relations child //debugPlan<child()->addParentProxyRelation(this, r); // add a child relation to myself addChildProxyRelation(r->child(), r); } } } void Task::addParentProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add parent proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addParentProxyRelation(node, rel); } } else { //debugPlan<<"Add parent proxy from"<name()<<" to (me)"<type(), rel->lag())); } } } void Task::addChildProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add child proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addChildProxyRelation(node, rel); } } else { //debugPlan<<"Add child proxy from (me)"<name(); m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag())); } } } bool Task::isEndNode() const { return m_dependChildNodes.isEmpty() && m_childProxyRelations.isEmpty(); } bool Task::isStartNode() const { return m_dependParentNodes.isEmpty() && m_parentProxyRelations.isEmpty(); } DateTime Task::workTimeAfter(const DateTime &dt, Schedule *sch) const { DateTime t; if ( m_estimate->type() == Estimate::Type_Duration ) { if ( m_estimate->calendar() ) { t = m_estimate->calendar()->firstAvailableAfter( dt, projectNode()->constraintEndTime() ); } } else { t = m_requests.workTimeAfter(dt, sch); #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( QStringLiteral( "workTimeAfter: %1 = %2" ).arg( dt.toString() ).arg( t.toString() ) ); #endif } return t.isValid() ? t : dt; } DateTime Task::workTimeBefore(const DateTime &dt, Schedule *sch) const { DateTime t; if ( m_estimate->type() == Estimate::Type_Duration ) { if ( m_estimate->calendar() ) { t = m_estimate->calendar()->firstAvailableBefore( dt, projectNode()->constraintStartTime() ); } } else { t = m_requests.workTimeBefore(dt, sch); } return t.isValid() ? t : dt; } Duration Task::positiveFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->positiveFloat; } void Task::setPositiveFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->positiveFloat = fl; } Duration Task::negativeFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->negativeFloat; } void Task::setNegativeFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->negativeFloat = fl; } Duration Task::freeFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->freeFloat; } void Task::setFreeFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->freeFloat = fl; } Duration Task::startFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 || s->earlyStart > s->lateStart ? Duration::zeroDuration : ( s->earlyStart - s->lateStart ); } Duration Task::finishFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 || s->lateFinish < s->earlyFinish ? Duration::zeroDuration : ( s->lateFinish - s->earlyFinish ); } bool Task::isCritical( long id ) const { Schedule *s = schedule( id ); return s == 0 ? false : s->isCritical(); } bool Task::calcCriticalPath(bool fromEnd) { if (m_currentSchedule == 0) return false; //debugPlan<child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependChildNodes) { if (r->child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } else { if (isStartNode() && startFloat() == 0 && finishFloat() == 0) { m_currentSchedule->inCriticalPath = true; //debugPlan<parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependParentNodes) { if (r->parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } //debugPlan<freeFloat.toString(); } } void Task::setCurrentSchedule(long id) { setCurrentSchedulePtr(findSchedule(id)); Node::setCurrentSchedule(id); } bool Task::effortMetError( long id ) const { Schedule *s = schedule( id ); if (s == 0 || s->notScheduled || m_estimate->type() != Estimate::Type_Effort) { return false; } return s->effortNotMet; } uint Task::state( long id ) const { int st = Node::State_None; if ( ! isScheduled( id ) ) { st |= State_NotScheduled; } if ( completion().isFinished() ) { st |= Node::State_Finished; if ( completion().finishTime() > endTime( id ) ) { st |= State_FinishedLate; } if ( completion().finishTime() < endTime( id ) ) { st |= State_FinishedEarly; } } else if ( completion().isStarted() ) { st |= Node::State_Started; if ( completion().startTime() > startTime( id ) ) { st |= State_StartedLate; } if ( completion().startTime() < startTime( id ) ) { st |= State_StartedEarly; } if ( completion().percentFinished() > 0 ) { st |= State_Running; } if ( endTime( id ) < QDateTime::currentDateTime() ) { st |= State_Late; } } else if ( isScheduled( id ) ) { if ( startTime( id ) < QDateTime::currentDateTime() ) { st |= State_Late; } } st |= State_ReadyToStart; //TODO: check proxy relations foreach ( const Relation *r, m_dependParentNodes ) { if ( ! static_cast( r->parent() )->completion().isFinished() ) { st &= ~Node::State_ReadyToStart; st |= Node::State_NotReadyToStart; break; } } return st; } void Task::addWorkPackage( WorkPackage *wp ) { emit workPackageToBeAdded( this, m_packageLog.count() ); m_packageLog.append( wp ); emit workPackageAdded( this ); } void Task::removeWorkPackage( WorkPackage *wp ) { int index = m_packageLog.indexOf( wp ); if ( index < 0 ) { return; } emit workPackageToBeRemoved( this, index ); m_packageLog.removeAt( index ); emit workPackageRemoved( this ); } WorkPackage *Task::workPackageAt( int index ) const { Q_ASSERT ( index >= 0 && index < m_packageLog.count() ); return m_packageLog.at( index ); } QString Task::wpOwnerName() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.ownerName(); } return m_packageLog.last()->ownerName(); } WorkPackage::WPTransmitionStatus Task::wpTransmitionStatus() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.transmitionStatus(); } return m_packageLog.last()->transmitionStatus(); } DateTime Task::wpTransmitionTime() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.transmitionTime(); } return m_packageLog.last()->transmitionTime(); } //------------------------------------------ Completion::Completion( Node *node ) : m_node( node ), m_started( false ), m_finished( false ), - m_entrymode( EnterCompleted ) + m_entrymode( EnterEffortPerResource ) {} Completion::Completion( const Completion &c ) { copy( c ); } Completion::~Completion() { qDeleteAll( m_entries ); qDeleteAll( m_usedEffort ); } void Completion::copy( const Completion &p ) { m_node = 0; //NOTE m_started = p.isStarted(); m_finished = p.isFinished(); m_startTime = p.startTime(); m_finishTime = p.finishTime(); m_entrymode = p.entrymode(); qDeleteAll( m_entries ); m_entries.clear(); Completion::EntryList::ConstIterator entriesIt = p.entries().constBegin(); const Completion::EntryList::ConstIterator entriesEnd = p.entries().constEnd(); for (; entriesIt != entriesEnd; ++entriesIt) { addEntry( entriesIt.key(), new Entry( *entriesIt.value() ) ); } qDeleteAll( m_usedEffort ); m_usedEffort.clear(); Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapIt = p.usedEffortMap().constBegin(); const Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapEnd = p.usedEffortMap().constEnd(); for (; usedEffortMapIt != usedEffortMapEnd; ++usedEffortMapIt) { addUsedEffort( usedEffortMapIt.key(), new UsedEffort( *usedEffortMapIt.value() ) ); } } bool Completion::operator==( const Completion &p ) { return m_started == p.isStarted() && m_finished == p.isFinished() && m_startTime == p.startTime() && m_finishTime == p.finishTime() && m_entries == p.entries() && m_usedEffort == p.usedEffortMap(); } Completion &Completion::operator=( const Completion &p ) { copy( p ); return *this; } void Completion::changed( int property) { if ( m_node ) { m_node->changed(property); } } void Completion::setStarted( bool on ) { m_started = on; changed(Node::CompletionStarted); } void Completion::setFinished( bool on ) { m_finished = on; changed(Node::CompletionFinished); } void Completion::setStartTime( const DateTime &dt ) { m_startTime = dt; changed(Node::CompletionStartTime); } void Completion::setFinishTime( const DateTime &dt ) { m_finishTime = dt; changed(Node::CompletionFinishTime); } void Completion::setPercentFinished( QDate date, int value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->percentFinished = value; changed(Node::CompletionPercentage); } void Completion::setRemainingEffort( QDate date, KPlato::Duration value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->remainingEffort = value; changed(Node::CompletionRemainingEffort); } void Completion::setActualEffort( QDate date, KPlato::Duration value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->totalPerformed = value; changed(Node::CompletionActualEffort); } void Completion::addEntry( QDate date, Entry *entry ) { m_entries.insert( date, entry ); //debugPlan<percentFinished; } int Completion::percentFinished( QDate date ) const { int x = 0; EntryList::const_iterator it; for (it = m_entries.constBegin(); it != m_entries.constEnd() && it.key() <= date; ++it) { x = it.value()->percentFinished; } return x; } Duration Completion::remainingEffort() const { return m_entries.isEmpty() ? Duration::zeroDuration : m_entries.last()->remainingEffort; } Duration Completion::remainingEffort( QDate date ) const { Duration x; EntryList::const_iterator it; for (it = m_entries.constBegin(); it != m_entries.constEnd() && it.key() <= date; ++it) { x = it.value()->remainingEffort; } return x; } Duration Completion::actualEffort() const { Duration eff; if ( m_entrymode == EnterEffortPerResource ) { foreach( const UsedEffort *ue, m_usedEffort ) { const QMap map = ue->actualEffortMap(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { eff += it.value().effort(); } } } else if ( ! m_entries.isEmpty() ) { eff = m_entries.last()->totalPerformed; } return eff; } Duration Completion::actualEffort( const Resource *resource, QDate date ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return Duration::zeroDuration; } UsedEffort::ActualEffort ae = ue->effort( date ); return ae.effort(); } Duration Completion::actualEffort( QDate date ) const { Duration eff; if ( m_entrymode == EnterEffortPerResource ) { foreach( const UsedEffort *ue, m_usedEffort ) { if ( ue && ue->actualEffortMap().contains( date ) ) { eff += ue->actualEffortMap().value( date ).effort(); } } } else { // Hmmm: How to really know a specific date? if ( m_entries.contains( date ) ) { eff = m_entries[ date ]->totalPerformed; } } return eff; } Duration Completion::actualEffortTo( QDate date ) const { //debugPlan<effortTo( date ); } } else { QListIterator it( m_entries.uniqueKeys() ); it.toBack(); while ( it.hasPrevious() ) { QDate d = it.previous(); if ( d <= date ) { eff = m_entries[ d ]->totalPerformed; break; } } } return eff; } double Completion::averageCostPrHour( QDate date, long id ) const { Schedule *s = m_node->schedule( id ); if ( s == 0 ) { return 0.0; } double cost = 0.0; double eff = 0.0; QList cl; foreach ( const Appointment *a, s->appointments() ) { cl << a->resource()->resource()->normalRate(); double e = a->plannedEffort( date ).toDouble( Duration::Unit_h ); if ( e > 0.0 ) { eff += e; cost += e * cl.last(); } } if ( eff > 0.0 ) { cost /= eff; } else { foreach ( double c, cl ) { cost += c; } cost /= cl.count(); } return cost; } EffortCostMap Completion::effortCostPrDay(QDate start, QDate end, long id ) const { //debugPlan<name()< et ) { break; } Duration e = m_entries[ d ]->totalPerformed; if ( e != Duration::zeroDuration && e != last ) { Duration eff = e - last; ec.insert( d, eff, eff.toDouble( Duration::Unit_h ) * averageCostPrHour( d, id ) ); last = e; } } break; } case EnterEffortPerResource: { std::pair dates = actualStartEndDates(); if ( ! dates.first.isValid() ) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for ( QDate d = st; d <= et; d = d.addDays( 1 ) ) { ec.add( d, actualEffort( d ), actualCost( d ) ); } break; } } return ec; } EffortCostMap Completion::effortCostPrDay(const Resource *resource, QDate start, QDate end, long id ) const { Q_UNUSED(id); //debugPlan<name()< dates = actualStartEndDates(); if ( ! dates.first.isValid() ) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for ( QDate d = st; d <= et; d = d.addDays( 1 ) ) { ec.add( d, actualEffort( resource, d ), actualCost( resource, d ) ); } break; } } return ec; } void Completion::addUsedEffort( const Resource *resource, Completion::UsedEffort *value ) { UsedEffort *v = value == 0 ? new UsedEffort() : value; if ( m_usedEffort.contains( resource ) ) { m_usedEffort[ resource ]->mergeEffort( *v ); delete v; } else { m_usedEffort.insert( resource, v ); } changed(Node::CompletionUsedEffort); } QString Completion::note() const { return m_entries.isEmpty() ? QString() : m_entries.last()->note; } void Completion::setNote( const QString &str ) { if ( ! m_entries.isEmpty() ) { m_entries.last()->note = str; changed(); } } std::pair Completion::actualStartEndDates() const { std::pair p; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { if (!it.value()->actualEffortMap().isEmpty()) { QDate d = it.value()->firstDate(); if (!p.first.isValid() || d < p.first) { p.first = d; } d = it.value()->lastDate(); if (!p.second.isValid() || d > p.second ) { p.second = d; } } } return p; } double Completion::actualCost( QDate date ) const { //debugPlan<normalRate(); double oc = it.key()->overtimeRate(); if (it.value()->actualEffortMap().contains(date)) { UsedEffort::ActualEffort a = it.value()->effort(date); c += a.normalEffort().toDouble( Duration::Unit_h ) * nc; c += a.overtimeEffort().toDouble( Duration::Unit_h ) * oc; } } return c; } double Completion::actualCost( const Resource *resource ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return 0.0; } double c = 0.0; double nc = resource->normalRate(); double oc = resource->overtimeRate(); foreach ( const UsedEffort::ActualEffort &a, ue->actualEffortMap() ) { c += a.normalEffort().toDouble( Duration::Unit_h ) * nc; c += a.overtimeEffort().toDouble( Duration::Unit_h ) * oc; } return c; } double Completion::actualCost() const { double c = 0.0; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { c += actualCost(it.key()); } return c; } double Completion::actualCost( const Resource *resource, QDate date ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return 0.0; } UsedEffort::ActualEffort a = ue->actualEffortMap().value( date ); double c = a.normalEffort().toDouble( Duration::Unit_h ) * resource->normalRate(); c += a.overtimeEffort().toDouble( Duration::Unit_h ) * resource->overtimeRate(); return c; } EffortCostMap Completion::actualEffortCost( long int id, KPlato::EffortCostCalculationType type ) const { //debugPlan; EffortCostMap map; if ( ! isStarted() ) { return map; } QList< QMap > lst; QList< double > rate; QDate start, end; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { const Resource *r = it.key(); //debugPlan<name()<name(); lst << usedEffort(r)->actualEffortMap(); if ( lst.last().isEmpty() ) { lst.takeLast(); continue; } if ( r->type() == Resource::Type_Material ) { if ( type == ECCT_All ) { rate.append( r->normalRate() ); } else if ( type == ECCT_EffortWork ) { rate.append( 0.0 ); } else { lst.takeLast(); continue; } } else { rate.append( r->normalRate() ); } if ( ! start.isValid() || start > lst.last().firstKey() ) { start = lst.last().firstKey(); } if ( ! end.isValid() || end < lst.last().lastKey() ) { end = lst.last().lastKey(); } } if ( ! lst.isEmpty() && start.isValid() && end.isValid() ) { for ( QDate d = start; d <= end; d = d.addDays( 1 ) ) { EffortCost c; for ( int i = 0; i < lst.count(); ++i ) { UsedEffort::ActualEffort a = lst.at( i ).value( d ); double nc = rate.value( i ); Duration eff = a.normalEffort(); double cost = eff.toDouble( Duration::Unit_h ) * nc; c.add( eff, cost ); } if ( c.effort() != Duration::zeroDuration || c.cost() != 0.0 ) { map.add( d, c ); } } } else if ( ! m_entries.isEmpty() ) { QDate st = start.isValid() ? start : m_startTime.date(); QDate et = end.isValid() ? end : m_finishTime.date(); Duration last; foreach ( const QDate &d, m_entries.uniqueKeys() ) { if ( d < st ) { continue; } Duration e = m_entries[ d ]->totalPerformed; if ( e != Duration::zeroDuration && e != last ) { //debugPlan<name()< et ) { break; } } } return map; } EffortCost Completion::actualCostTo( long int id, QDate date ) const { //debugPlan<( m ); } QString Completion::entryModeToString() const { return entrymodeList().value( m_entrymode ); } bool Completion::loadXML( KoXmlElement &element, XMLLoaderObject &status ) { //debugPlan; QString s; m_started = (bool)element.attribute(QStringLiteral("started"), QStringLiteral("0")).toInt(); m_finished = (bool)element.attribute(QStringLiteral("finished"), QStringLiteral("0")).toInt(); s = element.attribute(QStringLiteral("startTime")); if (!s.isEmpty()) { m_startTime = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute(QStringLiteral("finishTime")); if (!s.isEmpty()) { m_finishTime = DateTime::fromString(s, status.projectTimeZone()); } setEntrymode( element.attribute( QStringLiteral("entrymode") ) ); if (status.version() < QLatin1String("0.6")) { if ( m_started ) { Entry *entry = new Entry( element.attribute(QStringLiteral("percent-finished"), QStringLiteral("0")).toInt(), Duration::fromString(element.attribute(QStringLiteral("remaining-effort"))), Duration::fromString(element.attribute(QStringLiteral("performed-effort"))) ); entry->note = element.attribute(QStringLiteral("note")); QDate date = m_startTime.date(); if ( m_finished ) { date = m_finishTime.date(); } // almost the best we can do ;) addEntry( date, entry ); } } else { KoXmlElement e; forEachElement(e, element) { if (e.tagName() == QLatin1String("completion-entry")) { QDate date; s = e.attribute(QStringLiteral("date")); if ( !s.isEmpty() ) { date = QDate::fromString( s, Qt::ISODate ); } if ( !date.isValid() ) { warnPlan<<"Invalid date: "<percentFinished ); elm.setAttribute( QStringLiteral("remaining-effort"), e->remainingEffort.toString() ); elm.setAttribute( QStringLiteral("performed-effort"), e->totalPerformed.toString() ); elm.setAttribute( QStringLiteral("note"), e->note ); } if ( ! m_usedEffort.isEmpty() ) { QDomElement elm = el.ownerDocument().createElement(QStringLiteral("used-effort")); el.appendChild(elm); ResourceUsedEffortMap::ConstIterator i = m_usedEffort.constBegin(); for ( ; i != m_usedEffort.constEnd(); ++i ) { if ( i.value() == 0 ) { continue; } QDomElement e = elm.ownerDocument().createElement(QStringLiteral("resource")); elm.appendChild(e); e.setAttribute( QStringLiteral("id"), i.key()->id() ); i.value()->saveXML( e ); } } } //-------------- Completion::UsedEffort::UsedEffort() { } Completion::UsedEffort::UsedEffort( const UsedEffort &e ) { mergeEffort( e ); } Completion::UsedEffort::~UsedEffort() { } void Completion::UsedEffort::mergeEffort( const Completion::UsedEffort &value ) { const QMap map = value.actualEffortMap(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { setEffort(it.key(), it.value()); } } void Completion::UsedEffort::setEffort( QDate date, const ActualEffort &value ) { m_actual.insert( date, value ); } Duration Completion::UsedEffort::effortTo( QDate date ) const { Duration eff; QMap::const_iterator it; for (it = m_actual.constBegin(); it != m_actual.constEnd() && it.key() <= date; ++it) { eff += it.value().effort(); } return eff; } Duration Completion::UsedEffort::effort() const { Duration eff; foreach ( const ActualEffort &e, m_actual ) { eff += e.effort(); } return eff; } bool Completion::UsedEffort::operator==( const Completion::UsedEffort &e ) const { return m_actual == e.actualEffortMap(); } bool Completion::UsedEffort::loadXML(KoXmlElement &element, XMLLoaderObject & ) { //debugPlan; KoXmlElement e; forEachElement(e, element) { if (e.tagName() == QLatin1String("actual-effort")) { QDate date = QDate::fromString( e.attribute(QStringLiteral("date")), Qt::ISODate ); if ( date.isValid() ) { ActualEffort a; a.setNormalEffort( Duration::fromString( e.attribute( QStringLiteral("normal-effort") ) ) ); a.setOvertimeEffort( Duration::fromString( e.attribute( QStringLiteral("overtime-effort") ) ) ); setEffort( date, a ); } } } return true; } void Completion::UsedEffort::saveXML(QDomElement &element ) const { if ( m_actual.isEmpty() ) { return; } DateUsedEffortMap::ConstIterator i = m_actual.constBegin(); for ( ; i != m_actual.constEnd(); ++i ) { QDomElement el = element.ownerDocument().createElement(QStringLiteral("actual-effort")); element.appendChild( el ); el.setAttribute( QStringLiteral("overtime-effort"), i.value().overtimeEffort().toString() ); el.setAttribute( QStringLiteral("normal-effort"), i.value().normalEffort().toString() ); el.setAttribute( QStringLiteral("date"), i.key().toString( Qt::ISODate ) ); } } //---------------------------------- WorkPackage::WorkPackage( Task *task ) : m_task( task ), m_manager( 0 ), m_transmitionStatus( TS_None ) { m_completion.setNode( task ); } WorkPackage::WorkPackage( const WorkPackage &wp ) : m_task( 0 ), m_manager( 0 ), m_completion( wp.m_completion ), m_ownerName( wp.m_ownerName ), m_ownerId( wp.m_ownerId ), m_transmitionStatus( wp.m_transmitionStatus ), m_transmitionTime( wp.m_transmitionTime ) { } WorkPackage::~WorkPackage() { } bool WorkPackage::loadXML(KoXmlElement &element, XMLLoaderObject &status ) { Q_UNUSED(status); m_ownerName = element.attribute( QStringLiteral("owner") ); m_ownerId = element.attribute( QStringLiteral("owner-id") ); return true; } void WorkPackage::saveXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); el.setAttribute( QStringLiteral("owner"), m_ownerName ); el.setAttribute( QStringLiteral("owner-id"), m_ownerId ); } bool WorkPackage::loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status ) { m_ownerName = element.attribute( QStringLiteral("owner") ); m_ownerId = element.attribute( QStringLiteral("owner-id") ); m_transmitionStatus = transmitionStatusFromString( element.attribute( QStringLiteral("status") ) ); m_transmitionTime = DateTime( QDateTime::fromString( element.attribute( QStringLiteral("time") ), Qt::ISODate ) ); return m_completion.loadXML( element, status ); } void WorkPackage::saveLoggedXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); el.setAttribute( QStringLiteral("owner"), m_ownerName ); el.setAttribute( QStringLiteral("owner-id"), m_ownerId ); el.setAttribute( QStringLiteral("status"), transmitionStatusToString( m_transmitionStatus ) ); el.setAttribute( QStringLiteral("time"), m_transmitionTime.toString( Qt::ISODate ) ); m_completion.saveXML( el ); } QList WorkPackage::fetchResources() { return fetchResources( id() ); } QList WorkPackage::fetchResources( long id ) { //debugPlan< lst; if ( id == NOTSCHEDULED ) { if ( m_task ) { lst << m_task->requestedResources(); } } else { if ( m_task ) lst = m_task->assignedResources( id ); foreach ( const Resource *r, m_completion.resources() ) { if ( ! lst.contains( const_cast( r ) ) ) { lst << const_cast( r ); } } } return lst; } Completion &WorkPackage::completion() { return m_completion; } const Completion &WorkPackage::completion() const { return m_completion; } void WorkPackage::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } QString WorkPackage::transmitionStatusToString( WorkPackage::WPTransmitionStatus sts, bool trans ) { QString s = trans ? i18n( "None" ) : QStringLiteral("None"); switch ( sts ) { case TS_Send: s = trans ? i18n( "Send" ) : QStringLiteral("Send"); break; case TS_Receive: s = trans ? i18n( "Receive" ) : QStringLiteral("Receive"); break; default: break; } return s; } WorkPackage::WPTransmitionStatus WorkPackage::transmitionStatusFromString( const QString &sts ) { QStringList lst; lst << QStringLiteral("None") << QStringLiteral("Send") << QStringLiteral("Receive"); int s = lst.indexOf( sts ); return s < 0 ? TS_None : static_cast( s ); } void WorkPackage::clear() { //m_task = 0; m_manager = 0; m_ownerName.clear(); m_ownerId.clear(); m_transmitionStatus = TS_None; m_transmitionTime = DateTime(); m_log.clear(); m_completion = Completion(); m_completion.setNode( m_task ); } //-------------------------------- WorkPackageSettings::WorkPackageSettings() : usedEffort( true ), progress( true ), documents( true ) { } void WorkPackageSettings::saveXML( QDomElement &element ) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("settings")); element.appendChild( el ); el.setAttribute( QStringLiteral("used-effort"), QString::number(usedEffort) ); el.setAttribute( QStringLiteral("progress"), QString::number(progress) ); el.setAttribute( QStringLiteral("documents"), QString::number(documents) ); } bool WorkPackageSettings::loadXML( const KoXmlElement &element ) { usedEffort = (bool)element.attribute( QStringLiteral("used-effort") ).toInt(); progress = (bool)element.attribute( QStringLiteral("progress") ).toInt(); documents = (bool)element.attribute( QStringLiteral("documents") ).toInt(); return true; } bool WorkPackageSettings::operator==( KPlato::WorkPackageSettings s ) const { return usedEffort == s.usedEffort && progress == s.progress && documents == s.documents; } bool WorkPackageSettings::operator!=( KPlato::WorkPackageSettings s ) const { return ! operator==( s ); } } //KPlato namespace #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae ) { dbg << QStringLiteral( "%1" ).arg( ae.normalEffort().toDouble( KPlato::Duration::Unit_h ), 1 ); return dbg; } #endif diff --git a/src/libs/models/kptnodeitemmodel.cpp b/src/libs/models/kptnodeitemmodel.cpp index 729f496a..98e5c89f 100644 --- a/src/libs/models/kptnodeitemmodel.cpp +++ b/src/libs/models/kptnodeitemmodel.cpp @@ -1,5218 +1,5218 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2009, 2012 Dag Andersen Copyright (C) 2016 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 "kptnodeitemmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptduration.h" #include "kptproject.h" #include "kptnode.h" #include "kpttaskcompletedelegate.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "InsertProjectXmlCommand.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------------------------- NodeModel::NodeModel() : QObject(), m_project( 0 ), m_manager( 0 ), m_now( QDate::currentDate() ), m_prec( 1 ) { } const QMetaEnum NodeModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } void NodeModel::setProject( Project *project ) { debugPlan<"<"<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::DecorationRole: if ( node->isBaselined() ) { return koIcon("view-time-schedule-baselined"); } break; case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return static_cast( node )->completion().isFinished() ? m_project->config().taskFinishedColor() : m_project->config().taskNormalColor(); case Node::Type_Milestone: return static_cast( node )->completion().isFinished() ? m_project->config().milestoneFinishedColor() : m_project->config().milestoneNormalColor(); case Node::Type_Summarytask: return m_project->config().summaryTaskLevelColor( node->level() ); default: break; } break; } } return QVariant(); } QVariant NodeModel::leader( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return node->leader(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::allocation( const Node *node, int role ) const { if ( node->type() == Node::Type_Task ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->requests().requestNameList().join( "," ); case Qt::ToolTipRole: { QMap lst; foreach ( ResourceRequest *rr, node->requests().resourceRequests( false ) ) { QStringList sl; foreach( Resource *r, rr->requiredResources() ) { sl << r->name(); } lst.insert( rr->resource()->name(), sl ); } if ( lst.isEmpty() ) { return xi18nc( "@info:tooltip", "No resources has been allocated" ); } QStringList sl; for ( QMap::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) { if ( it.value().isEmpty() ) { sl << it.key(); } else { sl << xi18nc( "@info:tooltip 1=resource name, 2=list of required resources", "%1 (%2)", it.key(), it.value().join(", ") ); } } if ( sl.count() == 1 ) { return xi18nc( "@info:tooltip 1=resource name", "Allocated resource:%1", sl.first() ); } KLocalizedString ks = kxi18nc( "@info:tooltip 1=list of resources", "Allocated resources:%1"); // Hack to get around ks escaping '<' and '>' QString s = ks.subs(sl.join("#¤#")).toString(); return s.replace("#¤#", "
"); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::description( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); QString s = w.textOrHtml(); int i = s.indexOf( '\n' ); s = s.left( i ); if ( i > 0 ) { s += "..."; } return s; } case Qt::ToolTipRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); if ( w.textOrHtml().isEmpty() ) { return QVariant(); } return node->description(); } case Qt::EditRole: return node->description(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::type( const Node *node, int role ) const { //debugPlan<name()<<", "<typeToString( true ); case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return (int)(Qt::AlignLeft|Qt::AlignVCenter); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::constraint( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: return i18n( "Target times" ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Earliest start and latest finish" ); case Role::EnumList: case Qt::EditRole: case Role::EnumListValue: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return node->constraintToString( true ); case Role::EnumList: return Node::constraintList( true ); case Qt::EditRole: return node->constraint(); case Role::EnumListValue: return (int)node->constraint(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintStartTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::StartNotEarlier: case Node::MustStartOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintEndTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::FinishNotLater: case Node::MustFinishOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::FinishNotLater || c == Node::MustFinishOn || c == Node::FixedInterval ) { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::estimateType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return QString(); case Role::EnumList: return Estimate::typeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->type(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimateCalendar( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return i18n( "None" ); } return QString(); case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->type() == Estimate::Type_Effort ) { return xi18nc( "@info:tooltip", "Not applicable, estimate type is Effort" ); } if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return QVariant(); } return QString(); case Role::EnumList: { QStringList lst; lst << i18n( "None" ); const Node *n = const_cast( node )->projectNode(); if ( n ) { lst += static_cast( n )->calendarNames(); } return lst; } case Qt::EditRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() == 0 ) { return i18n( "None" ); } return node->estimate()->calendar()->name(); } return QString(); case Role::EnumListValue: { if ( node->estimate()->calendar() == 0 ) { return 0; } QStringList lst; const Node *n = const_cast( node )->projectNode(); if ( n ) { lst = static_cast( n )->calendarNames(); } return lst.indexOf( node->estimate()->calendar()->name() ) + 1; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimate( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); if ( node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { s = '(' + s + ')'; } return s; } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Estimated effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Estimated duration: %1", s ); } return s; } break; case Qt::EditRole: return node->estimate()->expectedEstimate(); case Role::DurationUnit: return static_cast( node->estimate()->unit() ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->optimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Optimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Optimistic duration: %1", s ); } return s; } break; case Role::Minimum: return -99; case Role::Maximum: return 0; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->pessimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Pessimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Pessimistic duration: %1", s ); } return s; } break; case Role::Minimum: return 0; case Role::Maximum: return INT_MAX; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::riskType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString( true ); } return QString(); case Role::EnumList: return Estimate::risktypeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->risktype(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::runningAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a ? xi18nc( "@info:tooltip", "Account for resource cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for resource cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->runningAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name() ) : xi18nc( "@info:tooltip", "Account for task startup cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->startupAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->startupCost() ); } break; case Qt::EditRole: return node->startupCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a ? xi18nc( "@info:tooltip", "Account for task shutdown cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for task shutdown cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->shutdownAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->shutdownCost() ); } break; case Qt::EditRole: return node->shutdownCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->startTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<startTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->startTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::endTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->endTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<endTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->endTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::duration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } break; case Qt::EditRole: { return node->duration( id() ).toDouble( Duration::Unit_h ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return QLocale().toString( v, 'f', 2 ); } break; case Qt::EditRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); return node->variance( id(), unit ); } return 0.0; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return xi18nc( "@info:tooltip", "PERT duration variance: %1", QLocale().toString( v ,'f', 2 ) ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceEstimate( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); //debugPlan<name()<<": "<variance( est->unit() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); return xi18nc( "@info:tooltip", "PERT estimate variance: %1", QLocale().toString( v, 'f', 2 ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); return d.toDouble( unit ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->optimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Optimistic estimate: %1", QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pertExpected( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return Estimate::scale( est->pertExpected(), est->unit(), est->scales() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return xi18nc( "@info:tooltip", "PERT expected estimate: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; return d.toDouble( node->estimate()->unit() ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->pessimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Pessimistic estimate: %1", QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::positiveFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->positiveFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::freeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->freeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->freeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->freeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::negativeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->negativeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->startFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->startFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->startFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->finishFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->finishFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->finishFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::assignedResources( const Node *node, int role ) const { if ( node->type() != Node::Type_Task ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->assignedNameList( id() ).join(","); case Qt::ToolTipRole: { QStringList lst = node->assignedNameList( id() ); if ( ! lst.isEmpty() ) { return xi18nc( "@info:tooltip 1=list of resources", "Assigned resources:%1", node->assignedNameList( id() ).join("") ); } break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::completed( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->completion().percentFinished(); case Qt::EditRole: return t->completion().percentFinished(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task is %1% completed", t->completion().percentFinished() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::status( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { return i18n( "Finished late" ); } if ( st & Node::State_FinishedEarly ) { return i18n( "Finished early" ); } return i18n( "Finished" ); } if ( st & Node::State_Running ) { if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Running" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { return i18n( "Started late" ); } if ( st & Node::State_StartedEarly ) { return i18n( "Started early" ); } if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Started" ); } if ( st & Node::State_ReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Not started" ); } return i18n( "Can start" ); } if ( st & Node::State_NotReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Delayed" ); } return i18n( "Cannot start" ); } return i18n( "Not started" ); break; } case Qt::ToolTipRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { Duration d = t->completion().finishTime() - t->endTime( id() ); return xi18nc( "@info:tooltip", "Finished %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_FinishedEarly ) { Duration d = t->endTime( id() ) - t->completion().finishTime(); return xi18nc( "@info:tooltip", "Finished %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Finished" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { Duration d = t->completion().startTime() - t->startTime( id() ); return xi18nc( "@info:tooltip", "Started %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_StartedEarly ) { Duration d = t->startTime( id() ) - t->completion().startTime(); return xi18nc( "@info:tooltip", "Started %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Started" ); } if ( st & Node::State_Running ) { return xi18nc( "@info:tooltip", "Running" ); } if ( st & Node::State_ReadyToStart ) { return xi18nc( "@info:tooltip", "Can start" ); } if ( st & Node::State_NotReadyToStart ) { QStringList names; // TODO: proxy relations foreach ( Relation *r, node->dependParentNodes() ) { switch ( r->type() ) { case Relation::FinishFinish: case Relation::FinishStart: if ( ! static_cast( r->parent() )->completion().isFinished() ) { if ( ! names.contains( r->parent()->name() ) ) { names << r->parent()->name(); } } break; case Relation::StartStart: if ( ! static_cast( r->parent() )->completion().isStarted() ) { if ( ! names.contains( r->parent()->name() ) ) { names << r->parent()->name(); } } break; } } return names.isEmpty() ? xi18nc( "@info:tooltip", "Cannot start" ) : xi18nc( "@info:tooltip 1=list of task names", "Cannot start, waiting for:%1", names.join( "" ) ); } return xi18nc( "@info:tooltip", "Not started" ); break; } case Qt::EditRole: return t->state( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isStarted() ) { return QLocale().toString( t->completion().startTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "Actual start: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isStarted() ) { return t->completion().startTime(); } return QDateTime::currentDateTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isStarted( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isStarted(); case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "The task started at: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not started" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isFinished() ) { return QLocale().toString( t->completion().finishTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "Actual finish: %1", QLocale().toString( t->completion().finishTime(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isFinished() ) { return t->completion().finishTime(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isFinished( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isFinished(); case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "The task finished at: %1", QLocale().toString( t->completion().finishTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not finished" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->plannedEffortTo( m_now, id() ).format(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned effort until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), node->plannedEffortTo( m_now, id() ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->plannedEffortTo( m_now, id() ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->actualEffortTo( m_now ).format(); case Qt::ToolTipRole: //debugPlan<actualEffortTo( m_now ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->actualEffortTo( m_now ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::remainingEffort( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t ) { return t->completion().remainingEffort().format(); } break; } case Qt::ToolTipRole: { const Task *t = dynamic_cast( node ); if ( t ) { return xi18nc( "@info:tooltip", "Remaining effort: %1", t->completion().remainingEffort().toString( Duration::Format_i18nHour ) ); } break; } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return t->completion().remainingEffort().toDouble( Duration::Unit_h ); } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->plannedCostTo( m_now, id() ) ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->plannedCostTo( m_now, id() ) ) ); case Qt::EditRole: return node->plannedCostTo( m_now ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->actualCostTo( id(), m_now ).cost() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->actualCostTo( id(), m_now ).cost() ) ); case Qt::EditRole: return node->actualCostTo( id(), m_now ).cost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::note( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Node *n = const_cast( node ); return static_cast( n )->completion().note(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeSchedulingStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->schedulingStatus( id(), true ).value( 0 ); case Qt::EditRole: return node->schedulingStatus( id(), false ).value( 0 ); case Qt::ToolTipRole: return node->schedulingStatus( id(), true ).join( "\n" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::resourceIsMissing( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceError( id() ); case Qt::ToolTipRole: if ( node->resourceError( id() ) ) { return xi18nc( "@info:tooltip", "Resource allocation is expected when the task estimate type is Effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsOverbooked( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceOverbooked( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceOverbooked( id() ); case Qt::ToolTipRole: if ( node->resourceOverbooked( id() ) ) { return xi18nc( "@info:tooltip", "A resource has been overbooked" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsNotAvailable( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceNotAvailable( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceNotAvailable( id() ); case Qt::ToolTipRole: if ( node->resourceNotAvailable( id() ) ) { return xi18nc( "@info:tooltip", "No resource is available for this task" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingConstraintsError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->constraintError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->constraintError( id() ); case Qt::ToolTipRole: if ( node->constraintError( id() ) ) { return xi18nc( "@info:tooltip", "Failed to comply with a timing constraint" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeIsNotScheduled( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->notScheduled( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->notScheduled( id() ); case Qt::ToolTipRole: if ( node->notScheduled( id() ) ) { return xi18nc( "@info:tooltip", "This task has not been scheduled" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::effortNotMet( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->effortMetError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->effortMetError( id() ); case Qt::ToolTipRole: if ( node->effortMetError( id() ) ) { return xi18nc( "@info:tooltip", "The assigned resources cannot deliver the required estimated effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->schedulingError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->schedulingError( id() ); case Qt::ToolTipRole: if ( node->schedulingError( id() ) ) { return xi18nc( "@info:tooltip", "Scheduling error" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wbsCode( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->wbsCode(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Work breakdown structure code: %1", node->wbsCode() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case SortableRole: return node->wbsCode(true); } return QVariant(); } QVariant NodeModel::nodeLevel( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->level(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task level: %1", node->level() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWS( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ); case Qt::EditRole: return node->bcws( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Scheduled at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ); case Qt::EditRole: return node->bcwp( id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeACWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost(), QString(), 0 ); case Qt::EditRole: return node->acwp( m_now, id() ).cost(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost() ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodePerformanceIndex( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ); case Qt::EditRole: return node->schedulePerformanceIndex( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Schedule Performance Index at %1: %2", m_now.toString(), QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::ForegroundRole: return QColor(node->schedulePerformanceIndex( m_now, id() ) < 1.0 ? Qt::red : Qt::black); } return QVariant(); } QVariant NodeModel::nodeIsCritical( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->isCritical( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeInCriticalPath( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->inCriticalPath( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wpOwnerName( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return t->wpOwnerName(); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent to %1 at %2", static_cast( node )->wpOwnerName(), t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received from %1 at %2", static_cast( node )->wpOwnerName(), t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), true ); } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), false ); } case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return QLocale().toString( t->wpTransmitionTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent: %1", t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received: %1", t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::data( const Node *n, int property, int role ) const { QVariant result; switch ( property ) { // Edited by user case NodeName: result = name( n, role ); break; case NodeType: result = type( n, role ); break; case NodeResponsible: result = leader( n, role ); break; case NodeAllocation: result = allocation( n, role ); break; case NodeEstimateType: result = estimateType( n, role ); break; case NodeEstimateCalendar: result = estimateCalendar( n, role ); break; case NodeEstimate: result = estimate( n, role ); break; case NodeOptimisticRatio: result = optimisticRatio( n, role ); break; case NodePessimisticRatio: result = pessimisticRatio( n, role ); break; case NodeRisk: result = riskType( n, role ); break; case NodeConstraint: result = constraint( n, role ); break; case NodeConstraintStart: result = constraintStartTime( n, role ); break; case NodeConstraintEnd: result = constraintEndTime( n, role ); break; case NodeRunningAccount: result = runningAccount( n, role ); break; case NodeStartupAccount: result = startupAccount( n, role ); break; case NodeStartupCost: result = startupCost( n, role ); break; case NodeShutdownAccount: result = shutdownAccount( n, role ); break; case NodeShutdownCost: result = shutdownCost( n, role ); break; case NodeDescription: result = description( n, role ); break; // Based on edited values case NodeExpected: result = pertExpected( n->estimate(), role ); break; case NodeVarianceEstimate: result = varianceEstimate( n->estimate(), role ); break; case NodeOptimistic: result = optimisticEstimate( n->estimate(), role ); break; case NodePessimistic: result = pessimisticEstimate( n->estimate(), role ); break; // After scheduling case NodeStartTime: result = startTime( n, role ); break; case NodeEndTime: result = endTime( n, role ); break; case NodeEarlyStart: result = earlyStart( n, role ); break; case NodeEarlyFinish: result = earlyFinish( n, role ); break; case NodeLateStart: result = lateStart( n, role ); break; case NodeLateFinish: result = lateFinish( n, role ); break; case NodePositiveFloat: result = positiveFloat( n, role ); break; case NodeFreeFloat: result = freeFloat( n, role ); break; case NodeNegativeFloat: result = negativeFloat( n, role ); break; case NodeStartFloat: result = startFloat( n, role ); break; case NodeFinishFloat: result = finishFloat( n, role ); break; case NodeAssignments: result = assignedResources( n, role ); break; // Based on scheduled values case NodeDuration: result = duration( n, role ); break; case NodeVarianceDuration: result = varianceDuration( n, role ); break; case NodeOptimisticDuration: result = optimisticDuration( n, role ); break; case NodePessimisticDuration: result = pessimisticDuration( n, role ); break; // Completion case NodeStatus: result = status( n, role ); break; case NodeCompleted: result = completed( n, role ); break; case NodePlannedEffort: result = plannedEffortTo( n, role ); break; case NodeActualEffort: result = actualEffortTo( n, role ); break; case NodeRemainingEffort: result = remainingEffort( n, role ); break; case NodePlannedCost: result = plannedCostTo( n, role ); break; case NodeActualCost: result = actualCostTo( n, role ); break; case NodeActualStart: result = startedTime( n, role ); break; case NodeStarted: result = isStarted( n, role ); break; case NodeActualFinish: result = finishedTime( n, role ); break; case NodeFinished: result = isFinished( n, role ); break; case NodeStatusNote: result = note( n, role ); break; // Scheduling errors case NodeSchedulingStatus: result = nodeSchedulingStatus( n, role ); break; case NodeNotScheduled: result = nodeIsNotScheduled( n, role ); break; case NodeAssignmentMissing: result = resourceIsMissing( n, role ); break; case NodeResourceOverbooked: result = resourceIsOverbooked( n, role ); break; case NodeResourceUnavailable: result = resourceIsNotAvailable( n, role ); break; case NodeConstraintsError: result = schedulingConstraintsError( n, role ); break; case NodeEffortNotMet: result = effortNotMet( n, role ); break; case NodeSchedulingError: result = schedulingError( n, role ); break; case NodeWBSCode: result = wbsCode( n, role ); break; case NodeLevel: result = nodeLevel( n, role ); break; // Performance case NodeBCWS: result = nodeBCWS( n, role ); break; case NodeBCWP: result = nodeBCWP( n, role ); break; case NodeACWP: result = nodeACWP( n, role ); break; case NodePerformanceIndex: result = nodePerformanceIndex( n, role ); break; case NodeCritical: result = nodeIsCritical( n, role ); break; case NodeCriticalPath: result = nodeInCriticalPath( n, role ); break; case WPOwnerName: result = wpOwnerName( n, role ); break; case WPTransmitionStatus: result = wpTransmitionStatus( n, role ); break; case WPTransmitionTime: result = wpTransmitionTime( n, role ); break; default: //debugPlan<<"Invalid property number: "<name() ) { return 0; } KUndo2MagicString s = kundo2_i18n( "Modify name" ); switch ( node->type() ) { case Node::Type_Task: s = kundo2_i18n( "Modify task name" ); break; case Node::Type_Milestone: s = kundo2_i18n( "Modify milestone name" ); break; case Node::Type_Summarytask: s = kundo2_i18n( "Modify summarytask name" ); break; case Node::Type_Project: s = kundo2_i18n( "Modify project name" ); break; } return new NodeModifyNameCmd( *node, value.toString(), s ); } } return 0; } KUndo2Command *NodeModel::setLeader( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( value.toString() != node->leader() ) { return new NodeModifyLeaderCmd( *node, value.toString(), kundo2_i18n( "Modify responsible" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setAllocation( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setDescription( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == node->description() ) { return 0; } return new NodeModifyDescriptionCmd( *node, value.toString(), kundo2_i18n( "Modify task description" ) ); } return 0; } KUndo2Command *NodeModel::setType( Node *, const QVariant &, int ) { return 0; } KUndo2Command *NodeModel::setConstraint( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Node::ConstraintType v; QStringList lst = node->constraintList( false ); if ( lst.contains( value.toString() ) ) { v = Node::ConstraintType( lst.indexOf( value.toString() ) ); } else { v = Node::ConstraintType( value.toInt() ); } //debugPlan<constraint() ) { return new NodeModifyConstraintCmd( *node, v, kundo2_i18n( "Modify constraint type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintStartTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintStartTime() ) { return new NodeModifyConstraintStartTimeCmd( *node, dt, kundo2_i18n( "Modify constraint start time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintEndTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintEndTime() ) { return new NodeModifyConstraintEndTimeCmd( *node, dt, kundo2_i18n( "Modify constraint end time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Estimate::Type v; QStringList lst = node->estimate()->typeToStringList( false ); if ( lst.contains( value.toString() ) ) { v = Estimate::Type( lst.indexOf( value.toString() ) ); } else { v = Estimate::Type( value.toInt() ); } if ( v != node->estimate()->type() ) { return new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v, kundo2_i18n( "Modify estimate type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateCalendar( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Calendar *c = 0; Calendar *old = node->estimate()->calendar(); if ( value.toInt() > 0 ) { QStringList lst = estimateCalendar( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { c = m_project->calendarByName( lst.at( value.toInt() ) ); } } if ( c != old ) { return new ModifyEstimateCalendarCmd( *node, old, c, kundo2_i18n( "Modify estimate calendar" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimate( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { double d; Duration::Unit unit; if ( value.toList().count() == 2 ) { d = value.toList()[0].toDouble(); unit = static_cast( value.toList()[1].toInt() ); } else if ( value.canConvert() ) { bool ok = Duration::valueFromString( value.toString(), d, unit ); if ( ! ok ) { return 0; } } else { return 0; } //debugPlan<"<estimate()->expectedEstimate() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), d ) ); } if ( unit != node->estimate()->unit() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), unit ) ); } if ( cmd ) { return cmd; } break; } default: break; } return 0; } KUndo2Command *NodeModel::setOptimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->optimisticRatio() ) { return new EstimateModifyOptimisticRatioCmd( *node, node->estimate()->optimisticRatio(), value.toInt(), kundo2_i18n( "Modify optimistic estimate" ) ); } break; default: break; } return 0; } KUndo2Command *NodeModel::setPessimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->pessimisticRatio() ) { return new EstimateModifyPessimisticRatioCmd( *node, node->estimate()->pessimisticRatio(), value.toInt(), kundo2_i18n( "Modify pessimistic estimate" ) ); } default: break; } return 0; } KUndo2Command *NodeModel::setRiskType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { int val = 0; QStringList lst = node->estimate()->risktypeToStringList( false ); if ( lst.contains( value.toString() ) ) { val = lst.indexOf( value.toString() ); } else { val = value.toInt(); } if ( val != node->estimate()->risktype() ) { Estimate::Risktype v = Estimate::Risktype( val ); return new EstimateModifyRiskCmd( *node, node->estimate()->risktype(), v, kundo2_i18n( "Modify risk type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setRunningAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = runningAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->runningAccount(); if ( old != a ) { return new NodeModifyRunningAccountCmd( *node, old, a, kundo2_i18n( "Modify running account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setStartupAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = startupAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->startupAccount(); //debugPlan<<(value.toInt())<<";"<<(lst.at( value.toInt()))<<":"<startupCost() ) { return new NodeModifyStartupCostCmd( *node, v, kundo2_i18n( "Modify startup cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = shutdownAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->shutdownAccount(); if ( old != a ) { return new NodeModifyShutdownAccountCmd( *node, old, a, kundo2_i18n( "Modify shutdown account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownCost( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { double v = value.toDouble(); if ( v != node->shutdownCost() ) { return new NodeModifyShutdownCostCmd( *node, v, kundo2_i18n( "Modify shutdown cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setCompletion( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::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 ); return new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ); } return 0; } KUndo2Command *NodeModel::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 ); return new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ); } return 0; } KUndo2Command *NodeModel::setStartedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual start time" ) ); 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 ) ); } } return m; } default: break; } return 0; } KUndo2Command *NodeModel::setFinishedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual finish time" ) ); if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); 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 ) ); } } 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() ) ); } return m; } default: break; } return 0; } //---------------------------- NodeItemModel::NodeItemModel( QObject *parent ) : ItemModelBase( parent ), m_node( 0 ), m_projectshown( false ) { setReadOnly( NodeModel::NodeDescription, true ); } NodeItemModel::~NodeItemModel() { } void NodeItemModel::setShowProject( bool on ) { beginResetModel(); m_projectshown = on; endResetModel(); emit projectShownChanged( on ); } void NodeItemModel::slotNodeToBeInserted( Node *parent, int row ) { //debugPlan<name()<<"; "<parentNode()->name()<<"-->"<name(); Q_ASSERT( node->parentNode() == m_node ); endInsertRows(); m_node = 0; emit nodeInserted( node ); } void NodeItemModel::slotNodeToBeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( m_node == 0 ); m_node = node; int row = index( node ).row(); beginRemoveRows( index( node->parentNode() ), row, row ); } void NodeItemModel::slotNodeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( node == m_node ); #ifdef NDEBUG Q_UNUSED(node) #endif endRemoveRows(); m_node = 0; } void NodeItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { //debugPlan<parentNode()->name()<name()<parentNode() ), pos, pos, index( newParent ), newPos ); } void NodeItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); //debugPlan<parentNode()->name()<parentNode()->indexOf( node ); endMoveRows(); } void NodeItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void NodeItemModel::slotProjectCalculated(ScheduleManager *sm) { debugPlan<allNodes() ) { int row = n->parentNode()->indexOf( n ); QModelIndex idx = createIndex( row, NodeModel::NodeWBSCode, n ); emit dataChanged( idx, idx ); } } void NodeItemModel::setProject( Project *project ) { beginResetModel(); if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &NodeItemModel::projectDeleted); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect( m_project, &Project::wbsDefinitionChanged, this, &NodeItemModel::slotWbsDefinitionChanged); disconnect( m_project, &Project::nodeChanged, this, &NodeItemModel::slotNodeChanged); disconnect( m_project, &Project::nodeToBeAdded, this, &NodeItemModel::slotNodeToBeInserted); disconnect( m_project, &Project::nodeToBeRemoved, this, &NodeItemModel::slotNodeToBeRemoved); disconnect( m_project, &Project::nodeToBeMoved, this, &NodeItemModel::slotNodeToBeMoved); disconnect( m_project, &Project::nodeMoved, this, &NodeItemModel::slotNodeMoved); disconnect( m_project, &Project::nodeAdded, this, &NodeItemModel::slotNodeInserted); disconnect( m_project, &Project::nodeRemoved, this, &NodeItemModel::slotNodeRemoved); disconnect( m_project, &Project::projectCalculated, this, &NodeItemModel::slotProjectCalculated); } m_project = project; debugPlan<"<isBaselined(); flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeAllocation: // allocation if ( n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeEstimateType: // estimateType { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimate: // estimate { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeOptimisticRatio: // optimisticRatio case NodeModel::NodePessimisticRatio: // pessimisticRatio { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimateCalendar: { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeRisk: // risktype { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraint: // constraint type if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeConstraintEnd: { // constraint end if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeRunningAccount: // running account if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description flags |= Qt::ItemIsEditable; break; default: break; } Task *t = static_cast( n ); if ( manager() && t->isScheduled( id() ) ) { if ( ! t->completion().isStarted() ) { switch ( index.column() ) { case NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualFinish: if ( t->type() == Node::Type_Milestone ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeCompleted: if ( t->state() & Node::State_ReadyToStart ) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if ( ! t->completion().isFinished() ) { switch ( index.column() ) { case NodeModel::NodeActualFinish: case NodeModel::NodeCompleted: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualEffort: if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) { flags |= Qt::ItemIsEditable; } break; default: break; } } } } return flags; } QModelIndex NodeItemModel::parent( const QModelIndex &index ) const { if ( ! index.isValid() ) { return QModelIndex(); } Node *n = node( index ); if ( n == 0 || n == m_project ) { return QModelIndex(); } Node *p = n->parentNode(); if ( p == m_project ) { return m_projectshown ? createIndex( 0, 0, p ) : QModelIndex(); } int row = p->parentNode()->indexOf( p ); if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, p ); } QModelIndex NodeItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { Q_ASSERT( parent.model() == this ); } //debugPlan<= columnCount() || row < 0 ) { //debugPlan<= p->numChildren() ) { errorPlan<name()<<" row too high"<childNode( row ); QModelIndex idx = createIndex(row, column, n); //debugPlan<parentNode(); if ( par ) { //debugPlan<"<indexOf( node ), column, const_cast(node) ); } if ( m_projectshown && node == m_project ) { return createIndex( 0, column, m_project ); } //debugPlan<( node ); if ( task == 0 ) { return false; } switch ( role ) { case Qt::EditRole: { MacroCommand *cmd = 0; QStringList res = m_project->resourceNameList(); QStringList req = node->requestNameList(); QStringList alloc; foreach ( const QString &s, value.toString().split( QRegExp(" *, *"), QString::SkipEmptyParts ) ) { alloc << s.trimmed(); } // first add all new resources (to "default" group) ResourceGroup *pargr = m_project->groupByName( i18n( "Resources" ) ); foreach ( const QString &s, alloc ) { Resource *r = m_project->resourceByName( s.trimmed() ); if ( r != 0 ) { continue; } if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Add resource" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s.trimmed() ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } KUndo2MagicString c = kundo2_i18n( "Modify resource allocations" ); // Handle deleted requests foreach ( const QString &s, req ) { // if a request is not in alloc, it must have been be removed by the user if ( alloc.indexOf( s ) == -1 ) { // remove removed resource request ResourceRequest *r = node->resourceRequest( s ); if ( r ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); //debugPlan<<"delete request:"<resource()->name()<<" group:"<parent()->group()->name(); cmd->addCommand( new RemoveResourceRequestCmd( r->parent(), r ) ); } } } // Handle new requests QHash groupmap; foreach ( const QString &s, alloc ) { // if an allocation is not in req, it must be added if ( req.indexOf( s ) == -1 ) { ResourceGroup *pargr = 0; Resource *r = m_project->resourceByName( s ); if ( r == 0 ) { // Handle request to non existing resource pargr = m_project->groupByName( i18n( "Resources" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } else { pargr = r->parentGroup(); //debugPlan<<"add '"<name()<<"' to group:"<resourceGroupRequest( pargr ); if ( g == 0 ) { g = groupmap.value( pargr ); } if ( g == 0 ) { // create a group request if ( cmd == 0 ) cmd = new MacroCommand( c ); g = new ResourceGroupRequest( pargr ); cmd->addCommand( new AddResourceGroupRequestCmd( *task, g ) ); groupmap.insert( pargr, g ); //debugPlan<<"add group request:"<addCommand( new AddResourceRequestCmd( g, new ResourceRequest( r, r->units() ) ) ); //debugPlan<<"add request:"<name()<<" group:"<name()<type() == Node::Type_Task ) { Completion &c = static_cast( node )->completion(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) ); if ( ! c.isStarted() ) { m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); } m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) ); if ( value.toInt() == 100 ) { m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); } emit executeCommand( m ); // also adds a new entry if necessary - if ( c.entrymode() == Completion::EnterCompleted ) { + if (c.entrymode() != Completion::EnterEffortPerResource) { Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlan<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); 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 ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) ); emit executeCommand( m ); // also adds a new entry if necessary return true; } return false; } return false; } QVariant NodeItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( role == Role::Object ) { return n ? QVariant::fromValue( static_cast( n ) ) : QVariant(); } QVariant result; if ( n != 0 ) { result = m_nodemodel.data( n, index.column(), role ); //debugPlan<name()<<": "<numChildren(); } Qt::DropActions NodeItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList NodeItemModel::mimeTypes() const { return ItemModelBase::mimeTypes() << "application/x-vnd.kde.plan.nodeitemmodel.internal" << "application/x-vnd.kde.plan.resourceitemmodel.internal" << "application/x-vnd.kde.plan.project" << "text/uri-list"; } QMimeData *NodeItemModel::mimeData( const QModelIndexList & indexes ) const { QMimeData *m = ItemModelBase::mimeData(indexes); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QList rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); QList nodes; foreach (const QModelIndex &index, indexes) { if (index.isValid()) { //debugPlan<setData("application/x-vnd.kde.plan.project", context.document.toByteArray()); } return m; } bool NodeItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { debugPlan<hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal") ) { switch ( dropIndicatorPosition ) { case ItemModelBase::OnItem: if ( index.column() == NodeModel::NodeAllocation ) { debugPlan<<"resource:"<type() == Node::Type_Task); return dn->type() == Node::Type_Task; } else if ( index.column() == NodeModel::NodeResponsible ) { debugPlan<<"resource:"<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal") || data->hasFormat( "application/x-vnd.kde.plan.project" ) || data->hasUrls() ) { switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling, if not project if ( dn == m_project ) { return dropAllowed( dn, data ); } return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } } return false; } QList NodeItemModel::resourceList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; debugPlan<<"id"<findResource( id ); if ( r ) { lst << r; } } debugPlan<isBaselined() && on->type() != Node::Type_Summarytask && on->type() != Node::Type_Project) { return false; } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( n->type() == Node::Type_Project || on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } } return true; } QList NodeItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList NodeItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool NodeItemModel::dropResourceMimeData( const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *n = node( parent ); debugPlan<name(); if ( parent.column() == NodeModel::NodeResponsible ) { QString s; foreach ( Resource *r, resourceList( stream ) ) { s += r->name(); } if ( ! s.isEmpty() ) { if ( action == Qt::CopyAction && ! n->leader().isEmpty() ) { s += ',' + n->leader(); } KUndo2Command *cmd = m_nodemodel.setLeader( n, s, Qt::EditRole ); if ( cmd ) { emit executeCommand( cmd ); } debugPlan<type() == Node::Type_Task ) { QList lst = resourceList( stream ); if ( action == Qt::CopyAction ) { lst += static_cast( n )->requestedResources(); } KUndo2Command *cmd = createAllocationCommand( static_cast( *n ), lst ); if ( cmd ) { emit executeCommand( cmd ); } return true; } return true; } bool NodeItemModel::dropProjectMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { Node *n = node( parent ); if ( n == 0 ) { n = m_project; } debugPlan<data("application/x-vnd.kde.plan.project"), n, n->childNode(row), kundo2_i18n("Insert tasks")); emit executeCommand( cmd ); return true; } bool NodeItemModel::dropUrlMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project project; XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( &project ); if ( ! project.load( element, status ) ) { debugPlan<<"Failed to load project from:"<childNode( row - 1 ), kundo2_i18n( "Insert %1", url.fileName() ) ); emit executeCommand( cmd ); return true; } KUndo2Command *NodeItemModel::createAllocationCommand( Task &task, const QList &lst ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Modify resource allocations" ) ); QHash groups; foreach ( Resource *r, lst ) { if ( ! groups.contains( r->parentGroup() ) && task.resourceGroupRequest( r->parentGroup() ) == 0 ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); groups[ r->parentGroup() ] = gr; cmd->addCommand( new AddResourceGroupRequestCmd( task, gr ) ); } } QList resources = task.requestedResources(); foreach ( Resource *r, lst ) { if ( resources.contains( r ) ) { continue; } ResourceGroupRequest *gr = groups.value( r->parentGroup() ); if ( gr == 0 ) { gr = task.resourceGroupRequest( r->parentGroup() ); } if ( gr == 0 ) { errorPlan<<"No group request found, cannot add resource request:"<name(); continue; } cmd->addCommand( new AddResourceRequestCmd( gr, new ResourceRequest( r, 100 ) ) ); } foreach ( Resource *r, resources ) { if ( ! lst.contains( r ) ) { ResourceGroupRequest *gr = task.resourceGroupRequest( r->parentGroup() ); ResourceRequest *rr = task.requests().find( r ); if ( gr && rr ) { cmd->addCommand( new RemoveResourceRequestCmd( gr, rr ) ); } } } if ( cmd->isEmpty() ) { delete cmd; return 0; } return cmd; } bool NodeItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { debugPlan<formats(); if (action == Qt::IgnoreAction) { return true; } if ( data->hasFormat( "application/x-vnd.kde.plan.resourceitemmodel.internal" ) ) { return dropResourceMimeData( data, action, row, column, parent ); } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; if ( pos >= 0 && n->parentNode() == par && par->indexOf( n ) < pos ) { --pos; } if ( n->parentNode() == par ) { // avoid drop into the same position, QAbstractItemModel does not like it int crow = par->indexOf( n ); if ( ( ( pos == -1 ) && ( crow == par->numChildren() - 1 ) ) || ( pos == crow ) ) { delete cmd; cmd = 0; continue; } } cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } } if ( data->hasFormat( "application/x-vnd.kde.plan.project" ) ) { debugPlan; return dropProjectMimeData( data, action, row, column, parent ); } if ( data->hasUrls() ) { return dropUrlMimeData( data, action, row, column, parent ); } return false; } Node *NodeItemModel::node( const QModelIndex &index ) const { Node *n = m_project; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); Q_ASSERT( n ); } return n; } void NodeItemModel::slotNodeChanged( Node *node ) { if ( node == 0 || ( ! m_projectshown && node->type() == Node::Type_Project ) ) { return; } if ( node->type() == Node::Type_Project ) { emit dataChanged( createIndex( 0, 0, node ), createIndex( 0, columnCount()-1, node ) ); return; } int row = node->parentNode()->findChildNode( node ); Q_ASSERT( row >= 0 ); emit dataChanged( createIndex( row, 0, node ), createIndex( row, columnCount()-1, node ) ); } QModelIndex NodeItemModel::insertTask( Node *node, Node *after ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Add task" ) ); cmd->addCommand( new TaskAddCmd( m_project, node, after ) ); if ( m_project && node->type() == Node::Type_Task ) { QHash groups; foreach ( Resource *r, m_project->autoAllocateResources() ) { if ( ! groups.contains( r->parentGroup() ) ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); cmd->addCommand( new AddResourceGroupRequestCmd( static_cast(*node), gr ) ); groups[ r->parentGroup() ] = gr; } ResourceRequest *rr = new ResourceRequest( r, 100 ); cmd->addCommand( new AddResourceRequestCmd( groups[ r->parentGroup() ], rr ) ); } } emit executeCommand( cmd ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<<"Inserted: "<name()<<"; "<name(); return QModelIndex(); } QModelIndex NodeItemModel::insertSubtask( Node *node, Node *parent ) { emit executeCommand( new SubtaskAddCmd( m_project, node, parent, kundo2_i18n( "Add sub-task" ) ) ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<parentNode()<<" inserted: "<name()<<"; "<name(); return QModelIndex(); } int NodeItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } debugPlan< lst = parentmap.values(); while ( ! lst.isEmpty() ) delete (int*)(lst.takeFirst()); } int GanttItemModel::rowCount( const QModelIndex &parent ) const { if ( m_showSpecial ) { if (parentmap.values().contains(parent.internalPointer())) { // clazy:exclude=container-anti-pattern return 0; } Node *n = node( parent ); if ( n && n->type() == Node::Type_Task ) { return 5; // the task + early start + late finish ++ } } return NodeItemModel::rowCount( parent ); } QModelIndex GanttItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( m_showSpecial && parent.isValid() ) { Node *p = node( parent ); if ( p->type() == Node::Type_Task ) { void *v = 0; foreach ( void *i, parentmap.values( p ) ) { // clazy:exclude=container-anti-pattern if ( *( (int*)( i ) ) == row ) { v = i; break; } } if ( v == 0 ) { v = new int( row ); const_cast( this )->parentmap.insertMulti( p, v ); } return createIndex( row, column, v ); } } return NodeItemModel::index( row, column, parent ); } QModelIndex GanttItemModel::parent( const QModelIndex &idx ) const { if ( m_showSpecial ) { QList lst = parentmap.keys( idx.internalPointer() ); if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); return index( lst.first() ); } } return NodeItemModel::parent( idx ); } QVariant GanttItemModel::data( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } QModelIndex idx = index; QList lst; if ( m_showSpecial ) { lst = parentmap.keys( idx.internalPointer() ); } if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); int row = *((int*)(idx.internalPointer())); Node *n = lst.first(); if ( role == SpecialItemTypeRole ) { return row; // 0=task, 1=early start, 2=late finish... } switch ( row ) { case 0: // the task if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { switch ( n->type() ) { case Node::Type_Task: return KGantt::TypeTask; default: break; } } break; case 1: { // early start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyStart( id() ); default: break; } return QVariant(); } case 2: { // late finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateFinish( id() ); default: break; } return QVariant(); } case 3: { // late start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateStart( id() ); default: break; } return QVariant(); } case 4: { // early finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyFinish( id() ); default: break; } return QVariant(); } default: return QVariant(); } idx = createIndex( idx.row(), idx.column(), n ); } else { if ( role == SpecialItemTypeRole ) { return 0; // task of some type } if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { QModelIndex notScheduled = idx.sibling(idx.row(), NodeModel::NodeNotScheduled); if (notScheduled.data(Qt::EditRole).toBool()) { return QVariant(); } QVariant result = NodeItemModel::data( idx, Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Project: return KGantt::TypeSummary; case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return m_showSpecial ? KGantt::TypeMulti : KGantt::TypeTask; } } } return NodeItemModel::data( idx, role ); } //---------------------------- MilestoneItemModel::MilestoneItemModel( QObject *parent ) : ItemModelBase( parent ) { } MilestoneItemModel::~MilestoneItemModel() { } QList MilestoneItemModel::mileStones() const { QList lst; foreach( Node* n, m_nodemap ) { if ( n->type() == Node::Type_Milestone ) { lst << n; } } return lst; } void MilestoneItemModel::slotNodeToBeInserted( Node *parent, int row ) { Q_UNUSED(parent); Q_UNUSED(row); } void MilestoneItemModel::slotNodeInserted( Node *node ) { Q_UNUSED(node); resetModel(); } void MilestoneItemModel::slotNodeToBeRemoved( Node *node ) { Q_UNUSED(node); //debugPlan<name(); /* int row = m_nodemap.values().indexOf( node ); if ( row != -1 ) { Q_ASSERT( m_nodemap.contains( node->wbsCode() ) ); Q_ASSERT( m_nodemap.keys().indexOf( node->wbsCode() ) == row ); beginRemoveRows( QModelIndex(), row, row ); m_nodemap.remove( node->wbsCode() ); endRemoveRows(); }*/ } void MilestoneItemModel::slotNodeRemoved( Node *node ) { Q_UNUSED(node); resetModel(); //endRemoveRows(); } void MilestoneItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void MilestoneItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { Q_UNUSED( node ); Q_UNUSED( pos ); Q_UNUSED( newParent ); Q_UNUSED( newPos ); } void MilestoneItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); resetModel(); } void MilestoneItemModel::setProject( Project *project ) { if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &MilestoneItemModel::projectDeleted); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect( m_project, &Project::wbsDefinitionChanged, this, &MilestoneItemModel::slotWbsDefinitionChanged); disconnect( m_project, &Project::nodeChanged, this, &MilestoneItemModel::slotNodeChanged); disconnect( m_project, &Project::nodeToBeAdded, this, &MilestoneItemModel::slotNodeToBeInserted); disconnect( m_project, &Project::nodeToBeRemoved, this, &MilestoneItemModel::slotNodeToBeRemoved); disconnect(m_project, &Project::nodeToBeMoved, this, &MilestoneItemModel::slotNodeToBeMoved); disconnect(m_project, &Project::nodeMoved, this, &MilestoneItemModel::slotNodeMoved); disconnect( m_project, &Project::nodeAdded, this, &MilestoneItemModel::slotNodeInserted); disconnect( m_project, &Project::nodeRemoved, this, &MilestoneItemModel::slotNodeRemoved); } m_project = project; //debugPlan<"<allNodes() ) { m_nodemap.insert( n->wbsCode(true), n ); } } return cnt != m_nodemap.count(); } void MilestoneItemModel::resetModel() { beginResetModel(); resetData(); endResetModel(); } Qt::ItemFlags MilestoneItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); if ( !index.isValid() ) { if ( m_readWrite ) { flags |= Qt::ItemIsDropEnabled; } return flags; } flags |= Qt::ItemIsDragEnabled; if ( m_readWrite ) { flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraint: // constraint type flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraintStart: { // constraint start Node *n = node( index ); if ( n == 0 ) break; int c = n->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraintEnd: { // constraint end Node *n = node( index ); if ( n == 0 ) break; int c = n->constraint(); if ( c == Node::MustFinishOn || c == Node::FinishNotLater || c == Node::FixedInterval ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost Node *n = node( index ); if ( n && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description break; default: flags &= ~Qt::ItemIsEditable; } } return flags; } QModelIndex MilestoneItemModel::parent( const QModelIndex &index ) const { Q_UNUSED(index); return QModelIndex(); } QModelIndex MilestoneItemModel::index( int row, int column, const QModelIndex &parent ) const { //debugPlan<= m_nodemap.count() ) { //debugPlan<<"No index for"<( node ) ), 0, const_cast(node) ); // clazy:exclude=container-anti-pattern } QVariant MilestoneItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( n != 0 ) { if ( index.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { result = m_nodemodel.data( n, index.column(), Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return KGantt::TypeTask; } return result; } } result = m_nodemodel.data( n, index.column(), role ); return result; } bool MilestoneItemModel::setData( const QModelIndex &index, const QVariant &/*value*/, int role ) { if ( ( flags(index) &Qt::ItemIsEditable ) == 0 || role != Qt::EditRole ) { return false; } // Node *n = node( index ); switch (index.column()) { default: qWarning("data: invalid display value column %d", index.column()); return false; } return false; } QVariant MilestoneItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole || role == Qt::EditRole) { return m_nodemodel.headerData(section, role); } } if ( role == Qt::ToolTipRole ) { return NodeModel::headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *MilestoneItemModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeModel::NodeEstimateType: return new EnumDelegate( parent ); case NodeModel::NodeEstimateCalendar: return new EnumDelegate( parent ); case NodeModel::NodeEstimate: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeOptimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodePessimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodeRisk: return new EnumDelegate( parent ); case NodeModel::NodeConstraint: return new EnumDelegate( parent ); case NodeModel::NodeRunningAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupCost: return new MoneyDelegate( parent ); case NodeModel::NodeShutdownAccount: return new EnumDelegate( parent ); case NodeModel::NodeShutdownCost: return new MoneyDelegate( parent ); case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent ); default: return 0; } return 0; } int MilestoneItemModel::columnCount( const QModelIndex &/*parent*/ ) const { return m_nodemodel.propertyCount(); } int MilestoneItemModel::rowCount( const QModelIndex &parent ) const { //debugPlan< rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool MilestoneItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { //debugPlan; Node *dn = node( index ); if ( dn == 0 ) { errorPlan<<"no node to drop on!"; return false; // hmmm } switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } return false; } bool MilestoneItemModel::dropAllowed( Node *on, const QMimeData *data ) { if ( !data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal") ) { return false; } if ( on == m_project ) { return true; } QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } return true; } QList MilestoneItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList MilestoneItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool MilestoneItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { //debugPlan<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { return false; } if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } return false; } Node *MilestoneItemModel::node( const QModelIndex &index ) const { Node *n = 0; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); } return n; } void MilestoneItemModel::slotNodeChanged( Node *node ) { //debugPlan<name(); if ( node == 0 ) { return; } beginResetModel(); resetData(); endResetModel(); } void MilestoneItemModel::slotWbsDefinitionChanged() { //debugPlan; if ( m_project == 0 ) { return; } if ( ! m_nodemap.isEmpty() ) { beginResetModel(); resetData(); endResetModel(); } } int MilestoneItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } return v; } //-------------- NodeSortFilterProxyModel::NodeSortFilterProxyModel( ItemModelBase* model, QObject *parent, bool filterUnscheduled ) : QSortFilterProxyModel( parent ), m_filterUnscheduled( filterUnscheduled ) { setSourceModel( model ); setDynamicSortFilter( true ); } ItemModelBase *NodeSortFilterProxyModel::itemModel() const { return static_cast( sourceModel() ); } void NodeSortFilterProxyModel::setFilterUnscheduled( bool on ) { m_filterUnscheduled = on; invalidateFilter(); } bool NodeSortFilterProxyModel::filterAcceptsRow ( int row, const QModelIndex & parent ) const { //debugPlan<project() == 0 ) { //debugPlan<project(); return false; } if ( m_filterUnscheduled ) { QString s = sourceModel()->data( sourceModel()->index( row, NodeModel::NodeNotScheduled, parent ), Qt::EditRole ).toString(); if ( s == "true" ) { //debugPlan<<"Filtered unscheduled:"<index( row, 0, parent ); return false; } } bool accepted = QSortFilterProxyModel::filterAcceptsRow( row, parent ); //debugPlan<index( row, 0, parent )<<"accepted ="<sortRole(column)); QSortFilterProxyModel::sort(column, order); } //------------------ TaskModuleModel::TaskModuleModel( QObject *parent ) : QAbstractItemModel( parent ) { } void TaskModuleModel::addTaskModule( Project *project, const QUrl &url ) { beginInsertRows( QModelIndex(), m_modules.count(), m_modules.count() ); m_modules << project; m_urls << url; endInsertRows(); } Qt::ItemFlags TaskModuleModel::flags( const QModelIndex &idx ) const { Qt::ItemFlags f = QAbstractItemModel::flags( idx ) | Qt::ItemIsDropEnabled; if ( idx.isValid() ) { f |= Qt::ItemIsDragEnabled; } return f; } int TaskModuleModel::columnCount (const QModelIndex &/*idx*/ ) const { return 1; } int TaskModuleModel::rowCount( const QModelIndex &idx ) const { return idx.isValid() ? 0 : m_modules.count(); } QVariant TaskModuleModel::data( const QModelIndex& idx, int role ) const { if (!idx.isValid() || idx.row() >= m_modules.count()) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_modules.value( idx.row() )->name(); case Qt::ToolTipRole: return m_modules.value( idx.row() )->description(); case Qt::WhatsThisRole: return m_modules.value( idx.row() )->description(); case Qt::UserRole: return m_urls.value(idx.row()); default: break; } return QVariant(); } QVariant TaskModuleModel::headerData( int /*section*/, Qt::Orientation orientation , int role ) const { if ( orientation == Qt::Horizontal ) { switch ( role ) { case Qt::DisplayRole: return xi18nc( "@title:column", "Name" ); default: break; } } return QVariant(); } QModelIndex TaskModuleModel::parent( const QModelIndex& /*idx*/ ) const { return QModelIndex(); } QModelIndex TaskModuleModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column, m_modules.value( row ) ); } QStringList TaskModuleModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan" << "text/uri-list"; } bool TaskModuleModel::dropMimeData( const QMimeData *data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex &/*parent*/ ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project *project = new Project(); XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( project ); if ( project->load( element, status ) ) { stripProject( project ); addTaskModule( project, url ); if ( emitsignal ) { // FIXME: save destroys the project, so give it a copy (see kptview.cpp) Project p; status.setProject( &p ); p.load( element, status ); emit saveTaskModule( url, &p ); } } else { debugPlan<<"Failed to load project from:"<setData( "application/x-vnd.kde.plan.project", context.document.toByteArray() ); } } return mime; } void TaskModuleModel::stripProject( Project *project ) const { foreach ( ScheduleManager *sm, project->scheduleManagers() ) { DeleteScheduleManagerCmd c( *project, sm ); } } void TaskModuleModel::loadTaskModules( const QStringList &files ) { debugPlan< 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 "kpttaskstatusmodel.h" #include "PlanMacros.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptitemmodelbase.h" #include "kpttaskcompletedelegate.h" #include "kptcommand.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptnodeitemmodel.h" #include "kptdebug.h" #include #include #include namespace KPlato { TaskStatusItemModel::TaskStatusItemModel( QObject *parent ) : ItemModelBase( parent ), m_period( 7 ), m_periodType( UseCurrentDate ), m_weekday( Qt::Friday ) { m_topNames << i18n( "Not Started" ); m_topTips << i18n( "Tasks that should have been started" ); m_top.append(&m_notstarted ); m_topNames << i18n( "Running" ); m_topTips << i18n( "Tasks that are running" ); m_top.append(&m_running ); m_topNames << i18n( "Finished" ); m_topTips << i18n( "Tasks that have finished during this period" ); m_top.append(&m_finished ); m_topNames << i18n( "Next Period" ); m_topTips << i18n( "Tasks that are scheduled to start next period" ); m_top.append(&m_upcoming ); /* connect( this, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()) ); connect( this, SIGNAL(modelReset()), SLOT(slotReset()) );*/ } TaskStatusItemModel::~TaskStatusItemModel() { } void TaskStatusItemModel::slotAboutToBeReset() { debugPlan; clear(); } void TaskStatusItemModel::slotReset() { debugPlan; refresh(); } void TaskStatusItemModel::slotNodeToBeInserted( Node *, int ) { //debugPlan<name(); clear(); } void TaskStatusItemModel::slotNodeInserted( Node * /*node*/ ) { //debugPlan<getParent->name()<<"-->"<name(); refresh(); } void TaskStatusItemModel::slotNodeToBeRemoved( Node * /*node*/ ) { //debugPlan<name(); clear(); } void TaskStatusItemModel::slotNodeRemoved( Node * /*node*/ ) { //debugPlan<name(); refresh(); } void TaskStatusItemModel::slotNodeToBeMoved(Node *node, int pos, Node *newParent, int newPos) { Q_UNUSED( node ); Q_UNUSED( pos ); Q_UNUSED( newParent ); Q_UNUSED( newPos ); clear(); } void TaskStatusItemModel::slotNodeMoved( Node * /*node*/ ) { //debugPlan<name(); refresh(); } void TaskStatusItemModel::setProject( Project *project ) { beginResetModel(); clear(); if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &TaskStatusItemModel::projectDeleted); disconnect(m_project, &Project::localeChanged, this, &TaskStatusItemModel::slotLayoutChanged); disconnect( m_project, &Project::wbsDefinitionChanged, this, &TaskStatusItemModel::slotWbsDefinitionChanged ); disconnect( m_project, &Project::nodeChanged, this, &TaskStatusItemModel::slotNodeChanged ); disconnect( m_project, &Project::nodeToBeAdded, this, &TaskStatusItemModel::slotNodeToBeInserted ); disconnect( m_project, &Project::nodeToBeRemoved, this, &TaskStatusItemModel::slotNodeToBeRemoved ); disconnect(m_project, &Project::nodeToBeMoved, this, &TaskStatusItemModel::slotNodeToBeMoved); disconnect( m_project, &Project::nodeAdded, this, &TaskStatusItemModel::slotNodeInserted ); disconnect( m_project, &Project::nodeRemoved, this, &TaskStatusItemModel::slotNodeRemoved ); disconnect(m_project, &Project::nodeMoved, this, &TaskStatusItemModel::slotNodeMoved); } m_project = project; m_nodemodel.setProject( project ); if ( project ) { connect(m_project, &Project::aboutToBeDeleted, this, &TaskStatusItemModel::projectDeleted); connect(m_project, &Project::localeChanged, this, &TaskStatusItemModel::slotLayoutChanged); connect( m_project, &Project::wbsDefinitionChanged, this, &TaskStatusItemModel::slotWbsDefinitionChanged ); connect( m_project, &Project::nodeChanged, this, &TaskStatusItemModel::slotNodeChanged ); connect( m_project, &Project::nodeToBeAdded, this, &TaskStatusItemModel::slotNodeToBeInserted ); connect( m_project, &Project::nodeToBeRemoved, this, &TaskStatusItemModel::slotNodeToBeRemoved ); connect(m_project, &Project::nodeToBeMoved, this, &TaskStatusItemModel::slotNodeToBeMoved); connect( m_project, &Project::nodeAdded, this, &TaskStatusItemModel::slotNodeInserted ); connect( m_project, &Project::nodeRemoved, this, &TaskStatusItemModel::slotNodeRemoved ); connect(m_project, &Project::nodeMoved, this, &TaskStatusItemModel::slotNodeMoved); } endResetModel(); } void TaskStatusItemModel::setScheduleManager( ScheduleManager *sm ) { beginResetModel(); if (sm == m_nodemodel.manager()) { return; } clear(); if ( m_nodemodel.manager() ) { } m_nodemodel.setManager( sm ); ItemModelBase::setScheduleManager( sm ); if ( sm ) { } endResetModel(); refresh(); } void TaskStatusItemModel::clear() { foreach ( NodeMap *l, m_top ) { int c = l->count(); if ( c > 0 ) { //FIXME: gives error msg: // Can't select indexes from different model or with different parents QModelIndex i = index( l ); debugPlan<clear(); endRemoveRows(); } } } void TaskStatusItemModel::setNow() { switch ( m_periodType ) { case UseWeekday: { QDate date = QDate::currentDate(); int wd = date.dayOfWeek(); date = date.addDays( m_weekday - wd ); if ( wd < m_weekday ) { date = date.addDays( -7 ); } m_nodemodel.setNow( date ); break; } case UseCurrentDate: m_nodemodel.setNow( QDate::currentDate() ); break; default: m_nodemodel.setNow( QDate::currentDate() ); break; } } void TaskStatusItemModel::refresh() { clear(); if ( m_project == 0 ) { return; } m_id = m_nodemodel.id(); if ( m_id == -1 ) { return; } setNow(); const QDate begin = m_nodemodel.now().addDays( -m_period ); const QDate end = m_nodemodel.now().addDays( m_period ); foreach( Node* n, m_project->allNodes() ) { if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) { continue; } Task *task = static_cast( n ); const TaskStatus status = taskStatus(task, begin, end); if (status != TaskUnknownStatus) { m_top.at(status)->insert(task->wbsCode(), task); } } foreach ( NodeMap *l, m_top ) { int c = l->count(); if ( c > 0 ) { debugPlan<isScheduled( m_id ) ) { 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 NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeCompleted: if ( t->state() & Node::State_ReadyToStart ) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if ( ! t->completion().isFinished() ) { // task is running switch ( index.column() ) { case NodeModel::NodeActualFinish: case NodeModel::NodeCompleted: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualEffort: if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) { flags |= Qt::ItemIsEditable; } break; default: break; } } return flags; } QModelIndex TaskStatusItemModel::parent( const QModelIndex &index ) const { if ( !index.isValid() ) { return QModelIndex(); } //debugPlan<( index.internalPointer() ) ); if ( row != -1 ) { return QModelIndex(); // top level has no parent } Node *n = node( index ); if ( n == 0 ) { return QModelIndex(); } NodeMap *lst = 0; foreach ( NodeMap *l, m_top ) { if (CONTAINS((*l), n)) { lst = l; break; } } if ( lst == 0 ) { return QModelIndex(); } return createIndex( m_top.indexOf( lst ), 0, lst ); } QModelIndex TaskStatusItemModel::index( int row, int column, const QModelIndex &parent ) const { //debugPlan<= columnCount() || row < 0 ) { return QModelIndex(); } if ( ! parent.isValid() ) { if ( row >= m_top.count() ) { return QModelIndex(); } return createIndex(row, column, m_top.value( row ) ); } NodeMap *l = list( parent ); if ( l == 0 ) { return QModelIndex(); } if ( row >= rowCount( parent ) ) { warnPlan<<"Row >= rowCount, Qt4.4 asks, so we need to handle it"< &nodes = l->values(); QModelIndex i = createIndex(row, column, nodes.value(row)); Q_ASSERT( i.internalPointer() != 0 ); return i; } QModelIndex TaskStatusItemModel::index( const Node *node ) const { if ( m_project == 0 || node == 0 ) { return QModelIndex(); } foreach( NodeMap *l, m_top ) { const QList &nodes = l->values(); int row = nodes.indexOf( const_cast( node ) ); if ( row != -1 ) { return createIndex( row, 0, const_cast( node ) ); } } return QModelIndex(); } QModelIndex TaskStatusItemModel::index( const NodeMap *lst ) const { if ( m_project == 0 || lst == 0 ) { return QModelIndex(); } NodeMap *l = const_cast( lst ); int row = m_top.indexOf( l ); if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, l ); } QVariant TaskStatusItemModel::name( int row, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return m_topNames.value( row ); case Qt::ToolTipRole: return m_topTips.value( row ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool TaskStatusItemModel::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(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); // 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 ) ); } emit executeCommand( m ); // also adds a new entry if necessary - if ( c.entrymode() == Completion::EnterCompleted ) { + if (c.entrymode() != Completion::EnterEffortPerResource) { Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlan<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); 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 TaskStatusItemModel::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 TaskStatusItemModel::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 TaskStatusItemModel::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_i18n( "Modify actual start time" ) ); 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 TaskStatusItemModel::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_i18n( "Modify actual finish time" ) ); if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); 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 ) ); } } 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; } QVariant TaskStatusItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( ! index.isValid() ) { return result; } if ( role == Qt::TextAlignmentRole ) { return alignment( index.column() ); } Node *n = node( index ); if ( n == 0 ) { switch ( index.column() ) { case NodeModel::NodeName: return name( index.row(), role ); default: break; } return QVariant(); } result = m_nodemodel.data( n, index.column(), role ); if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case NodeModel::NodeActualStart: if ( ! result.isValid() ) { return m_nodemodel.data( n, NodeModel::NodeStatus, role ); } break; } } else if ( role == Qt::EditRole ) { switch ( index.column() ) { case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: if ( ! result.isValid() ) { return QDateTime::currentDateTime(); } break; } } return result; } bool TaskStatusItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { if ( ! index.isValid() ) { return ItemModelBase::setData( index, value, role ); } switch ( index.column() ) { case NodeModel::NodeCompleted: return setCompletion( node( index ), value, role ); case NodeModel::NodeRemainingEffort: return setRemainingEffort( node( index ), value, role ); case NodeModel::NodeActualEffort: return setActualEffort( node( index ), value, role ); case NodeModel::NodeActualStart: return setStartedTime( node( index ), value, role ); case NodeModel::NodeActualFinish: return setFinishedTime( node( index ), value, role ); default: break; } return false; } QVariant TaskStatusItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if (role == Qt::DisplayRole || role == Qt::EditRole) { return m_nodemodel.headerData( section, role ); } else if ( role == Qt::TextAlignmentRole ) { return alignment( section ); } } if ( role == Qt::ToolTipRole ) { return m_nodemodel.headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QVariant TaskStatusItemModel::alignment( int column ) const { return m_nodemodel.headerData( column, Qt::TextAlignmentRole ); } QAbstractItemDelegate *TaskStatusItemModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent ); default: return 0; } return 0; } int TaskStatusItemModel::columnCount( const QModelIndex & ) const { return m_nodemodel.propertyCount(); } int TaskStatusItemModel::rowCount( const QModelIndex &parent ) const { if ( ! parent.isValid() ) { //debugPlan<<"top="<count()<count(); } //debugPlan<<"node"<( index.internalPointer() ) ) ) { return static_cast( index.internalPointer() ); } } return 0; } Node *TaskStatusItemModel::node( const QModelIndex &index ) const { if ( index.isValid() ) { foreach ( NodeMap *l, m_top ) { const QList &nodes = l->values(); int row = nodes.indexOf( static_cast( index.internalPointer() ) ); if ( row != -1 ) { return static_cast( index.internalPointer() ); } } } return 0; } TaskStatusItemModel::TaskStatus TaskStatusItemModel::taskStatus(const Task *task, const QDate &begin, const QDate &end) { TaskStatus result = TaskUnknownStatus; const Completion &completion = task->completion(); if (completion.isFinished()) { if (completion.finishTime().date() > begin) { result = TaskFinished; } } else if (completion.isStarted()) { result = TaskRunning; } else if (task->startTime(m_id).date() < m_nodemodel.now()) { // should have been started result = TaskNotStarted; } else if (task->startTime(m_id).date() <= end) { // start next period result = TaskUpcoming; } return result; } void TaskStatusItemModel::slotNodeChanged( Node *node ) { debugPlan; if (node == 0 || node->type() == Node::Type_Project || (node->type() != Node::Type_Task && node->type() != Node::Type_Milestone)) { return; } Task *task = static_cast(node); const QDate begin = m_nodemodel.now().addDays( -m_period ); const QDate end = m_nodemodel.now().addDays( m_period ); const TaskStatus status = taskStatus(task, begin, end); int row = -1; if (status != TaskUnknownStatus) { // find the row of the task const QString wbs = node->wbsCode(); // TODO: not enough to just check the result of indexOf? wbs not unique? if (m_top.at(status)->value(wbs) == node ) { row = m_top.at(status)->keys().indexOf(wbs); } } if (row >= 0) { // task in old group, just changed values emit dataChanged(createIndex(row, 0, node), createIndex(row, columnCount() - 1, node)); } else { // task is new or changed groups refresh(); } } void TaskStatusItemModel::slotWbsDefinitionChanged() { debugPlan; foreach ( NodeMap *l, m_top ) { for ( int row = 0; row < l->count(); ++row ) { const QList &nodes = l->values(); emit dataChanged( createIndex( row, NodeModel::NodeWBSCode, nodes.value( row ) ), createIndex( row, NodeModel::NodeWBSCode, nodes.value( row ) ) ); } } } int TaskStatusItemModel::sortRole( int column ) const { switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: return Qt::EditRole; default: break; } return Qt::DisplayRole; } } // namespace KPlato diff --git a/src/libs/ui/kpttaskprogressdialog.cpp b/src/libs/ui/kpttaskprogressdialog.cpp index 50d034d9..7d57f0c8 100644 --- a/src/libs/ui/kpttaskprogressdialog.cpp +++ b/src/libs/ui/kpttaskprogressdialog.cpp @@ -1,81 +1,104 @@ /* This file is part of the KDE project Copyright (C) 2005-2010 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 "kpttaskprogressdialog.h" #include "kpttaskprogresspanel.h" #include "kptcommand.h" #include "kptproject.h" #include "kpttask.h" #include "kptnode.h" +#include "Help.h" #include namespace KPlato { TaskProgressDialog::TaskProgressDialog(Task &task, ScheduleManager *sm, StandardWorktime *workTime, QWidget *p) : KoDialog( p), m_node( &task ) { setCaption( i18n("Task Progress") ); setButtons( Ok|Cancel ); setDefaultButton( Ok ); showButtonSeparator( true ); m_panel = new TaskProgressPanel(task, sm, workTime, this); setMainWidget(m_panel); enableButtonOk(false); connect(m_panel, &TaskProgressPanelImpl::changed, this, &TaskProgressDialog::slotChanged); Project *proj = static_cast( task.projectNode() ); if ( proj ) { connect(proj, &Project::nodeRemoved, this, &TaskProgressDialog::slotNodeRemoved); } + Help::add(this, + xi18nc("@info:whatsthis", + "Edit Task Progress" + "" + "This dialog consists of the following parts:" + "" + "Edit mode control to define the behaviour of the dialog." + "Controls to mark the task as started and finished." + "Table to enter used effort for each resource." + "Table for entry of task completion and remaining effort." + "" + "Edit modes:" + "" + "Per resource requires you to enter used effort for each resource assigned to this task." + " This enables the most detailed tracking of effort- and cost usage." + "Per task enables you to enter the minimum of information. When completion is changed," + " used effort and remaining effort is automatically calculated, but can of coursey be manually modified if needed." + "" + "You should select the desired edit mode when starting the task." + "More..." + "", Help::page("Manual/Task_Progress_Dialog"))); + } void TaskProgressDialog::slotNodeRemoved( Node *node ) { if ( m_node == node ) { reject(); } } void TaskProgressDialog::slotChanged() { enableButtonOk(true); } MacroCommand *TaskProgressDialog::buildCommand() { MacroCommand *m = new MacroCommand(kundo2_i18n("Modify Task Progress")); bool modified = false; MacroCommand *cmd = m_panel->buildCommand(); if (cmd) { m->addCommand(cmd); modified = true; } if (!modified) { delete m; return 0; } return m; } } //KPlato namespace diff --git a/src/libs/ui/kpttaskprogresspanel.cpp b/src/libs/ui/kpttaskprogresspanel.cpp index 603a7835..3bfd2955 100644 --- a/src/libs/ui/kpttaskprogresspanel.cpp +++ b/src/libs/ui/kpttaskprogresspanel.cpp @@ -1,438 +1,423 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007, 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 "kpttaskprogresspanel.h" #include "kptusedefforteditor.h" #include #include #include #include #include "kpttask.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptresource.h" #include "kptdurationspinbox.h" #include "kptschedule.h" #include "kptproject.h" #include "kptdebug.h" -namespace KPlato -{ +using namespace KPlato; +int toEditMode(Completion::Entrymode m) +{ + const QList modes = QList() << Completion::EnterEffortPerResource << Completion::EnterEffortPerTask << Completion::EnterCompleted; + return qBound(0, modes.indexOf(m), 1); +} +Completion::Entrymode fromEditMode(int m) +{ + const QList modes = QList() << Completion::EnterEffortPerResource << Completion::EnterEffortPerTask; + return modes.value(m); +} //----------------- TaskProgressPanel::TaskProgressPanel( Task &task, ScheduleManager *sm, StandardWorktime *workTime, QWidget *parent ) : TaskProgressPanelImpl( task, parent ) { + Q_UNUSED(workTime); debugPlan; started->setChecked(m_completion.isStarted()); finished->setChecked(m_completion.isFinished()); startTime->setDateTime(m_completion.startTime()); finishTime->setDateTime(m_completion.finishTime()); finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); - - if (workTime) { - debugPlan<<"daylength="<durationDay().hours(); - m_dayLength = workTime->durationDay().hours(); - setEstimateScales(m_dayLength); - } - + scheduledEffort = task.estimate()->expectedValue(); - + setYear( QDate::currentDate().year() ); if ( m_completion.usedEffortMap().isEmpty() || m_task.requests().isEmpty() ) { foreach ( ResourceGroupRequest *g, m_task.requests().requests() ) { foreach ( ResourceRequest *r, g->resourceRequests() ) { m_completion.addUsedEffort( r->resource() ); } } } - if ( m_completion.isStarted() ) { - tabWidget->setCurrentWidget( completionTab ); - } enableWidgets(); started->setFocus(); - + connect( weekNumber, SIGNAL(currentIndexChanged(int)), SLOT(slotWeekNumberChanged(int)) ); - connect( addResource, &QAbstractButton::clicked, this, &TaskProgressPanel::slotAddResource ); + connect(ui_resourceCombo, SIGNAL(activated(QString)), resourceTable->model(), SLOT(addResource(QString))); connect( addEntryBtn, &QAbstractButton::clicked, entryTable, &CompletionEntryEditor::addEntry ); connect( removeEntryBtn, &QAbstractButton::clicked, entryTable, &CompletionEntryEditor::removeEntry ); entryTable->model()->setManager( sm ); entryTable->model()->setTask( &task ); entryTable->setCompletion( &m_completion ); connect( entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanel::slotEntryAdded ); - + resourceTable->setProject( static_cast( task.projectNode() ) ); resourceTable->setCompletion( &m_completion ); slotWeekNumberChanged( weekNumber->currentIndex() ); - addResource->setEnabled( resourceTable->hasFreeResources() ); + updateResourceCombo(); + connect(resourceTable->model(), &UsedEffortItemModel::rowInserted, this, &TaskProgressPanelImpl::updateResourceCombo); //resourceTable->resizeColumnsToContents(); connect(started, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotStartedChanged); connect(started, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotChanged); connect(finished, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotFinishedChanged); connect(finished, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotStartTimeChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotFinishTimeChanged); } MacroCommand *TaskProgressPanel::buildCommand() { Project *project = dynamic_cast( m_task.projectNode() ); if ( project == 0 ) { return 0; } return buildCommand( *project, m_original, m_completion ); } MacroCommand *TaskProgressPanel::buildCommand( const Project &project, Completion &org, Completion &curr ) { MacroCommand *cmd = 0; KUndo2MagicString c = kundo2_i18n("Modify task completion"); if ( org.entrymode() != curr.entrymode() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionEntrymodeCmd(org, curr.entrymode() ) ); } if ( org.startTime() != curr.startTime() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionStartTimeCmd(org, curr.startTime() ) ); } if ( org.finishTime() != curr.finishTime() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionFinishTimeCmd(org, curr.finishTime() ) ); } if ( org.isStarted() != curr.isStarted() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionStartedCmd(org, curr.isStarted() ) ); } if ( org.isFinished() != curr.isFinished() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionFinishedCmd(org, curr.isFinished() ) ); } QList orgdates = org.entries().keys(); QList currdates = curr.entries().keys(); foreach ( const QDate &d, orgdates ) { if ( currdates.contains( d ) ) { if ( curr.entry( d ) == org.entry( d ) ) { continue; } if ( cmd == 0 ) cmd = new MacroCommand( c ); debugPlan<<"modify entry "<addCommand( new ModifyCompletionEntryCmd(org, d, e ) ); } else { if ( cmd == 0 ) cmd = new MacroCommand( c ); debugPlan<<"remove entry "<addCommand( new RemoveCompletionEntryCmd(org, d ) ); } } foreach ( const QDate &d, currdates ) { if ( ! orgdates.contains( d ) ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); Completion::Entry *e = new Completion::Entry( * ( curr.entry( d ) ) ); debugPlan<<"add entry "<addCommand( new AddCompletionEntryCmd(org, d, e ) ); } } const Completion::ResourceUsedEffortMap &map = curr.usedEffortMap(); Completion::ResourceUsedEffortMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { Resource *r = project.findResource(it.key()->id()); if ( r == 0 ) { warnPlan<<"Can't find resource:"<id()<name(); continue; } Completion::UsedEffort *ue = map[ r ]; if ( ue == 0 ) { continue; } if ( org.usedEffort( r ) == 0 || *ue != *(org.usedEffort( r )) ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new AddCompletionUsedEffortCmd( org, r, new Completion::UsedEffort( *ue ) ) ); } } return cmd; } -void TaskProgressPanel::setEstimateScales( int day ) -{ - QVariantList lst; - lst << QVariant( day ); -// remainingEffort->setScales( lst ); -// remainingEffort->setFieldScale(0, day); -// remainingEffort->setFieldRightscale(0, day); -// remainingEffort->setFieldLeftscale(1, day); - -// actualEffort->setScales( QVariant( lst ) ); -/* actualEffort->setFieldScale(0, day); - actualEffort->setFieldRightscale(0, day); - actualEffort->setFieldLeftscale(1, day);*/ -} - void TaskProgressPanel::slotWeekNumberChanged( int index ) { debugPlan<setCurrentMonday( date ); } -void TaskProgressPanel::slotAddResource() -{ - debugPlan; - resourceTable->addResource(); - addResource->setEnabled( resourceTable->hasFreeResources() ); -} - void TaskProgressPanel::slotEntryAdded( const QDate &date ) { debugPlan<setIcon(koIcon("list-add")); - removeEntryBtn->setIcon(koIcon("list-remove")); - connect(entryTable, &CompletionEntryEditor::selectedItemsChanged, this, &TaskProgressPanelImpl::slotSelectionChanged ); removeEntryBtn->setEnabled( false ); - editmode->setCurrentIndex( m_original.entrymode() - 1 ); + editmode->setCurrentIndex(toEditMode(m_original.entrymode())); connect( editmode, SIGNAL(currentIndexChanged(int)), SLOT(slotEditmodeChanged(int)) ); connect( editmode, SIGNAL(activated(int)), SLOT(slotChanged()) ); connect(resourceTable, &UsedEffortEditor::changed, this, &TaskProgressPanelImpl::slotChanged ); connect(resourceTable, &UsedEffortEditor::resourceAdded, this, &TaskProgressPanelImpl::slotChanged ); - + connect(resourceTable->model(), &UsedEffortItemModel::effortChanged, this, &TaskProgressPanelImpl::slotEffortChanged); + connect(entryTable, &CompletionEntryEditor::changed, this, &TaskProgressPanelImpl::slotChanged ); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanelImpl::slotChanged ); connect(entryTable, &CompletionEntryEditor::changed, this, &TaskProgressPanelImpl::slotEntryChanged ); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanelImpl::slotEntryChanged ); connect(entryTable, &CompletionEntryEditor::rowRemoved, this, &TaskProgressPanelImpl::slotEntryChanged ); connect( prevWeekBtn, &QAbstractButton::clicked, this, &TaskProgressPanelImpl::slotPrevWeekBtnClicked ); connect( nextWeekBtn, &QAbstractButton::clicked, this, &TaskProgressPanelImpl::slotNextWeekBtnClicked ); connect ( ui_year, SIGNAL(valueChanged(int)), SLOT(slotFillWeekNumbers(int)) ); int y = 0; int wn = QDate::currentDate().weekNumber( &y ); setYear( y ); weekNumber->setCurrentIndex( wn - m_weekOffset ); } +void TaskProgressPanelImpl::slotEffortChanged(const QDate &date) +{ + if (date.isValid()) { + entryTable->insertEntry(date); + } +} + void TaskProgressPanelImpl::slotChanged() { emit changed(); } void TaskProgressPanelImpl::slotEditmodeChanged( int idx ) { - m_completion.setEntrymode( static_cast( idx + 1 ) ); + m_completion.setEntrymode(fromEditMode(idx)); entryTable->model()->slotDataChanged(); enableWidgets(); } void TaskProgressPanelImpl::slotStartedChanged(bool state) { m_completion.setStarted( state ); if (state) { QTime t = QTime::currentTime(); t.setHMS( t.hour(), t.minute(), 0 ); m_completion.setStartTime( QDateTime(QDate::currentDate(), t, Qt::LocalTime) ); startTime->setDateTime( m_completion.startTime() ); slotCalculateEffort(); } enableWidgets(); } void TaskProgressPanelImpl::setFinished() { QTime t = QTime::currentTime(); t.setHMS( t.hour(), t.minute(), 0 ); finishTime->setDateTime( QDateTime(QDate::currentDate(), t, Qt::LocalTime) ); slotFinishTimeChanged( finishTime->dateTime() ); } void TaskProgressPanelImpl::slotFinishedChanged(bool state) { debugPlan<dateTime(); slotCalculateEffort(); } enableWidgets(); } void TaskProgressPanelImpl::slotFinishTimeChanged( const QDateTime &dt ) { if ( ! m_completion.isFinished() ) { return; } m_completion.setFinishTime( dt ); if ( m_completion.percentFinished() < 100 ) { m_completion.setPercentFinished( dt.date(), 100 ); } entryTable->setCompletion( &m_completion ); // for refresh } void TaskProgressPanelImpl::slotStartTimeChanged( const QDateTime &dt ) { m_completion.setStartTime( dt ); finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); } void TaskProgressPanelImpl::slotEntryChanged() { finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); } +void TaskProgressPanelImpl::updateResourceCombo() +{ + ui_resourceCombo->blockSignals(true); + ui_resourceCombo->clear(); + ui_resourceCombo->addItems(resourceTable->model()->freeResources().keys()); + ui_resourceCombo->blockSignals(false); +} + void TaskProgressPanelImpl::enableWidgets() { editmode->setEnabled( !finished->isChecked() ); started->setEnabled(!finished->isChecked()); finished->setEnabled(started->isChecked()); finishTime->setEnabled(finished->isChecked()); startTime->setEnabled(started->isChecked() && !finished->isChecked()); addEntryBtn->setEnabled( started->isChecked() && !finished->isChecked() ); removeEntryBtn->setEnabled( ! entryTable->selectionModel()->selectedIndexes().isEmpty() && started->isChecked() && ! finished->isChecked() ); - if ( finished->isChecked() ) { - for ( int i = 0; i < entryTable->model()->columnCount(); ++i ) { - entryTable->model()->setFlags( i, Qt::NoItemFlags ); - } - } - - resourceTable->model()->setReadOnly( ( ! started->isChecked() ) || finished->isChecked() || m_completion.entrymode() != Completion::EnterEffortPerResource ); + ui_resourceFrame->setVisible(m_completion.entrymode() == Completion::EnterEffortPerResource); + resourceTable->model()->setReadOnly( ( ! started->isChecked() ) || finished->isChecked() || editmode->currentIndex() != 0 ); } void TaskProgressPanelImpl::slotPercentFinishedChanged( int ) { slotCalculateEffort(); } void TaskProgressPanelImpl::slotCalculateEffort() { } void TaskProgressPanelImpl::slotPrevWeekBtnClicked() { debugPlan; int i = weekNumber->currentIndex(); if ( i == 0 ) { debugPlan<value() - 1 ); if ( m_lastIsNextYear ) { decr = 2; } weekNumber->setCurrentIndex( weekNumber->count() - decr ); } else { weekNumber->setCurrentIndex( i - 1 ); } } void TaskProgressPanelImpl::slotNextWeekBtnClicked() { int i = weekNumber->currentIndex(); debugPlan<count(); if ( i == weekNumber->count() - 1 ) { debugPlan<value() + 1 ); if ( m_firstIsPrevYear ) { index = 1; } weekNumber->setCurrentIndex( index ); } else { weekNumber->setCurrentIndex( i + 1 ); } } void TaskProgressPanelImpl::setYear( int year ) { debugPlan; ui_year->setValue( year ); } void TaskProgressPanelImpl::slotFillWeekNumbers( int year ) { debugPlan; weekNumber->clear(); m_year = year; m_weekOffset = 1; int y = 0; QDate date( year, 1, 1 ); int wn = date.weekNumber( &y ); m_firstIsPrevYear = false; debugPlan<addItem( i18nc( "Week number (year)", "Week %1 (%2)", wn, y ) ); m_weekOffset = 0; m_firstIsPrevYear = true; debugPlan<<"Added last week of prev year"; } for ( int i=1; i <= 52; ++i ) { weekNumber->addItem( i18nc( "Week number", "Week %1", i ) ); } date = QDate( year, 12, 31 ); wn = date.weekNumber( &y ); debugPlan<addItem( i18nc( "Week number", "Week %1", wn ) ); } else if ( wn == 1 ) { weekNumber->addItem( i18nc( "Week number (year)", "Week %1 (%2)", wn, y ) ); m_lastIsNextYear = true; } } void TaskProgressPanelImpl::slotSelectionChanged( const QItemSelection &sel ) { removeEntryBtn->setEnabled( ! sel.isEmpty() && started->isChecked() && ! finished->isChecked() ); } - -} //KPlato namespace diff --git a/src/libs/ui/kpttaskprogresspanel.h b/src/libs/ui/kpttaskprogresspanel.h index f1f15208..34fe666b 100644 --- a/src/libs/ui/kpttaskprogresspanel.h +++ b/src/libs/ui/kpttaskprogresspanel.h @@ -1,107 +1,104 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007 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. */ #ifndef KPTTASKPROGRESSPANEL_H #define KPTTASKPROGRESSPANEL_H #include "planui_export.h" #include "ui_kpttaskprogresspanelbase.h" #include "kpttask.h" #include namespace KPlato { class StandardWorktime; class Duration; class ScheduleManager; class MacroCommand; //------------------------ class TaskProgressPanelImpl : public QWidget, public Ui_TaskProgressPanelBase { Q_OBJECT public: explicit TaskProgressPanelImpl( Task &task, QWidget *parent=0 ); - + void enableWidgets(); - + void setYear( int year ); - + Q_SIGNALS: void changed(); - + public Q_SLOTS: void slotChanged(); void slotEditmodeChanged( int idx ); void slotStartedChanged(bool state); void slotFinishedChanged(bool state); void slotPercentFinishedChanged(int value); void slotStartTimeChanged( const QDateTime &dt ); void slotFinishTimeChanged( const QDateTime &dt ); void slotEntryChanged(); void slotSelectionChanged( const QItemSelection &sel ); void slotPrevWeekBtnClicked(); void slotNextWeekBtnClicked(); - + + void updateResourceCombo(); + void slotEffortChanged(const QDate &date); + protected Q_SLOTS: void slotCalculateEffort(); void slotFillWeekNumbers( int year ); - + protected: void setFinished(); Task &m_task; Completion &m_original; Completion m_completion; int m_dayLength; - + Duration scheduledEffort; int m_weekOffset; int m_year; bool m_firstIsPrevYear; bool m_lastIsNextYear; - }; class PLANUI_EXPORT TaskProgressPanel : public TaskProgressPanelImpl { Q_OBJECT public: explicit TaskProgressPanel( Task &task, ScheduleManager *sm, StandardWorktime *workTime=0, QWidget *parent=0 ); MacroCommand *buildCommand(); - + static MacroCommand *buildCommand( const Project &project, Completion &org, Completion &curr ); protected Q_SLOTS: void slotWeekNumberChanged( int ); - void slotAddResource(); void slotEntryAdded( const QDate &date); - -protected: - void setEstimateScales( int day ); - }; } //KPlato namespace #endif // TASKPROGRESSPANEL_H diff --git a/src/libs/ui/kpttaskprogresspanelbase.ui b/src/libs/ui/kpttaskprogresspanelbase.ui index 16f28651..b72ed3bf 100644 --- a/src/libs/ui/kpttaskprogresspanelbase.ui +++ b/src/libs/ui/kpttaskprogresspanelbase.ui @@ -1,311 +1,297 @@ Dag Andersen <danders@get2net.dk> KPlato::TaskProgressPanelBase 0 0 - 696 - 307 + 887 + 427 - + QFrame::StyledPanel QFrame::Raised Started: true Finished: true Qt::Horizontal 36 20 Edit mode: Per resource Per task + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + 0 + + + 0 + + + 0 + + + 0 + - - Calculate effort - + + + 1900 + + + 2999 + + + + + + + Prev + + + + .. + + + + + + + + + + Next + + + + .. + + + + + + + Qt::Horizontal + + + + 16 + 25 + + + + + + + + Add resource: + + + + + + + + + + + + 0 + 0 + + + + false + - - - 1 + + + QFrame::StyledPanel - - - Completion - - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 + + QFrame::Raised + + + + + + + + Add Entry - - 0 + + + .. - - 0 + + + + + + Remove Entry - - 0 + + + .. - - - - - - Add Entry - - - - - - - Remove Entry - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - false - - - - - - - - - - - Per Resource - - - - 0 - - - 4 - - - 0 - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1900 - - - 2999 - - - - - - - Prev - - - - - - - - - - Next - - - - - - - Qt::Horizontal - - - - 16 - 25 - - - - - - - - Add Resource... - - - - - - - - - false - - - - - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 0 + 100 + + + + false + + + + KComboBox QComboBox
kcombobox.h
CompletionEntryEditor QTableView
kptusedefforteditor.h
UsedEffortEditor QTableView
kptusedefforteditor.h
- tabWidget started startTime finished finishTime addEntryBtn removeEntryBtn - entryTable ui_year prevWeekBtn weekNumber nextWeekBtn - addResource - resourceTable
diff --git a/src/libs/ui/kptusedefforteditor.cpp b/src/libs/ui/kptusedefforteditor.cpp index 19184a3b..5b131d60 100644 --- a/src/libs/ui/kptusedefforteditor.cpp +++ b/src/libs/ui/kptusedefforteditor.cpp @@ -1,877 +1,927 @@ /* This file is part of the KDE project Copyright (C) 2007, 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 "kptusedefforteditor.h" #include "kptitemmodelbase.h" #include #include #include #include #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdebug.h" namespace KPlato { UsedEffortItemModel::UsedEffortItemModel ( QWidget *parent ) : QAbstractItemModel( parent ), m_completion( 0 ), m_readonly( false ) { m_headers << i18n( "Resource" ); QLocale locale; for ( int i = 1; i <= 7; ++i ) { m_headers << locale.dayName( i, QLocale::ShortFormat ); } m_headers << i18n( "This Week" ); } Qt::ItemFlags UsedEffortItemModel::flags ( const QModelIndex &index ) const { - Qt::ItemFlags flags = QAbstractItemModel::flags( index ); if ( m_readonly || ! index.isValid() || index.column() == 8 ) { return flags; } if ( index.column() == 0 ) { const Resource *r = resource( index ); if ( r ) { if ( m_resourcelist.contains( r ) && ! m_completion->usedEffortMap().contains( r ) ) { return flags | Qt::ItemIsEditable; } } return flags; } return flags | Qt::ItemIsEditable; } QVariant UsedEffortItemModel::data ( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: { if ( index.column() == 0 ) { const Resource *r = resource( index ); //debugPlan<name(); } break; } Completion::UsedEffort *ue = usedEffort( index ); if ( ue == 0 ) { return QVariant(); } if ( index.column() == 8 ) { // Total //debugPlan<effort( d ); res += e.normalEffort().toDouble( Duration::Unit_h ); } return QLocale().toString(res, 'f', 1 ); } Completion::UsedEffort::ActualEffort e = ue->effort( m_dates.value( index.column() - 1 ) ); double res = e.normalEffort().toDouble( Duration::Unit_h ); return QLocale().toString(res, 'f', 1 ); } case Qt::EditRole: { if ( index.column() == 8 ) { return QVariant(); } if ( index.column() == 0 ) { const Resource *r = resource( index ); //debugPlan<name(); } } else { Completion::UsedEffort *ue = usedEffort( index ); if ( ue == 0 ) { return QVariant(); } Completion::UsedEffort::ActualEffort e = ue->effort( m_dates.value( index.column() - 1 ) ); double res = e.normalEffort().toDouble( Duration::Unit_h ); return QLocale().toString(res, 'f', 1 ); } break; } case Role::EnumList: { if ( index.column() == 0 ) { QStringList lst = m_editlist.keys(); return lst; } break; } case Role::EnumListValue: { if ( index.column() == 0 ) { return m_editlist.values().indexOf( resource( index ) ); // clazy:exclude=container-anti-pattern } break; } default: break; } return QVariant(); } bool UsedEffortItemModel::setData ( const QModelIndex &idx, const QVariant &value, int role ) { debugPlan; switch ( role ) { case Qt::EditRole: { if ( idx.column() == 8 ) { return false; } if ( idx.column() == 0 ) { const Resource *er = resource( idx ); Q_ASSERT( er != 0 ); Q_ASSERT ( m_editlist.count() > value.toInt() ); const Resource *v = m_editlist.values().value( value.toInt() ); // clazy:exclude=container-anti-pattern Q_ASSERT( v != 0 ); int x = m_resourcelist.indexOf( er ); Q_ASSERT( x != -1 ); m_resourcelist.replace( x, v ); m_completion->addUsedEffort( v ); emit dataChanged( createIndex( idx.row(), 1 ), createIndex( idx.row(), columnCount() - 1 ) ); emit rowInserted( createIndex( idx.row(), 0 ) ); return true; } Completion::UsedEffort *ue = usedEffort( idx ); if ( ue == 0 ) { return false; } QDate d = m_dates.value( idx.column() - 1 ); Completion::UsedEffort::ActualEffort e = ue->effort( d ); e.setNormalEffort( Duration( value.toDouble(), Duration::Unit_h ) ); ue->setEffort( d, e ); + emit effortChanged(d); emit dataChanged( idx, idx ); return true; } default: break; } return false; } bool UsedEffortItemModel::submit() { debugPlan; return QAbstractItemModel::submit(); } void UsedEffortItemModel::revert() { debugPlan; QList lst = m_resourcelist; foreach ( const Resource *r, lst ) { if ( ! m_completion->usedEffortMap().contains( r ) ) { int row = m_resourcelist.indexOf( r ); if ( row != -1 ) { beginRemoveRows( QModelIndex(), row, row ); m_resourcelist.removeAt( row ); endRemoveRows(); } } } } QVariant UsedEffortItemModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Vertical ) { - return QVariant(); + return data(index(section, 0), role); } if ( section < 0 || section >= m_headers.count() ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_headers.at( section ); case Qt::ToolTipRole: { if ( section >= 1 && section <= 7 ) { - return QLocale().toString(m_dates.at( section - 1 ), QLocale::ShortFormat); + return QLocale().toString(m_dates.at( section - 1 ), QLocale::LongFormat); } if ( section == 8 ) { return i18n( "Total effort this week" ); } break; } default: break; } return QVariant(); } int UsedEffortItemModel::columnCount(const QModelIndex & parent ) const { int c = 0; if ( m_completion && ! parent.isValid() ) { c = 9; } return c; } int UsedEffortItemModel::rowCount(const QModelIndex & ) const { int rows = 0; if ( m_completion ) { rows = m_resourcelist.count(); } return rows; } QModelIndex UsedEffortItemModel::index ( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column ); } void UsedEffortItemModel::setCompletion( Completion *completion ) { beginResetModel(); m_completion = completion; m_resourcelist.clear(); QMap lst; const Completion::ResourceUsedEffortMap &map = completion->usedEffortMap(); Completion::ResourceUsedEffortMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { lst.insertMulti(it.key()->name(), it.key()); } m_resourcelist = lst.values(); endResetModel(); } const Resource *UsedEffortItemModel::resource(const QModelIndex &index ) const { int row = index.row(); if ( m_completion == 0 || row < 0 || row >= m_resourcelist.count() ) { return 0; } return m_resourcelist.value( row ); } Completion::UsedEffort *UsedEffortItemModel::usedEffort(const QModelIndex &index ) const { const Resource *r = resource( index ); if ( r == 0 ) { return 0; } return m_completion->usedEffort( r ); } void UsedEffortItemModel::setCurrentMonday( const QDate &date ) { beginResetModel(); m_dates.clear(); for ( int i = 0; i < 7; ++i ) { m_dates << date.addDays( i ); } endResetModel(); emit headerDataChanged ( Qt::Horizontal, 1, 7 ); } QModelIndex UsedEffortItemModel::addRow() { if ( m_project == 0 ) { return QModelIndex(); } m_editlist.clear(); m_editlist = freeResources(); if ( m_editlist.isEmpty() ) { return QModelIndex(); } int row = rowCount(); beginInsertRows( QModelIndex(), row, row ); m_resourcelist.append(m_editlist.first()); endInsertRows(); return createIndex(row, 0, const_cast(m_editlist.first())); } +void UsedEffortItemModel::addResource(const QString &name) +{ + const Resource *resource = freeResources().value(name); + if (!resource) { + return; + } + m_editlist.clear(); + m_editlist.insert(name, resource); + int row = rowCount(); + beginInsertRows(QModelIndex(), row, row); + m_resourcelist.append(resource); + endInsertRows(); + setData(createIndex(row, 0, const_cast(resource)), 0, Qt::EditRole); +} + QMap UsedEffortItemModel::freeResources() const { QMap map; foreach ( Resource *r, m_project->resourceList() ) { if ( ! m_resourcelist.contains( r ) ) { map.insertMulti( r->name(), r ); } } return map; } //----------- UsedEffortEditor::UsedEffortEditor( QWidget *parent ) : QTableView( parent ) { UsedEffortItemModel *m = new UsedEffortItemModel(this ); setModel( m ); - setItemDelegateForColumn ( 0, new EnumDelegate( this ) ); - setItemDelegateForColumn ( 1, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 2, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 3, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 4, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 5, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 6, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 7, new DoubleSpinBoxDelegate( this ) ); connect ( model(), &QAbstractItemModel::dataChanged, this, &UsedEffortEditor::changed ); connect ( m, &UsedEffortItemModel::rowInserted, this, &UsedEffortEditor::resourceAdded ); + connect (m, &UsedEffortItemModel::rowInserted, this, &UsedEffortEditor::scrollToBottom); } bool UsedEffortEditor::hasFreeResources() const { - return ! static_cast( model() )->freeResources().isEmpty(); + return ! model()->freeResources().isEmpty(); } void UsedEffortEditor::setProject( Project *p ) { - static_cast( model() )->setProject( p ); + model()->setProject( p ); } void UsedEffortEditor::setCompletion( Completion *completion ) { - static_cast( model() )->setCompletion( completion ); + model()->setCompletion(completion); + setColumnHidden(0, true); } void UsedEffortEditor::setCurrentMonday( const QDate &date ) { static_cast( model() )->setCurrentMonday( date ); } void UsedEffortEditor::addResource() { - UsedEffortItemModel *m = static_cast( model() ); + UsedEffortItemModel *m = model(); QModelIndex i = m->addRow(); if ( i.isValid() ) { setCurrentIndex( i ); edit( i ); } } //---------------------------------------- CompletionEntryItemModel::CompletionEntryItemModel ( QObject *parent ) : QAbstractItemModel( parent ), m_node( 0 ), m_project( 0 ), m_manager( 0 ), m_completion( 0 ) { m_headers << i18n( "Date" ) // xgettext: no-c-format << i18n( "% Completed" ) << i18n( "Used Effort" ) << i18n( "Remaining Effort" ) << i18n( "Planned Effort" ); m_flags.insert( Property_Date, Qt::NoItemFlags ); m_flags.insert( Property_Completion, Qt::ItemIsEditable ); m_flags.insert( Property_UsedEffort, Qt::NoItemFlags ); m_flags.insert( Property_RemainingEffort, Qt::ItemIsEditable ); m_flags.insert( Property_PlannedEffort, Qt::NoItemFlags ); } void CompletionEntryItemModel::setTask( Task *t ) { m_node = t; m_project = 0; if ( m_node && m_node->projectNode() ) { m_project = static_cast( m_node->projectNode() ); } } void CompletionEntryItemModel::slotDataChanged() { refresh(); } void CompletionEntryItemModel::setManager( ScheduleManager *sm ) { m_manager = sm; refresh(); } Qt::ItemFlags CompletionEntryItemModel::flags ( const QModelIndex &index ) const { if ( index.isValid() && index.column() < m_flags.count() ) { return QAbstractItemModel::flags( index ) | m_flags[ index.column() ]; } return QAbstractItemModel::flags( index ); } QVariant CompletionEntryItemModel::date ( int row, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return m_datelist.value( row ); default: break; } return QVariant(); } QVariant CompletionEntryItemModel::percentFinished ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return e->percentFinished; default: break; } return QVariant(); } QVariant CompletionEntryItemModel::remainingEffort ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: { return e->remainingEffort.format(); } case Qt::EditRole: return e->remainingEffort.toDouble( Duration::Unit_h ); case Role::DurationScales: { QVariantList lst; // TODO: week if ( m_node && m_project ) { if ( m_node->estimate()->type() == Estimate::Type_Effort ) { lst.append( m_project->standardWorktime()->day() ); } } if ( lst.isEmpty() ) { lst.append( 24.0 ); } lst << 60.0 << 60.0 << 1000.0; return lst; } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant CompletionEntryItemModel::actualEffort ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { - case Qt::DisplayRole: - case Qt::ToolTipRole: { + case Qt::DisplayRole: { Duration v; if ( m_completion->entrymode() == Completion::EnterEffortPerResource ) { v = m_completion->actualEffortTo( date( row ).toDate() ); } else { v = e->totalPerformed; } //debugPlan<name()<<": "<totalPerformed.toDouble( Duration::Unit_h ); case Role::DurationScales: { QVariantList lst; // TODO: week if ( m_node && m_project ) { if ( m_node->estimate()->type() == Estimate::Type_Effort ) { lst.append( m_project->standardWorktime()->day() ); } } if ( lst.isEmpty() ) { lst.append( 24 ); } lst << 60 << 60 << 1000; return lst; } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); + case Qt::ToolTipRole: + return xi18nc("@info:tooltip", "Accumulated effort %1", actualEffort(row, Qt::DisplayRole).toString()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant CompletionEntryItemModel::plannedEffort ( int /*row*/, int role ) const { if ( m_node == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: { Duration v = m_node->plannedEffort( id(), ECCT_EffortWork ); //debugPlan<name()<<": "<estimate()->type() == Estimate::Type_Effort ) { lst.append( m_project->standardWorktime()->day() ); } } if ( lst.isEmpty() ) { lst.append( 24.0 ); } lst << 60.0 << 60.0 << 1000.0; return lst; } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant CompletionEntryItemModel::data ( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } switch ( index.column() ) { case Property_Date: return date( index.row(), role ); case Property_Completion: return percentFinished( index.row(), role ); case Property_UsedEffort: return actualEffort( index.row(), role ); case Property_RemainingEffort: return remainingEffort( index.row(), role ); case Property_PlannedEffort: return plannedEffort( index.row(), role ); default: break; } return QVariant(); } QList CompletionEntryItemModel::scales() const { QList lst; if ( m_node && m_project ) { if ( m_node->estimate()->type() == Estimate::Type_Effort ) { lst = m_project->standardWorktime()->scales(); } } if ( lst.isEmpty() ) { lst = Estimate::defaultScales(); } //debugPlan<entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } e->percentFinished = value.toInt(); - if ( m_completion->entrymode() == Completion::EnterCompleted && m_node ) { + if ( m_completion->entrymode() != Completion::EnterEffortPerResource && m_node ) { // calculate used/remaining Duration est = m_node->plannedEffort( id(), ECCT_EffortWork ); e->totalPerformed = est * e->percentFinished / 100; e->remainingEffort = est - e->totalPerformed; } emit dataChanged( idx, createIndex( idx.row(), 3 ) ); return true; } if ( idx.column() == Property_UsedEffort ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } double v( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration d = Estimate::scale( v, unit, scales() ); if ( d == e->totalPerformed ) { return false; } e->totalPerformed = d; emit dataChanged( idx, idx ); return true; } if ( idx.column() == Property_RemainingEffort ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } double v( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration d = Estimate::scale( v, unit, scales() ); if ( d == e->remainingEffort ) { return false; } e->remainingEffort = d; emit dataChanged( idx, idx ); return true; } } default: break; } return false; } bool CompletionEntryItemModel::submit() { debugPlan<= m_headers.count() ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_headers.at( section ); + case Qt::ToolTipRole: + switch (section) { + case Property_UsedEffort: + if ( m_completion->entrymode() == Completion::EnterEffortPerResource ) { + return xi18nc("@info", "Accumulated used effort.Used effort must be entered per resource"); + } + return xi18nc("@info:tooltip", "Accumulated used effort"); + case Property_RemainingEffort: + return xi18nc("@info:tooltip", "Remaining effort to complete the task"); + case Property_PlannedEffort: + return xi18nc("@info:tooltip", "Planned effort accumulated until date"); + } + break; default: break; } return QVariant(); } int CompletionEntryItemModel::columnCount(const QModelIndex & /*parent */) const { return 5; } int CompletionEntryItemModel::rowCount(const QModelIndex &idx ) const { if ( m_completion == 0 || idx.isValid() ) { return 0; } return m_datelist.count(); } QModelIndex CompletionEntryItemModel::index ( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column ); } void CompletionEntryItemModel::setCompletion( Completion *completion ) { m_completion = completion; refresh(); } void CompletionEntryItemModel::refresh() { beginResetModel(); m_datelist.clear(); m_flags[ Property_UsedEffort ] = Qt::NoItemFlags; if ( m_completion ) { m_datelist = m_completion->entries().keys(); - if ( m_completion->entrymode() == Completion::EnterEffortPerTask ) { + if ( m_completion->entrymode() != Completion::EnterEffortPerResource ) { m_flags[ Property_UsedEffort ] = Qt::ItemIsEditable; } } - debugPlan< 0 && d <= m_datelist.last() ) { d = m_datelist.last().addDays( 1 ); } beginInsertRows( QModelIndex(), row, row ); m_datelist.append( d ); endInsertRows(); return createIndex( row, 0 ); } void CompletionEntryItemModel::removeEntry( const QDate& date ) { removeRow( m_datelist.indexOf( date ) ); } void CompletionEntryItemModel::removeRow( int row ) { debugPlan<= rowCount() ) { return; } QDate date = m_datelist.value( row ); beginRemoveRows( QModelIndex(), row, row ); m_datelist.removeAt( row ); endRemoveRows(); debugPlan<takeEntry( date ); emit rowRemoved( date ); emit changed(); } void CompletionEntryItemModel::addEntry( const QDate& date ) { debugPlan<entries().isEmpty() ) { if ( m_node ) { e->remainingEffort = m_node->plannedEffort( id(), ECCT_EffortWork ); } } else { e->percentFinished = m_completion->percentFinished(); e->totalPerformed = m_completion->actualEffort(); e->remainingEffort = m_completion->remainingEffort(); } m_completion->addEntry( date, e ); refresh(); int i = m_datelist.indexOf( date ); if ( i != -1 ) { emit rowInserted( date ); emit dataChanged( createIndex( i, 1 ), createIndex( i, rowCount() - 1 ) ); } else errorPlan<<"Failed to find added entry: "<date(i).toDate() == date) { + const QModelIndex idx1 = index(i, Property_UsedEffort); + const QModelIndex idx2 = index(rowCount()-1, Property_UsedEffort); + emit dataChanged(idx1, idx2); + return; + } + } + addEntry(date); +} //----------- CompletionEntryEditor::CompletionEntryEditor( QWidget *parent ) : QTableView( parent ) { verticalHeader()->hide(); CompletionEntryItemModel *m = new CompletionEntryItemModel(this ); setItemDelegateForColumn ( 1, new ProgressBarDelegate( this ) ); setItemDelegateForColumn ( 2, new DurationSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 3, new DurationSpinBoxDelegate( this ) ); setCompletionModel( m ); + resizeColumnToContents(1); + resizeColumnToContents(2); + resizeColumnToContents(3); + resizeColumnToContents(4); } void CompletionEntryEditor::setCompletionModel( CompletionEntryItemModel *m ) { if ( model() ) { disconnect(model(), &CompletionEntryItemModel::rowInserted, this, &CompletionEntryEditor::rowInserted); disconnect(model(), &CompletionEntryItemModel::rowRemoved, this, &CompletionEntryEditor::rowRemoved); disconnect(model(), &QAbstractItemModel::dataChanged, this, &CompletionEntryEditor::changed); disconnect(model(), &CompletionEntryItemModel::changed, this, &CompletionEntryEditor::changed); disconnect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &CompletionEntryEditor::selectedItemsChanged); } setModel( m ); if ( model() ) { connect(model(), &CompletionEntryItemModel::rowInserted, this, &CompletionEntryEditor::rowInserted); connect(model(), &CompletionEntryItemModel::rowRemoved, this, &CompletionEntryEditor::rowRemoved); connect(model(), &QAbstractItemModel::dataChanged, this, &CompletionEntryEditor::changed); connect(model(), &CompletionEntryItemModel::changed, this, &CompletionEntryEditor::changed); connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &CompletionEntryEditor::selectedItemsChanged); } } void CompletionEntryEditor::setCompletion( Completion *completion ) { model()->setCompletion( completion ); } +void CompletionEntryEditor::insertEntry(const QDate &date) +{ + model()->addRow(date); +} + void CompletionEntryEditor::addEntry() { debugPlan<addRow(); if ( i.isValid() ) { model()->setFlags( i.column(), Qt::ItemIsEditable ); setCurrentIndex( i ); emit selectedItemsChanged(QItemSelection(), QItemSelection()); //hmmm, control removeEntryBtn scrollTo( i ); edit( i ); model()->setFlags( i.column(), Qt::NoItemFlags ); } } void CompletionEntryEditor::removeEntry() { //debugPlan; QModelIndexList lst = selectedIndexes(); debugPlan< rows; while ( ! lst.isEmpty() ) { QModelIndex idx = lst.takeFirst(); rows[ idx.row() ] = 0; } QList r = rows.uniqueKeys(); debugPlan<= 0; --i ) { model()->removeRow( r.at( i ) ); } } } //KPlato namespace diff --git a/src/libs/ui/kptusedefforteditor.h b/src/libs/ui/kptusedefforteditor.h index 330d3775..12c7f031 100644 --- a/src/libs/ui/kptusedefforteditor.h +++ b/src/libs/ui/kptusedefforteditor.h @@ -1,207 +1,211 @@ /* This file is part of the KDE project Copyright (C) 2007, 2011 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. */ #ifndef KPTUSEDEFFORTEDITOR_H #define KPTUSEDEFFORTEDITOR_H #include "planui_export.h" #include #include #include "kpttask.h" +class QDate; namespace KPlato { class Completion; class Resource; class Project; class PLANUI_EXPORT UsedEffortItemModel : public QAbstractItemModel { Q_OBJECT public: explicit UsedEffortItemModel(QWidget *parent = 0); void setProject( Project *project ) { m_project = project; } virtual Qt::ItemFlags flags( const QModelIndex & index ) const; virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex &index, const QVariant & value, int role = Qt::EditRole ); virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; virtual QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; void setCompletion( Completion *completion ); const Resource *resource(const QModelIndex &index ) const; Completion::UsedEffort *usedEffort(const QModelIndex &index ) const; void setCurrentMonday( const QDate &date ); QModelIndex addRow(); QMap freeResources() const; void setReadOnly( bool ro ) { m_readonly = ro; } bool readOnly() const { return m_readonly; } Q_SIGNALS: void rowInserted( const QModelIndex& ); void changed(); + void effortChanged(const QDate &date); public Q_SLOTS: bool submit(); void revert(); + void addResource(const QString &name); private: Project *m_project; Completion *m_completion; QList m_dates; QStringList m_headers; QList m_resourcelist; QMap m_editlist; bool m_readonly; }; class PLANUI_EXPORT UsedEffortEditor : public QTableView { Q_OBJECT public: explicit UsedEffortEditor(QWidget *parent); void setProject( Project *project ); void setCompletion( Completion *completion ); void setCurrentMonday( const QDate &date ); void addResource(); bool hasFreeResources() const; UsedEffortItemModel *model() const { return static_cast( QTableView::model() ); } Q_SIGNALS: void changed(); void resourceAdded(); - -private: - }; //-------------------------------------------- class PLANUI_EXPORT CompletionEntryItemModel : public QAbstractItemModel { Q_OBJECT public: enum Properties { Property_Date, /// Date of entry Property_Completion, /// % Completed Property_UsedEffort, /// Used Effort Property_RemainingEffort, /// Remaining Effort Property_PlannedEffort /// Planned Effort }; explicit CompletionEntryItemModel(QObject *parent = 0); void setTask( Task *t ); virtual Qt::ItemFlags flags( const QModelIndex & index ) const; virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex &index, const QVariant & value, int role = Qt::EditRole ); virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; virtual QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; void setCompletion( Completion *completion ); const Resource *resource(const QModelIndex &index ) const; Completion::UsedEffort *usedEffort(const QModelIndex &index ) const; void setCurrentMonday( const QDate &date ); QModelIndex addRow(); void removeRow( int row ); /// These flags are in addition to flags return from QAbstractItemModel::flags() void setFlags( int col, Qt::ItemFlags flags ) { m_flags[ col ] = flags; } long id() const { return m_manager == 0 ? -1 : m_manager->scheduleId(); } + void addRow(const QDate &date); + Q_SIGNALS: void rowInserted( const QDate& ); void rowRemoved( const QDate& ); void changed(); public Q_SLOTS: bool submit(); void revert(); void slotDataChanged(); void setManager(KPlato::ScheduleManager *sm); protected: QVariant date ( int row, int role = Qt::DisplayRole ) const; QVariant percentFinished ( int row, int role ) const; QVariant remainingEffort ( int row, int role ) const; virtual QVariant actualEffort ( int row, int role ) const; QVariant plannedEffort ( int row, int role ) const; void removeEntry( const QDate& date ); void addEntry( const QDate& date ); void refresh(); QList scales() const; protected: Task *m_node; Project *m_project; ScheduleManager *m_manager; Completion *m_completion; QList m_dates; QStringList m_headers; QList m_datelist; QList m_flags; }; class PLANUI_EXPORT CompletionEntryEditor : public QTableView { Q_OBJECT public: explicit CompletionEntryEditor(QWidget *parent); void setCompletion( Completion *completion ); CompletionEntryItemModel *model() const { return static_cast( QTableView::model() ); } void setCompletionModel( CompletionEntryItemModel *m ); + void insertEntry(const QDate &date); + Q_SIGNALS: void changed(); void rowInserted( const QDate& ); void rowRemoved( const QDate& ); void selectedItemsChanged(const QItemSelection&, const QItemSelection&); public Q_SLOTS: void addEntry(); void removeEntry(); private: }; } //KPlato namespace #endif // KPTUSEDEFFORTEDITOR_H diff --git a/src/workpackage/taskcompletiondialog.cpp b/src/workpackage/taskcompletiondialog.cpp index 081809e1..76704b36 100644 --- a/src/workpackage/taskcompletiondialog.cpp +++ b/src/workpackage/taskcompletiondialog.cpp @@ -1,484 +1,490 @@ /* 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 "taskcompletiondialog.h" #include "workpackage.h" #include "kptusedefforteditor.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include #include #include #include "debugarea.h" using namespace KPlato; namespace KPlatoWork { TaskCompletionDialog::TaskCompletionDialog(WorkPackage &p, ScheduleManager *sm, QWidget *parent) : KoDialog(parent) { - setCaption( i18n("Task Progress") ); + setCaption(i18n("Task Progress") ); setButtons( Ok|Cancel ); setDefaultButton( Ok ); showButtonSeparator( true ); m_panel = new TaskCompletionPanel( p, sm, this); setMainWidget(m_panel); enableButtonOk(false); connect(m_panel, &TaskCompletionPanel::changed, this, &TaskCompletionDialog::slotChanged); } void TaskCompletionDialog::slotChanged( bool ) { enableButtonOk( true ); } KUndo2Command *TaskCompletionDialog::buildCommand() { //debugPlanWork; return m_panel->buildCommand(); } TaskCompletionPanel::TaskCompletionPanel(WorkPackage &p, ScheduleManager *sm, QWidget *parent) : QWidget(parent), m_package( &p ) { //debugPlanWork; setupUi(this); addEntryBtn->setIcon(koIcon("list-add")); removeEntryBtn->setIcon(koIcon("list-remove")); CompletionEntryItemModel *m = new CompletionEntryItemModel( this ); entryTable->setItemDelegateForColumn ( 1, new ProgressBarDelegate( this ) ); entryTable->setItemDelegateForColumn ( 2, new DurationSpinBoxDelegate( this ) ); entryTable->setItemDelegateForColumn ( 3, new DurationSpinBoxDelegate( this ) ); entryTable->setCompletionModel( m ); Task *task = qobject_cast( p.node() ); m_completion = task->completion(); started->setChecked(m_completion.isStarted()); finished->setChecked(m_completion.isFinished()); startTime->setDateTime(m_completion.startTime()); finishTime->setDateTime(m_completion.finishTime()); finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); scheduledEffort = p.node()->estimate()->expectedValue(); if ( m_completion.usedEffortMap().isEmpty() || task->requests().isEmpty() ) { foreach ( ResourceGroupRequest *g, task->requests().requests() ) { foreach ( ResourceRequest *r, g->resourceRequests() ) { m_completion.addUsedEffort( r->resource() ); } } } enableWidgets(); started->setFocus(); m->setManager( sm ); m->setTask( task ); Resource *r = p.project()->findResource( task->workPackage().ownerId() ); m->setSource( r, task, &m_completion ); entryTable->horizontalHeader()->swapSections( CompletionEntryItemModel::Property_PlannedEffort, CompletionEntryItemModel::Property_ActualAccumulated ); - //FIXME when string freeze is lifted Duration pr = task->plannedEffort( r ); Duration tr = task->plannedEffort(); - if ( pr == tr ) { - ui_plannedFrame->hide(); - } else { - ui_plannedLabel->setText( m->headerData( CompletionEntryItemModel::Property_PlannedEffort, Qt::Horizontal ).toString() ); - ui_labelResource->setText( r->name() ); - ui_plannedResource->setText( pr.format() ); - - ui_labelTask->setText( Node::typeToString( Node::Type_Task, true ) ); - ui_plannedTask->setText( tr.format() ); - } + ui_plannedLabel->setText(i18n("Planned effort for %1: %2. Total planned effort for task: %3", r->name(), pr.format(), tr.format())); - if ( m->rowCount() > 0 ) { - QModelIndex idx = m->index( m->rowCount() -1, 0 ); - entryTable->scrollTo( idx ); - } + entryTable->scrollToBottom(); + entryTable->resizeColumnsToContents(); connect( addEntryBtn, &QAbstractButton::clicked, this, &TaskCompletionPanel::slotAddEntry ); connect( removeEntryBtn, &QAbstractButton::clicked, entryTable, &KPlato::CompletionEntryEditor::removeEntry ); connect( entryTable, &KPlato::CompletionEntryEditor::rowInserted, this, &TaskCompletionPanel::slotEntryAdded ); connect(entryTable, &KPlato::CompletionEntryEditor::changed, this, &TaskCompletionPanel::slotChanged ); connect(entryTable, &KPlato::CompletionEntryEditor::changed, this, &TaskCompletionPanel::slotEntryChanged ); connect(entryTable, &KPlato::CompletionEntryEditor::rowInserted, this, &TaskCompletionPanel::slotChanged ); connect(entryTable, &KPlato::CompletionEntryEditor::rowInserted, this, &TaskCompletionPanel::slotEntryChanged ); connect(entryTable, &KPlato::CompletionEntryEditor::rowRemoved, this, &TaskCompletionPanel::slotEntryChanged ); connect(entryTable, &KPlato::CompletionEntryEditor::selectedItemsChanged, this, &TaskCompletionPanel::slotSelectionChanged ); connect(started, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotStartedChanged); connect(started, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotChanged); connect(finished, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotFinishedChanged); connect(finished, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotStartTimeChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotFinishTimeChanged); removeEntryBtn->setEnabled( false ); } QSize TaskCompletionPanel::sizeHint() const { return QWidget::sizeHint().expandedTo( QSize( 610, 0 ) ); } KUndo2Command *TaskCompletionPanel::buildCommand() { MacroCommand *cmd = new MacroCommand( kundo2_i18n("Modify task completion") ); Completion &org = m_package->task()->completion(); if ( org.entrymode() != m_completion.entrymode() ) { cmd->addCommand( new ModifyCompletionEntrymodeCmd(org, m_completion.entrymode() ) ); } if ( org.isStarted() != m_completion.isStarted() ) { cmd->addCommand( new ModifyCompletionStartedCmd(org, m_completion.isStarted() ) ); } if ( org.isFinished() != m_completion.isFinished() ) { cmd->addCommand( new ModifyCompletionFinishedCmd(org, m_completion.isFinished() ) ); } if ( org.startTime() != m_completion.startTime() ) { cmd->addCommand( new ModifyCompletionStartTimeCmd(org, m_completion.startTime() ) ); } if ( org.finishTime() != m_completion.finishTime() ) { cmd->addCommand( new ModifyCompletionFinishTimeCmd(org, m_completion.finishTime() ) ); } QList orgdates = org.entries().keys(); QList m_completiondates = m_completion.entries().keys(); foreach ( const QDate &d, orgdates ) { if ( m_completiondates.contains( d ) ) { if ( m_completion.entry( d ) == org.entry( d ) ) { continue; } Completion::Entry *e = new Completion::Entry( *( m_completion.entry( d ) ) ); cmd->addCommand( new ModifyCompletionEntryCmd(org, d, e ) ); } else { cmd->addCommand( new RemoveCompletionEntryCmd(org, d ) ); } } foreach ( const QDate &d, m_completiondates ) { if ( ! orgdates.contains( d ) ) { Completion::Entry *e = new Completion::Entry( * ( m_completion.entry( d ) ) ); cmd->addCommand( new AddCompletionEntryCmd(org, d, e ) ); } } if ( cmd->isEmpty() ) { delete cmd; return 0; } return cmd; } void TaskCompletionPanel::slotChanged() { emit changed( true ); //FIXME } void TaskCompletionPanel::slotStartedChanged(bool state) { m_completion.setStarted( state ); if (state) { m_completion.setStartTime( QDateTime::currentDateTime() ); startTime->setDateTime( m_completion.startTime() ); slotCalculateEffort(); } enableWidgets(); } void TaskCompletionPanel::setFinished() { finishTime->setDateTime( QDateTime::currentDateTime() ); slotFinishTimeChanged( finishTime->dateTime() ); } void TaskCompletionPanel::slotFinishedChanged(bool state) { debugPlanWork<node()->plannedEffort() ); m_completion.addEntry( m_completion.finishTime().date(), e ); entryTable->setCompletion( &m_completion ); debugPlanWork<<"Entry added:"<model()->rowCount() - 1; QModelIndex idx = entryTable->model()->index( row, CompletionEntryItemModel::Property_Completion ); entryTable->model()->setData( idx, 100 ); } } enableWidgets(); } void TaskCompletionPanel::slotFinishTimeChanged( const QDateTime &dt ) { m_completion.setFinishTime( dt ); } void TaskCompletionPanel::slotStartTimeChanged( const QDateTime &dt ) { m_completion.setStartTime( dt ); finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); } void TaskCompletionPanel::slotAddEntry() { CompletionEntryItemModel *m = static_cast( entryTable->model() ); int col = KPlato::CompletionEntryItemModel::Property_UsedEffort; entryTable->addEntry(); m_completion.setEntrymode( Completion::EnterEffortPerTask ); m->setFlags( col, Qt::ItemIsEditable ); } void TaskCompletionPanel::slotEntryChanged() { finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); if ( ! finished->isChecked() && ! m_completion.isFinished() && m_completion.percentFinished() == 100 ) { finished->setChecked( true ); } } void TaskCompletionPanel::enableWidgets() { started->setEnabled(!finished->isChecked()); finished->setEnabled(started->isChecked()); finishTime->setEnabled(finished->isChecked()); startTime->setEnabled(started->isChecked() && !finished->isChecked()); } void TaskCompletionPanel::slotPercentFinishedChanged( int ) { slotCalculateEffort(); } void TaskCompletionPanel::slotCalculateEffort() { } void TaskCompletionPanel::slotEntryAdded( const QDate& date ) { debugPlanWork<setEnabled( !sel.isEmpty() ); } void TaskCompletionPanel::slotEditmodeChanged( int index ) { Q_UNUSED( index ); } //------------------- CompletionEntryItemModel::CompletionEntryItemModel( QObject *parent ) : KPlato::CompletionEntryItemModel( parent ), m_calculate( false ), m_resource( 0 ), m_task( 0 ) { m_headers << i18nc("@title:column", "Total"); } void CompletionEntryItemModel::setSource( Resource *resource, Task *task, Completion *completion ) { m_resource = resource; m_task = task; setCompletion( completion ? completion : &(task->completion()) ); } +QVariant CompletionEntryItemModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + switch (role) { + case Qt::ToolTipRole: + switch (section) { + case Property_UsedEffort: + return xi18nc("@info:tooltip", "Used effort since previous entry"); + case Property_ActualAccumulated: + return xi18nc("@info:tooltip", "Accumulated used effort"); + default: break; + } + default: break; + } + } + return KPlato::CompletionEntryItemModel::headerData(section, orientation, role); +} + int CompletionEntryItemModel::columnCount( const QModelIndex& ) const { return 6; } QVariant CompletionEntryItemModel::actualEffort ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: { Duration v = e->totalPerformed; if ( row > 0 ) { v -= m_completion->entry( date( row - 1 ).toDate() )->totalPerformed; } //debugPlanWork<name()<<": "<totalPerformed; if ( row > 0 ) { v -= m_completion->entry( date( row - 1 ).toDate() )->totalPerformed; } //debugPlanWork<name()<<": "<( Duration::Unit_h ); case Role::Minimum: return static_cast( Duration::Unit_h ); case Role::Maximum: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); default: break; } return QVariant(); } QVariant CompletionEntryItemModel::data( const QModelIndex &idx, int role ) const { if ( idx.column() == Property_PlannedEffort && m_resource ) { switch ( role ) { case Qt::DisplayRole: { Duration v = m_task->plannedEffortTo( m_resource, date( idx.row() ).toDate() ); return v.format(); } default: return QVariant(); } } else if ( idx.column() == Property_ActualAccumulated ) { switch ( role ) { case Qt::DisplayRole: { Duration v; Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e ) { v = e->totalPerformed; } return v.format(); } default: return QVariant(); } } return KPlato::CompletionEntryItemModel::data( idx, role ); } bool CompletionEntryItemModel::setData( const QModelIndex &idx, const QVariant &value, int role ) { //debugPlanWork; switch ( role ) { case Qt::EditRole: { if ( idx.column() == Property_Date ) { QDate od = date( idx.row() ).toDate(); removeEntry( od ); addEntry( value.toDate() ); // emit dataChanged( idx, idx ); m_calculate = true; return true; } if ( idx.column() == Property_Completion ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } e->percentFinished = value.toInt(); if ( m_calculate && m_node && idx.row() == rowCount() - 1 ) { // calculate used/remaining Duration est = m_node->plannedEffort( id(), ECCT_EffortWork ); e->totalPerformed = est * e->percentFinished / 100; e->remainingEffort = est - e->totalPerformed; } else if ( e->percentFinished == 100 && e->remainingEffort != 0 ) { e->remainingEffort = Duration::zeroDuration; } emit dataChanged( idx, createIndex( idx.row(), 3 ) ); return true; } if ( idx.column() == Property_ActualEffort ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } m_calculate = false; Duration prev; if ( idx.row() > 0 ) { Completion::Entry *pe = m_completion->entry( date( idx.row() - 1 ).toDate() ); if ( pe ) { prev = pe->totalPerformed; } } double v( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration d = Estimate::scale( v, unit, scales() ); if ( d + prev == e->totalPerformed ) { return false; } e->totalPerformed = d + prev; emit dataChanged( idx, idx ); return true; } if ( idx.column() == Property_RemainigEffort ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } m_calculate = false; double v( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration d = Estimate::scale( v, unit, scales() ); if ( d == e->remainingEffort ) { return false; } e->remainingEffort = d; debugPlanWork<remainingEffort.format(); emit dataChanged( idx, idx ); return true; } break; } default: break; } return false; } } //KPlatoWork namespace diff --git a/src/workpackage/taskcompletiondialog.h b/src/workpackage/taskcompletiondialog.h index 31ccffb0..9e9a0046 100644 --- a/src/workpackage/taskcompletiondialog.h +++ b/src/workpackage/taskcompletiondialog.h @@ -1,136 +1,137 @@ /* This file is part of the KDE project Copyright (C) 2009, 2011 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. */ #ifndef KPLATOWORK_TASKCOMPLETIONDIALOG_H #define KPLATOWORK_TASKCOMPLETIONDIALOG_H #include "planwork_export.h" #include "ui_taskcompletionpanel.h" #include "workpackage.h" #include "kptusedefforteditor.h" #include #include class KUndo2Command; namespace KPlato { class ScheduleManager; } namespace KPlatoWork { class TaskCompletionPanel; class PLANWORK_EXPORT TaskCompletionDialog : public KoDialog { Q_OBJECT public: explicit TaskCompletionDialog( WorkPackage &p, ScheduleManager *sm, QWidget *parent=0 ); KUndo2Command *buildCommand(); protected Q_SLOTS: void slotChanged( bool ); private: TaskCompletionPanel *m_panel; }; class PLANWORK_EXPORT TaskCompletionPanel : public QWidget, public Ui::TaskCompletionPanel { Q_OBJECT public: explicit TaskCompletionPanel( WorkPackage &p, ScheduleManager *sm, QWidget *parent=0 ); KUndo2Command *buildCommand(); void enableWidgets(); QSize sizeHint() const; Q_SIGNALS: void changed( bool ); public Q_SLOTS: void slotChanged(); void slotStartedChanged(bool state); void slotFinishedChanged(bool state); void slotPercentFinishedChanged(int value); void slotStartTimeChanged( const QDateTime &dt ); void slotFinishTimeChanged( const QDateTime &dt ); void slotAddEntry(); void slotEntryChanged(); void slotSelectionChanged( const QItemSelection &sel ); void slotEntryAdded( const QDate& date ); void slotEditmodeChanged( int ); protected Q_SLOTS: void slotCalculateEffort(); protected: void setFinished(); WorkPackage *m_package; Completion m_completion; int m_dayLength; Duration scheduledEffort; }; class CompletionEntryItemModel : public KPlato::CompletionEntryItemModel { Q_OBJECT public: enum Properties { Property_Date = KPlato::CompletionEntryItemModel::Property_Date, Property_Completion = KPlato::CompletionEntryItemModel::Property_Completion, Property_ActualEffort = KPlato::CompletionEntryItemModel::Property_UsedEffort, Property_RemainigEffort = KPlato::CompletionEntryItemModel::Property_RemainingEffort, Property_PlannedEffort = KPlato::CompletionEntryItemModel::Property_PlannedEffort, Property_ActualAccumulated }; explicit CompletionEntryItemModel(QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; int columnCount( const QModelIndex &idx = QModelIndex() ) const; QVariant data( const QModelIndex &idx, int role ) const; bool setData( const QModelIndex &idx, const QVariant &value, int role ); void setSource( Resource *resource, Task *task, Completion *completion = 0 ); protected: virtual QVariant actualEffort( int row, int role ) const; private: bool m_calculate; // opens for calculating used-/remaining effort Resource *m_resource; Task *m_task; }; } //KPlatoWork namespace #endif diff --git a/src/workpackage/taskcompletionpanel.ui b/src/workpackage/taskcompletionpanel.ui index 4faee19b..688d5c8f 100644 --- a/src/workpackage/taskcompletionpanel.ui +++ b/src/workpackage/taskcompletionpanel.ui @@ -1,255 +1,187 @@ Dag Andersen <danders@get2net.dk> KPlatoWork::TaskCompletionPanel 0 0 620 323 0 0 QFrame::StyledPanel QFrame::Raised Started: true Finished: true Qt::Horizontal 16 25 QFrame::StyledPanel QFrame::Raised 10 0 0 - + Planned effort: Qt::AlignCenter - - - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - Qt::Horizontal 40 20 false false Add Entry Remove Entry Qt::Horizontal 40 20 KPlato::CompletionEntryEditor QTableView
kptusedefforteditor.h
started startTime finished finishTime entryTable