diff --git a/src/libs/kernel/kptresourcerequest.cpp b/src/libs/kernel/kptresourcerequest.cpp index 4c00f1e5..6b7b817c 100644 --- a/src/libs/kernel/kptresourcerequest.cpp +++ b/src/libs/kernel/kptresourcerequest.cpp @@ -1,1244 +1,1255 @@ /* This file is part of the KDE project * Copyright (C) 2001 Thomas Zander zander@kde.org * Copyright (C) 2004-2007 Dag Andersen * Copyright (C) 2011 Dag Andersen * Copyright (C) 2019 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 "kptresourcerequest.h" #include "ResourceGroup.h" #include "Resource.h" #include "kptlocale.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include #include using namespace KPlato; ResourceRequest::ResourceRequest(Resource *resource, int units) : m_id(0), m_resource(resource), m_units(units), m_collection(nullptr), m_parent(nullptr), m_dynamic(false) { if (resource) { m_required = resource->requiredResources(); } //debugPlan<<"("<name() : QString("None")); } ResourceRequest::ResourceRequest(const ResourceRequest &r) : m_id(r.m_id), m_resource(r.m_resource), m_units(r.m_units), m_collection(nullptr), m_parent(nullptr), m_dynamic(r.m_dynamic), m_required(r.m_required) { } ResourceRequest::~ResourceRequest() { if (m_resource) { m_resource->unregisterRequest(this); } m_resource = 0; if (m_parent) { m_parent->removeResourceRequest(this); } - if (m_collection) { + if (m_collection && m_collection->contains(this)) { m_collection->removeResourceRequest(this); } } ResourceRequestCollection *ResourceRequest::collection() const { return m_collection; } void ResourceRequest::setCollection(ResourceRequestCollection *collection) { m_collection = collection; } int ResourceRequest::id() const { return m_id; } void ResourceRequest::setId(int id) { m_id = id; } void ResourceRequest::registerRequest() { if (m_resource) m_resource->registerRequest(this); } void ResourceRequest::unregisterRequest() { if (m_resource) m_resource->unregisterRequest(this); } bool ResourceRequest::load(KoXmlElement &element, Project &project) { debugPlanXml<id()); me.setAttribute("units", QString::number(m_units)); } int ResourceRequest::units() const { //debugPlan<name()<<": units="<task() : 0; } void ResourceRequest::changed() { if (task()) { task()->changed(Node::ResourceRequestProperty); } } void ResourceRequest::setCurrentSchedulePtr(Schedule *ns) { setCurrentSchedulePtr(m_resource, ns); } void ResourceRequest::setCurrentSchedulePtr(Resource *resource, Schedule *ns) { resource->setCurrentSchedulePtr(resourceSchedule(ns, resource)); if(resource->type() == Resource::Type_Team) { foreach (Resource *member, resource->teamMembers()) { member->setCurrentSchedulePtr(resourceSchedule(ns, member)); } } foreach (Resource *r, m_required) { r->setCurrentSchedulePtr(resourceSchedule(ns, r)); } } Schedule *ResourceRequest::resourceSchedule(Schedule *ns, Resource *res) { if (ns == 0) { return 0; } Resource *r = res == 0 ? resource() : res; Schedule *s = r->findSchedule(ns->id()); if (s == 0) { s = r->createSchedule(ns->parent()); } s->setCalculationMode(ns->calculationMode()); s->setAllowOverbookingState(ns->allowOverbookingState()); static_cast(s)->setNodeSchedule(ns); //debugPlan<name()<<": id="<id()<<" mode="<calculationMode(); return s; } DateTime ResourceRequest::workTimeAfter(const DateTime &dt, Schedule *ns) { if (m_resource->type() == Resource::Type_Work) { DateTime t = availableAfter(dt, ns); foreach (Resource *r, m_required) { if (! t.isValid()) { break; } t = r->availableAfter(t, DateTime(), resourceSchedule(ns, r)); } return t; } else if (m_resource->type() == Resource::Type_Team) { return availableAfter(dt, ns); } return DateTime(); } DateTime ResourceRequest::workTimeBefore(const DateTime &dt, Schedule *ns) { if (m_resource->type() == Resource::Type_Work) { DateTime t = availableBefore(dt, ns); foreach (Resource *r, m_required) { if (! t.isValid()) { break; } t = r->availableBefore(t, DateTime(), resourceSchedule(ns, r)); } return t; } else if (m_resource->type() == Resource::Type_Team) { return availableBefore(dt, ns); } return DateTime(); } DateTime ResourceRequest::availableFrom() { DateTime dt = m_resource->availableFrom(); if (! dt.isValid()) { dt = m_resource->project()->constraintStartTime(); } return dt; } DateTime ResourceRequest::availableUntil() { DateTime dt = m_resource->availableUntil(); if (! dt.isValid()) { dt = m_resource->project()->constraintEndTime(); } return dt; } DateTime ResourceRequest::availableAfter(const DateTime &time, Schedule *ns) { if (m_resource->type() == Resource::Type_Team) { DateTime t;// = m_resource->availableFrom(); foreach (Resource *r, m_resource->teamMembers()) { setCurrentSchedulePtr(r, ns); DateTime x = r->availableAfter(time); if (x.isValid()) { t = t.isValid() ? qMin(t, x) : x; } } return t; } setCurrentSchedulePtr(ns); return m_resource->availableAfter(time); } DateTime ResourceRequest::availableBefore(const DateTime &time, Schedule *ns) { if (m_resource->type() == Resource::Type_Team) { DateTime t; foreach (Resource *r, m_resource->teamMembers()) { setCurrentSchedulePtr(r, ns); DateTime x = r->availableBefore(time); if (x.isValid()) { t = t.isValid() ? qMax(t, x) : x; } } return t; } setCurrentSchedulePtr(ns); return resource()->availableBefore(time); } Duration ResourceRequest::effort(const DateTime &time, const Duration &duration, Schedule *ns, bool backward) { setCurrentSchedulePtr(ns); Duration e = m_resource->effort(time, duration, m_units, backward, m_required); //debugPlan<name()<makeAppointment(ns, (m_resource->units() * m_units / 100), m_required); } } void ResourceRequest::makeAppointment(Schedule *ns, int amount) { if (m_resource) { setCurrentSchedulePtr(ns); m_resource->makeAppointment(ns, amount, m_required); } } long ResourceRequest::allocationSuitability(const DateTime &time, const Duration &duration, Schedule *ns, bool backward) { setCurrentSchedulePtr(ns); return resource()->allocationSuitability(time, duration, backward); } QList ResourceRequest::teamMembers() const { qDeleteAll(m_teamMembers); m_teamMembers.clear(); if (m_resource->type() == Resource::Type_Team) { foreach (Resource *r, m_resource->teamMembers()) { m_teamMembers << new ResourceRequest(r, m_units); } } return m_teamMembers; } ///////// ResourceGroupRequest::ResourceGroupRequest(ResourceGroup *group, int units) : m_id(0) , m_group(group) , m_units(units) , m_parent(nullptr) { //debugPlan<<"Request to:"<<(group ? group->name() : QString("None")); if (group) group->registerRequest(this); } ResourceGroupRequest::ResourceGroupRequest(const ResourceGroupRequest &g) : m_id(g.m_id) , m_group(g.m_group) , m_units(g.m_units) , m_parent(0) { } ResourceGroupRequest::~ResourceGroupRequest() { //debugPlan; if (m_group) { m_group->unregisterRequest(this); } for (ResourceRequest *r : m_resourceRequests) { r->setParent(nullptr); } - if (m_parent) { + if (m_parent && m_parent->contains(this)) { m_parent->takeRequest(this); } } int ResourceGroupRequest::id() const { return m_id; } void ResourceGroupRequest::setId(int id) { m_id = id; } void ResourceGroupRequest::addResourceRequest(ResourceRequest *request) { //debugPlan<<"("<setParent(this); m_resourceRequests.append(request); changed(); } ResourceRequest *ResourceGroupRequest::takeResourceRequest(ResourceRequest *request) { if (request) request->unregisterRequest(); ResourceRequest *r = 0; int i = m_resourceRequests.indexOf(request); if (i != -1) { r = m_resourceRequests.takeAt(i); } changed(); return r; } ResourceRequest *ResourceGroupRequest::find(const Resource *resource) const { foreach (ResourceRequest *gr, m_resourceRequests) { if (gr->resource() == resource) { return gr; } } return 0; } ResourceRequest *ResourceGroupRequest::resourceRequest(const QString &name) { foreach (ResourceRequest *r, m_resourceRequests) { if (r->resource()->name() == name) return r; } return 0; } QStringList ResourceGroupRequest::requestNameList(bool includeGroup) const { QStringList lst; if (includeGroup && m_units > 0 && m_group) { lst << m_group->name(); } foreach (ResourceRequest *r, m_resourceRequests) { if (! r->isDynamicallyAllocated()) { Q_ASSERT(r->resource()); lst << r->resource()->name(); } } return lst; } QList ResourceGroupRequest::requestedResources() const { QList lst; foreach (ResourceRequest *r, m_resourceRequests) { if (! r->isDynamicallyAllocated()) { Q_ASSERT(r->resource()); lst << r->resource(); } } return lst; } QList ResourceGroupRequest::resourceRequests(bool resolveTeam) const { QList lst; foreach (ResourceRequest *rr, m_resourceRequests) { if (resolveTeam && rr->resource()->type() == Resource::Type_Team) { lst += rr->teamMembers(); } else { lst << rr; } } return lst; } bool ResourceGroupRequest::load(KoXmlElement &element, XMLLoaderObject &status) { debugPlanXml<registerRequest(this); KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "resource-request") { ResourceRequest *r = new ResourceRequest(); if (r->load(e, status.project())) addResourceRequest(r); else { errorPlan<<"Failed to load resource request"; delete r; } } } // meaning of m_units changed // Pre 0.6.6 the number *included* all requests, now it is in *addition* to resource requests m_units = element.attribute("units").toInt(); if (status.version() < "0.6.6") { int x = m_units - m_resourceRequests.count(); m_units = x > 0 ? x : 0; } return true; } void ResourceGroupRequest::save(QDomElement &element) const { QDomElement me = element.ownerDocument().createElement("resourcegroup-request"); element.appendChild(me); me.setAttribute("group-id", m_group->id()); me.setAttribute("units", QString::number(m_units)); } int ResourceGroupRequest::units() const { return m_units; } Duration ResourceGroupRequest::duration(const DateTime &time, const Duration &_effort, Schedule *ns, bool backward) { Duration dur; if (m_parent) { dur = m_parent->duration(m_resourceRequests, time, _effort, ns, backward); } return dur; } DateTime ResourceGroupRequest::workTimeAfter(const DateTime &time, Schedule *ns) { DateTime start; if (m_resourceRequests.isEmpty()) { return start; } foreach (ResourceRequest *r, m_resourceRequests) { DateTime t = r->workTimeAfter(time, ns); if (t.isValid() && (!start.isValid() || t < start)) start = t; } if (start.isValid() && start < time) start = time; //debugPlan<workTimeBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } DateTime ResourceGroupRequest::availableAfter(const DateTime &time, Schedule *ns) { DateTime start; if (m_resourceRequests.isEmpty()) { return start; } foreach (ResourceRequest *r, m_resourceRequests) { DateTime t = r->availableAfter(time, ns); if (t.isValid() && (!start.isValid() || t < start)) start = t; } if (start.isValid() && start < time) start = time; //debugPlan<name(); return start; } DateTime ResourceGroupRequest::availableBefore(const DateTime &time, Schedule *ns) { DateTime end; if (m_resourceRequests.isEmpty()) { return end; } foreach (ResourceRequest *r, m_resourceRequests) { DateTime t = r->availableBefore(time, ns); if (t.isValid() && (!end.isValid() || t > end)) end = t; } if (!end.isValid() || end > time) end = time; //debugPlan<name(); return end; } void ResourceGroupRequest::makeAppointments(Schedule *schedule) { //debugPlan; foreach (ResourceRequest *r, m_resourceRequests) { r->makeAppointment(schedule); } } void ResourceGroupRequest::reserve(const DateTime &start, const Duration &duration) { m_start = start; m_duration = duration; } bool ResourceGroupRequest::isEmpty() const { return m_resourceRequests.isEmpty() && m_units == 0; } Task *ResourceGroupRequest::task() const { return m_parent ? m_parent->task() : 0; } void ResourceGroupRequest::changed() { if (m_parent) m_parent->changed(); } void ResourceGroupRequest::removeResourceRequest(ResourceRequest *request) { m_resourceRequests.removeAll(request); changed(); } void ResourceGroupRequest::deleteResourceRequest(ResourceRequest *request) { int i = m_resourceRequests.indexOf(request); if (i != -1) { m_resourceRequests.removeAt(i); } changed(); } void ResourceGroupRequest::resetDynamicAllocations() { QList lst; foreach (ResourceRequest *r, m_resourceRequests) { if (r->isDynamicallyAllocated()) { lst << r; } } while (! lst.isEmpty()) { deleteResourceRequest(lst.takeFirst()); } } void ResourceGroupRequest::allocateDynamicRequests(const DateTime &time, const Duration &effort, Schedule *ns, bool backward) { int num = m_units; if (num <= 0) { return; } if (num == m_group->numResources()) { // TODO: allocate all } Duration e = effort / m_units; QMap map; foreach (Resource *r, m_group->resources()) { if (r->type() == Resource::Type_Team) { continue; } ResourceRequest *rr = find(r); if (rr) { if (rr->isDynamicallyAllocated()) { --num; // already allocated } continue; } rr = new ResourceRequest(r, r->units()); long s = rr->allocationSuitability(time, e, ns, backward); if (s == 0) { // not suitable at all delete rr; } else { map.insertMulti(s, rr); } } for (--num; num >= 0 && ! map.isEmpty(); --num) { long key = map.lastKey(); ResourceRequest *r = map.take(key); r->setAllocatedDynaically(true); addResourceRequest(r); debugPlan<setCollection(nullptr); } for (ResourceGroupRequest *r : m_groupRequests) { r->setParent(nullptr); } qDeleteAll(m_resourceRequests); // removes themselves from possible group qDeleteAll(m_groupRequests); } +bool ResourceRequestCollection::contains(ResourceGroupRequest *request) const +{ + return m_groupRequests.contains(request->id()) && m_groupRequests.value(request->id()) == request; +} + +bool ResourceRequestCollection::contains(ResourceRequest *request) const +{ + return m_resourceRequests.contains(request->id()) && m_resourceRequests.value(request->id()) == request; +} + void ResourceRequestCollection::removeRequests() { const QList resourceRequests = m_resourceRequests.values(); for (ResourceRequest *r : resourceRequests) { removeResourceRequest(r); } const QList groupRequests = m_groupRequests.values(); for (ResourceGroupRequest *r : groupRequests) { takeRequest(r); } } Task *ResourceRequestCollection::task() const { return m_task; } void ResourceRequestCollection::setTask(Task *t) { m_task = t; } void ResourceRequestCollection::addRequest(ResourceGroupRequest *request) { Q_ASSERT(request->group()); foreach (ResourceGroupRequest *r, m_groupRequests) { if (r->group() == request->group()) { errorPlan<<"Request to this group already exists"; errorPlan<<"Task:"<name()<<"Group:"<group()->name(); Q_ASSERT(false); } } int id = request->id(); + m_lastGroupId = std::max(m_lastGroupId, id); if (id == 0) { - int i = 1; - while (m_groupRequests.contains(i)) { - ++i; + while (m_groupRequests.contains(++m_lastGroupId)) { } - request->setId(i); + request->setId(m_lastGroupId); } Q_ASSERT(!m_groupRequests.contains(request->id())); m_groupRequests.insert(request->id(), request); if (!request->group()->requests().contains(request)) { request->group()->registerRequest(request); } request->setParent(this); changed(); } int ResourceRequestCollection::takeRequest(ResourceGroupRequest *request) { Q_ASSERT(m_groupRequests.contains(request->id())); Q_ASSERT(m_groupRequests.value(request->id()) == request); int i = m_groupRequests.values().indexOf(request); m_groupRequests.remove(request->id()); changed(); return i; } ResourceGroupRequest *ResourceRequestCollection::groupRequest(int id) const { return m_groupRequests.value(id); } ResourceGroupRequest *ResourceRequestCollection::find(const ResourceGroup *group) const { foreach (ResourceGroupRequest *r, m_groupRequests) { if (r->group() == group) return r; // we assume only one request to the same group } return 0; } void ResourceRequestCollection::addResourceRequest(ResourceRequest *request, ResourceGroupRequest *group) { if (group) { Q_ASSERT(m_groupRequests.contains(group->id())); } int id = request->id(); + m_lastResourceId = std::max(m_lastResourceId, id); + Q_ASSERT(!m_resourceRequests.contains(id)); if (id == 0) { - int i = 1; - while (m_resourceRequests.contains(i)) { - ++i; + while (m_resourceRequests.contains(++m_lastResourceId)) { } - request->setId(i); + request->setId(m_lastResourceId); } Q_ASSERT(!m_resourceRequests.contains(request->id())); request->setCollection(this); request->registerRequest(); m_resourceRequests.insert(request->id(), request); if (group && !group->resourceRequests().contains(request)) { group->addResourceRequest(request); } } void ResourceRequestCollection::removeResourceRequest(ResourceRequest *request) { Q_ASSERT(m_resourceRequests.contains(request->id())); Q_ASSERT(m_resourceRequests.values().contains(request)); m_resourceRequests.remove(request->id()); Q_ASSERT(!m_resourceRequests.values().contains(request)); if (request->parent()) { request->parent()->takeResourceRequest(request); } } void ResourceRequestCollection::deleteResourceRequest(ResourceRequest *request) { delete request; } ResourceRequest *ResourceRequestCollection::find(const Resource *resource) const { for (ResourceRequest *r : m_resourceRequests) { if (r->resource() == resource) { return r; } } return nullptr; } ResourceRequest *ResourceRequestCollection::resourceRequest(int id) const { return m_resourceRequests.value(id); } ResourceRequest *ResourceRequestCollection::resourceRequest(const QString &name) const { for (ResourceRequest *r : m_resourceRequests) { if (r->resource()->name() == name) { return r; } } return nullptr; } QStringList ResourceRequestCollection::requestNameList(bool includeGroup) const { QStringList lst; foreach (ResourceGroupRequest *r, m_groupRequests) { lst << r->requestNameList(includeGroup); } return lst; } QList ResourceRequestCollection::requestedResources() const { QList lst; foreach (ResourceGroupRequest *g, m_groupRequests) { lst += g->requestedResources(); } return lst; } QList ResourceRequestCollection::resourceRequests(bool resolveTeam) const { // FIXME: skip going through groups, probably separate method for resolveTeam case? if (!resolveTeam) { return m_resourceRequests.values(); } QList lst; foreach (ResourceGroupRequest *g, m_groupRequests) { foreach (ResourceRequest *r, g->resourceRequests(resolveTeam)) { lst << r; } } return lst; } bool ResourceRequestCollection::contains(const QString &identity) const { QStringList lst = requestNameList(); return lst.indexOf(QRegExp(identity, Qt::CaseSensitive, QRegExp::FixedString)) != -1; } ResourceGroupRequest *ResourceRequestCollection::findGroupRequestById(const QString &id) const { foreach (ResourceGroupRequest *r, m_groupRequests) { if (r->group()->id() == id) { return r; } } return 0; } // bool ResourceRequestCollection::load(KoXmlElement &element, Project &project) { // //debugPlan; // return true; // } void ResourceRequestCollection::save(QDomElement &element) const { //debugPlan; foreach (ResourceGroupRequest *r, m_groupRequests) { r->save(element); } } // Returns the duration needed by the working resources // "Material type" of resourcegroups does not (atm) affect the duration. Duration ResourceRequestCollection::duration(const DateTime &time, const Duration &effort, Schedule *ns, bool backward) { //debugPlan<<"time="<workTimeBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } DateTime ResourceRequestCollection::availableAfter(const DateTime &time, Schedule *ns) { DateTime start; foreach (ResourceGroupRequest *r, m_groupRequests) { DateTime t = r->availableAfter(time, ns); if (t.isValid() && (!start.isValid() || t < start)) start = t; } if (start.isValid() && start < time) start = time; //debugPlan<availableBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } DateTime ResourceRequestCollection::workStartAfter(const DateTime &time, Schedule *ns) { DateTime start; foreach (ResourceGroupRequest *r, m_groupRequests) { if (r->group()->type() != ResourceGroup::Type_Work) { continue; } DateTime t = r->availableAfter(time, ns); if (t.isValid() && (!start.isValid() || t < start)) start = t; } if (start.isValid() && start < time) start = time; //debugPlan<group()->type() != ResourceGroup::Type_Work) { continue; } DateTime t = r->availableBefore(time, ns); if (t.isValid() && (!end.isValid() ||t > end)) end = t; } if (!end.isValid() || end > time) end = time; return end; } void ResourceRequestCollection::makeAppointments(Schedule *schedule) { //debugPlan; foreach (ResourceGroupRequest *r, m_groupRequests) { r->makeAppointments(schedule); } } void ResourceRequestCollection::reserve(const DateTime &start, const Duration &duration) { //debugPlan; foreach (ResourceGroupRequest *r, m_groupRequests) { r->reserve(start, duration); } } bool ResourceRequestCollection::isEmpty() const { foreach (ResourceGroupRequest *r, m_groupRequests) { if (!r->isEmpty()) return false; } return true; } void ResourceRequestCollection::changed() { //debugPlan<changed(Node::ResourceRequestProperty); } } void ResourceRequestCollection::resetDynamicAllocations() { foreach (ResourceGroupRequest *g, m_groupRequests) { g->resetDynamicAllocations(); } } Duration ResourceRequestCollection::effort(const QList &lst, const DateTime &time, const Duration &duration, Schedule *ns, bool backward) const { Duration e; foreach (ResourceRequest *r, lst) { e += r->effort(time, duration, ns, backward); //debugPlan<<(backward?"(B)":"(F)")<availableUntil(); if (!t2.isValid() || t2 < t1) t2 = t1; } //debugPlan<<"fw"< &lst, const DateTime &time, const Duration &_effort, Schedule *ns, bool backward) { //debugPlan<<"--->"<<(backward?"(B)":"(F)")<resource()->name(); } ns->logDebug("Match effort:" + time.toString() + "," + _effort.toString()); ns->logDebug("Resources: " + (nl.isEmpty() ? QString("None") : nl.join(", "))); } #endif QLocale locale; Duration e; if (_effort == Duration::zeroDuration) { return e; } DateTime logtime = time; bool match = false; DateTime start = time; int inc = backward ? -1 : 1; DateTime end = start; Duration e1; int nDays = numDays(lst, time, backward) + 1; int day = 0; for (day=0; !match && day <= nDays; ++day) { // days end = end.addDays(inc); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); //debugPlan<<"["<logDebug("Days: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')'); #endif logtime = start; for (int i=0; !match && i < 24; ++i) { // hours end = end.addSecs(inc*60*60); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); if (e + e1 < _effort) { e += e1; start = end; } else if (e + e1 == _effort) { e += e1; match = true; } else { if (false/*roundToHour*/ && (_effort - e) < (e + e1 - _effort)) { end = start; match = true; } else { end = start; } break; } //debugPlan<<"duration(h)["< _effort) { end = start; break; } //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<logDebug("Minutes: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')'); #endif logtime = start; for (int i=0; !match && i < 60; ++i) { //seconds end = end.addSecs(inc); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); if (e + e1 < _effort) { e += e1; start = end; } else if (e + e1 == _effort) { e += e1; match = true; } else if (e + e1 > _effort) { end = start; break; } //debugPlan<<"duration(s)["<logDebug("Seconds: duration " + logtime.toString() + " - " + end.toString() + " e=" + e.toString() + " (" + (_effort - e).toString() + ')'); #endif for (int i=0; !match && i < 1000; ++i) { //milliseconds end.setTime(end.time().addMSecs(inc)); e1 = effort(lst, start, backward ? start - end : end - start, ns, backward); if (e + e1 < _effort) { e += e1; start = end; } else if (e + e1 == _effort) { e += e1; match = true; } else if (e + e1 > _effort) { break; } //debugPlan<<"duration(ms)["<logError(i18n("Could not match effort. Want: %1 got: %2", _effort.toString(Duration::Format_Hour), e.toString(Duration::Format_Hour))); foreach (ResourceRequest *r, lst) { Resource *res = r->resource(); ns->logInfo(i18n("Resource %1 available from %2 to %3", res->name(), locale.toString(r->availableFrom(), QLocale::ShortFormat), locale.toString(r->availableUntil(), QLocale::ShortFormat))); } } DateTime t; if (e != Duration::zeroDuration) { foreach (ResourceRequest *r, lst) { DateTime tt; if (backward) { tt = r->availableAfter(end, ns); if (tt.isValid() && (! t.isValid() || tt < t)) { t = tt; } } else { tt = r->availableBefore(end, ns); if (tt.isValid() && (! t.isValid() || tt > t)) { t = tt; } } } } end = t.isValid() ? t : time; //debugPlan<<"<---"<<(backward?"(B)":"(F)")<<":"<time?end-time:time-end); } QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest *rr) { if (rr) { dbg<<*rr; } else { dbg<<(void*)rr; } return dbg; } QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest &rr) { if (rr.resource()) { dbg<<"ResourceRequest["<name()<<']'; } else { dbg<<"ResourceRequest["< * Copyright (C) 2011 Dag Andersen * Copyright (C) 2019 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 KPTRESOURCEREQUEST_H #define KPTRESOURCEREQUEST_H #include "plankernel_export.h" #include "kptglobal.h" #include "kptduration.h" #include "kptdatetime.h" #include #include #include #include class QDomElement; /// The main namespace. namespace KPlato { class Account; class Risk; class Effort; class Appointment; class Task; class Node; class Project; class Resource; class ResourceGroup; class ResourceRequest; class ResourceGroupRequest; class ResourceRequestCollection; class Schedule; class ResourceSchedule; class Schedule; class XMLLoaderObject; class DateTimeInterval; class ResourceRequestCollection; class PLANKERNEL_EXPORT ResourceRequest { public: explicit ResourceRequest(Resource *resource = nullptr, int units = 1); explicit ResourceRequest(const ResourceRequest &r); ~ResourceRequest(); int id() const; void setId(int id); ResourceRequestCollection *collection() const; void setCollection(ResourceRequestCollection *collection); ResourceGroupRequest *parent() const { return m_parent; } void setParent(ResourceGroupRequest *parent) { m_parent = parent; } Resource *resource() const { return m_resource; } void setResource(Resource* resource) { m_resource = resource; } bool load(KoXmlElement &element, Project &project); void save(QDomElement &element) const; /** * Get amount of requested resource units in percent */ int units() const; void setUnits(int value); void registerRequest(); void unregisterRequest(); void makeAppointment(Schedule *schedule, int amount); void makeAppointment(Schedule *schedule); Task *task() const; /// Return the datetime from when the resource is available. /// If it is not valid, the project constraint start time is used. /// For teams the earliest time for any team member is used. DateTime availableFrom(); /// Return the datetime until when the resource is available. /// If it is not valid, the project constraint end time is used. /// For teams the latest time for any team member is used. DateTime availableUntil(); Schedule *resourceSchedule(Schedule *ns, Resource *resource = 0); DateTime availableAfter(const DateTime &time, Schedule *ns); DateTime availableBefore(const DateTime &time, Schedule *ns); Duration effort(const DateTime &time, const Duration &duration, Schedule *ns, bool backward); DateTime workTimeAfter(const DateTime &dt, Schedule *ns = 0); DateTime workTimeBefore(const DateTime &dt, Schedule *ns = 0); /// Resource is allocated dynamically by the group request bool isDynamicallyAllocated() const { return m_dynamic; } /// Set resource is allocated dynamically void setAllocatedDynaically(bool dyn) { m_dynamic = dyn; } /// Return a measure of how suitable the resource is for allocation long allocationSuitability(const DateTime &time, const Duration &duration, Schedule *ns, bool backward); /// Returns a list of all the required resources that will be used in scheduling. /// Note: This list overrides the resources own list which is just used as default for allocation dialog. QList requiredResources() const { return m_required; } /// Set the list of required resources that will be used in scheduling. void setRequiredResources(const QList &lst) { m_required = lst; } private: friend class ResourceGroupRequest; QList teamMembers() const; protected: void changed(); void setCurrentSchedulePtr(Schedule *ns); void setCurrentSchedulePtr(Resource *resource, Schedule *ns); private: int m_id; Resource *m_resource; int m_units; ResourceRequestCollection *m_collection; ResourceGroupRequest *m_parent; bool m_dynamic; QList m_required; mutable QList m_teamMembers; #ifndef NDEBUG public: void printDebug(const QString& ident); #endif }; class PLANKERNEL_EXPORT ResourceGroupRequest { public: explicit ResourceGroupRequest(ResourceGroup *group = 0, int units = 0); explicit ResourceGroupRequest(const ResourceGroupRequest &group); ~ResourceGroupRequest(); int id() const; void setId(int id); void setParent(ResourceRequestCollection *parent) { m_parent = parent;} ResourceRequestCollection *parent() const { return m_parent; } ResourceGroup *group() const { return m_group; } void setGroup(ResourceGroup *group) { m_group = group; } void unregister(const ResourceGroup *group) { if (group == m_group) m_group = 0; } /// Return a list of resource requests. /// If @p resolveTeam is true, include the team members, /// if @p resolveTeam is false, include the team resource itself. QList resourceRequests(bool resolveTeam=true) const; int count() const { return m_resourceRequests.count(); } ResourceRequest *requestAt(int idx) const { return m_resourceRequests.value(idx); } ResourceRequest *takeResourceRequest(ResourceRequest *request); ResourceRequest *find(const Resource *resource) const; ResourceRequest *resourceRequest(const QString &name); /// Return a list of allocated resources, allocation to group is not included by default. QStringList requestNameList(bool includeGroup = false) const; /// Return a list of allocated resources. /// Allocations to groups are not included. /// Team resources are included but *not* the team members. /// Any dynamically allocated resource is not included. QList requestedResources() const; bool load(KoXmlElement &element, XMLLoaderObject &status); void save(QDomElement &element) const; /// The number of requested resources int units() const; void setUnits(int value) { m_units = value; changed(); } /** * Returns the duration needed to do the @p effort starting at @p start. */ Duration duration(const DateTime &start, const Duration &effort, Schedule *ns, bool backward = false); DateTime availableAfter(const DateTime &time, Schedule *ns); DateTime availableBefore(const DateTime &time, Schedule *ns); DateTime workTimeAfter(const DateTime &dt, Schedule *ns = 0); DateTime workTimeBefore(const DateTime &dt, Schedule *ns = 0); /** * Makes appointments for schedule @p schedule to the * requested resources for the duration found in @ref duration(). * @param schedule the schedule */ void makeAppointments(Schedule *schedule); /** * Reserves the requested resources for the specified interval */ void reserve(const DateTime &start, const Duration &duration); bool isEmpty() const; Task *task() const; void changed(); /// Reset dynamic resource allocations void resetDynamicAllocations(); /// Allocate dynamic requests. Do nothing if already allocated. void allocateDynamicRequests(const DateTime &time, const Duration &effort, Schedule *ns, bool backward); void removeResourceRequest(ResourceRequest *request); private: friend ResourceRequestCollection; void addResourceRequest(ResourceRequest *request); void deleteResourceRequest(ResourceRequest *request); private: int m_id; ResourceGroup *m_group; int m_units; ResourceRequestCollection *m_parent; QList m_resourceRequests; DateTime m_start; Duration m_duration; #ifndef NDEBUG public: void printDebug(const QString& ident); #endif }; class PLANKERNEL_EXPORT ResourceRequestCollection { public: explicit ResourceRequestCollection(Task *task = 0); ~ResourceRequestCollection(); + bool contains(ResourceGroupRequest *request) const; + bool contains(ResourceRequest *request) const; + /// Remove all group- and resource requests /// Note: Does not delete void removeRequests(); QList requests() const { return m_groupRequests.values(); } void addRequest(ResourceGroupRequest *request); void deleteRequest(ResourceGroupRequest *request) { Q_ASSERT(m_groupRequests.contains(request->id())); Q_ASSERT(m_groupRequests.value(request->id()) == request); m_groupRequests.remove(request->id()); delete request; changed(); } int takeRequest(ResourceGroupRequest *request); ResourceGroupRequest *groupRequest(int id) const; ResourceGroupRequest *find(const ResourceGroup *resource) const; ResourceRequest *resourceRequest(int id) const; ResourceRequest *find(const Resource *resource) const; ResourceRequest *resourceRequest(const QString &name) const; /// The ResourceRequestCollection has no requests bool isEmpty() const; /// Empty the ResourceRequestCollection of all requets // void clear() { m_groupRequests.clear(); } /// Reset dynamic resource allocations void resetDynamicAllocations(); bool contains(const QString &identity) const; ResourceGroupRequest *findGroupRequestById(const QString &id) const; /// Return a list of names of allocated resources. /// Allocations to groups are not included by default. /// Team resources are included but *not* the team members. /// Any dynamically allocated resource is not included. QStringList requestNameList(bool includeGroup = false) const; /// Return a list of allocated resources. /// Allocations to groups are not included. /// Team resources are included but *not* the team members. /// Any dynamically allocated resource is not included. QList requestedResources() const; /// Return a list of all resource requests. /// If @p resolveTeam is true, include the team members, /// if @p resolveTeam is false, include the team resource itself. QList resourceRequests(bool resolveTeam=true) const; /// Add the resource request @p request /// If @p group is not nullptr, the request is also added to the @p group void addResourceRequest(ResourceRequest *request, ResourceGroupRequest *group = nullptr); /// Remove resource request @p request void removeResourceRequest(ResourceRequest *request); /// Delete resource request @p request void deleteResourceRequest(ResourceRequest *request); //bool load(KoXmlElement &element, Project &project); void save(QDomElement &element) const; /** * Returns the duration needed to do the @p effort starting at @p time. */ Duration duration(const DateTime &time, const Duration &effort, Schedule *sch, bool backward = false); DateTime availableAfter(const DateTime &time, Schedule *ns); DateTime availableBefore(const DateTime &time, Schedule *ns); DateTime workTimeAfter(const DateTime &dt, Schedule *ns = 0) const; DateTime workTimeBefore(const DateTime &dt, Schedule *ns = 0) const; DateTime workStartAfter(const DateTime &time, Schedule *ns); DateTime workFinishBefore(const DateTime &time, Schedule *ns); /** * Makes appointments for the schedule @p schedule to the requested resources. * Assumes that @ref duration() has been run. * @param schedule the schedule */ void makeAppointments(Schedule *schedule); /** * Reserves the requested resources for the specified interval */ void reserve(const DateTime &start, const Duration &duration); Task *task() const; void setTask(Task *t); void changed(); Duration effort(const QList &lst, const DateTime &time, const Duration &duration, Schedule *ns, bool backward) const; int numDays(const QList &lst, const DateTime &time, bool backward) const; Duration duration(const QList &lst, const DateTime &time, const Duration &_effort, Schedule *ns, bool backward); private: Task *m_task; + int m_lastGroupId; + int m_lastResourceId; QMap m_groupRequests; QMap m_resourceRequests; }; } //KPlato namespace PLANKERNEL_EXPORT QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest *r); PLANKERNEL_EXPORT QDebug operator<<(QDebug dbg, const KPlato::ResourceRequest &r); #endif diff --git a/src/libs/models/kptnodeitemmodel.cpp b/src/libs/models/kptnodeitemmodel.cpp index 4eb1b9ea..7e8c1b6b 100644 --- a/src/libs/models/kptnodeitemmodel.cpp +++ b/src/libs/models/kptnodeitemmodel.cpp @@ -1,5379 +1,5393 @@ /* This file is part of the KDE project * Copyright (C) 2007 - 2009, 2012 Dag Andersen * Copyright (C) 2016 Dag Andersen * Copyright (C) 2019 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 #include #include "kptduration.h" #include "kptproject.h" #include "kptnode.h" #include "kpttaskcompletedelegate.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "InsertProjectXmlCommand.h" #include "InsertTaskModuleCommand.h" #include "kptdebug.h" #include #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::ToolTipRole: { QTextEdit w(node->description(), nullptr); QString description = w.toPlainText(); if (description.length() > 200) { description = description.left(200) + " ..."; description.replace('\n', "
"); } else { description = node->description(); } w.setHtml(i18n("

%1: %2

%3

", node->wbsCode(), node->name(), description)); return w.toHtml(); } 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(); s.remove('\r'); return s.replace('\n', ' '); } 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::priority(const Node *node, int role) const { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return node->priority(); case Qt::ToolTipRole: break; 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) { return xi18nc("@info:tooltip", "Cannot start"); } 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 NodePriority: result = priority(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::setPriority(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { if (value.toInt() != node->priority()) { return new NodeModifyPriorityCmd(*node, node->priority(), value.toInt(), kundo2_i18n("Modify priority")); } 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::NodePriority: { if (!baselined) { 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::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeCompleted: 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"<numChildren(); return QModelIndex(); } // now get the internal pointer for the index Node *n = p->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 = new MacroCommand(); bool removedAllocations = false; bool addedAllocations = false; KUndo2Command *cc = nullptr; 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 (pargr == nullptr) { pargr = new ResourceGroup(); pargr->setName(i18n("Resources")); cc = new AddResourceGroupCmd(m_project, pargr); cc->redo(); cmd->addCommand(cc); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName(s.trimmed()); cc = new AddResourceCmd(m_project, r); cc->redo(); cmd->addCommand(cc); cc = new AddParentGroupCmd(r, pargr); cc->redo(); cmd->addCommand(cc); //debugPlan<<"add resource:"<name(); } // 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) { //debugPlan<<"delete request:"<resource()->name()<<" group:"<parent()->group()->name(); cc = new RemoveResourceRequestCmd(r->parent(), r); cc->redo(); cmd->addCommand(cc); removedAllocations = true; } } } // Handle new requests QHash groupmap; for (const QString &s : alloc) { // if an allocation is not in req, it must be added if (req.indexOf(s) == -1) { - ResourceGroup *pargr = 0; + ResourceGroup *pargr = nullptr; Resource *r = m_project->resourceByName(s); if (r == nullptr) { // Handle request to non existing resource pargr = m_project->groupByName(i18n("Resources")); if (pargr == nullptr) { pargr = new ResourceGroup(); pargr->setName(i18n("Resources")); cc = new AddResourceGroupCmd(m_project, pargr); cc->redo(); cmd->addCommand(cc); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName(s); cc = new AddResourceCmd(pargr, r); //debugPlan<<"add resource:"<name(); cc->redo(); cmd->addCommand(cc); addedAllocations = true; } else { - pargr = r->parentGroups().value(0); // ### TODO + pargr = r->parentGroups().value(0); + if (pargr == nullptr) { + // For now we use/add default group + pargr = m_project->groupByName(i18n("Resources")); + if (pargr == nullptr) { + pargr = new ResourceGroup(); + pargr->setName(i18n("Resources")); + cc = new AddResourceGroupCmd(m_project, pargr); + cc->redo(); + cmd->addCommand(cc); + } + cc = new AddParentGroupCmd(r, pargr); + cc->redo(); + cmd->addCommand(cc); + } //debugPlan<<"add '"<name()<<"' to group:"<resourceGroupRequest(pargr); if (g == nullptr) { g = groupmap.value(pargr); } if (g == nullptr) { // create a group request g = new ResourceGroupRequest(pargr); cc = new AddResourceGroupRequestCmd(*task, g); cc->redo(); cmd->addCommand(cc); groupmap.insert(pargr, g); //debugPlan<<"add group request:"<units())); cc->redo(); cmd->addCommand(cc); //debugPlan<<"add request:"<name()<<" group:"<isEmpty()) { KUndo2MagicString s = kundo2_i18n("Add resource allocation"); if (!addedAllocations && removedAllocations) { s = kundo2_i18n("Removed resource allocation"); } MacroCommand *m = new MacroCommand(s); emit executeCommand(m); m->addCommand(cmd); return true; } delete cmd; } } return false; } bool NodeItemModel::setCompletion(Node *node, const QVariant &value, int role) { debugPlan<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::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(); } return rows; } 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" << "application/x-vnd.kde.plan.taskmodule" << "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->hasFormat("application/x-vnd.kde.plan.taskmodule") || 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::dropTaskModuleMimeData(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.taskmodule")); QStringList substitute; while (it.hasNext()) { QRegularExpressionMatch match = it.next(); QString param = match.captured().remove("[[").remove("]]"); if (!substitute.contains(param)) { substitute << param; } } QMap params; if (!substitute.isEmpty()) { ParameterSubstitutionDialog dlg(substitute); dlg.setCaption(xi18nc("@title:window", "Task Module Parameters")); if (!dlg.exec()) { return false; } params = dlg.parameters(); } KUndo2Command *cmd = new InsertTaskModuleCommand(project(), data->data("application/x-vnd.kde.plan.taskmodule"), n, n->childNode(row), params, kundo2_i18n("Insert task module")); 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->parentGroups().value(0)) && task.resourceGroupRequest(r->parentGroups().value(0)) == 0) { ResourceGroupRequest *gr = new ResourceGroupRequest(r->parentGroups().value(0)); groups[ r->parentGroups().value(0) ] = 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->parentGroups().value(0)); if (gr == 0) { gr = task.resourceGroupRequest(r->parentGroups().value(0)); } 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->parentGroups().value(0)); 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->hasFormat("application/x-vnd.kde.plan.taskmodule")) { debugPlan; return dropTaskModuleMimeData(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, int property) { 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->parentGroups().value(0))) { ResourceGroupRequest *gr = new ResourceGroupRequest(r->parentGroups().value(0)); cmd->addCommand(new AddResourceGroupRequestCmd(static_cast(*node), gr)); groups[ r->parentGroups().value(0) ] = gr; } ResourceRequest *rr = new ResourceRequest(r, 100); cmd->addCommand(new AddResourceRequestCmd(groups[ r->parentGroups().value(0) ], 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 if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraint: // constraint type if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if (!isColumnReadOnly(index.column())) { 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 if (!isColumnReadOnly(index.column())) { 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 if (!isColumnReadOnly(index.column())) { 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; case NodeModel::NodeCompleted: if (!isColumnReadOnly(index.column())) { flags |= Qt::ItemIsEditable; } 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) , m_project(nullptr) { } void TaskModuleModel::setProject(Project *project) { if (m_project) { disconnect(m_project, &Project::taskModulesChanged, this, &TaskModuleModel::slotTaskModulesChanged); } m_project = project; if (m_project) { connect(m_project, &Project::taskModulesChanged, this, &TaskModuleModel::slotTaskModulesChanged); } slotReset(); } void TaskModuleModel::slotReset() { slotTaskModulesChanged(m_project ? m_project->taskModules() : QList()); } 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 QVariant(); 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); warnPlan<open("root")) { // maindoc.xml warnPlan<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); } else { warnPlan<setData("application/x-vnd.kde.plan.taskmodule", context.document.toByteArray()); delete project; } } } return mime; } void TaskModuleModel::stripProject(Project *project) const { foreach (ScheduleManager *sm, project->scheduleManagers()) { DeleteScheduleManagerCmd c(*project, sm); } } void TaskModuleModel::loadTaskModules(const QStringList &files) { debugPlan< &modules) { debugPlan<