diff --git a/src/libs/kernel/CMakeLists.txt b/src/libs/kernel/CMakeLists.txt index 56ff3e57..d7d031a4 100644 --- a/src/libs/kernel/CMakeLists.txt +++ b/src/libs/kernel/CMakeLists.txt @@ -1,75 +1,76 @@ include_directories( ${PLANKERNEL_INCLUDES} ${PLANKUNDO2_INCLUDES} ${PLANSTORE_INCLUDES} ) ########### KPlato kernel library ############### set(plankernel_LIB_SRCS kptglobal.cpp kptlocale.cpp kpteffortcostmap.cpp kptdocuments.cpp kptaccount.cpp kptappointment.cpp kptnode.cpp kptproject.cpp kptrelation.cpp ResourceGroup.cpp Resource.cpp kptresourcerequest.cpp kpttask.cpp kptduration.cpp kptdatetime.cpp kptcalendar.cpp kptschedule.cpp kptwbsdefinition.cpp kptcommand.cpp kptpackage.cpp kptdebug.cpp commands/NamedCommand.cpp commands/MacroCommand.cpp commands/SetTaskModulesCommand.cpp commands/AddResourceCmd.cpp commands/RemoveResourceCmd.cpp commands/AddParentGroupCmd.cpp commands/RemoveParentGroupCmd.cpp commands/AddParentGroupCmd.cpp commands/RemoveParentGroupCmd.cpp + commands/ModifyResourceRequestAlternativeCmd.cpp kptschedulerplugin.cpp kptconfigbase.cpp KPlatoXmlLoaderBase.cpp ) add_library(plankernel SHARED ${plankernel_LIB_SRCS}) generate_export_header(plankernel) target_link_libraries(plankernel PUBLIC plankundo2 planstore planwidgetutils # KF5::I18n # KF5::CoreAddons ) if(KF5Holidays_FOUND) target_link_libraries(plankernel PUBLIC KF5::Holidays) endif() set_target_properties(plankernel PROPERTIES VERSION ${GENERIC_PLAN_LIB_VERSION} SOVERSION ${GENERIC_PLAN_LIB_SOVERSION} ) install(TARGETS plankernel ${INSTALL_TARGETS_DEFAULT_ARGS}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # plan_schedulerplugin.desktop if(BUILD_TESTING) add_subdirectory( tests ) endif() diff --git a/src/libs/kernel/commands/ModifyResourceRequestAlternativeCmd.cpp b/src/libs/kernel/commands/ModifyResourceRequestAlternativeCmd.cpp new file mode 100644 index 00000000..a6f18c9e --- /dev/null +++ b/src/libs/kernel/commands/ModifyResourceRequestAlternativeCmd.cpp @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + * Copyright (C) 2020 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. + */ + +#include "ModifyResourceRequestAlternativeCmd.h" + +#include + + +using namespace KPlato; + +ModifyResourceRequestAlternativeCmd::ModifyResourceRequestAlternativeCmd(ResourceRequest *request, const QList &value, const KUndo2MagicString& name) + : NamedCommand(name) + , m_request(request) + , m_newvalue(value) +{ + m_oldvalue = request->alternativeRequests(); +} +void ModifyResourceRequestAlternativeCmd::execute() +{ + m_request->setAlternativeRequests(m_newvalue); +} +void ModifyResourceRequestAlternativeCmd::unexecute() +{ + m_request->setAlternativeRequests(m_oldvalue); +} diff --git a/src/libs/kernel/commands/ModifyResourceRequestAlternativeCmd.h b/src/libs/kernel/commands/ModifyResourceRequestAlternativeCmd.h new file mode 100644 index 00000000..a643515a --- /dev/null +++ b/src/libs/kernel/commands/ModifyResourceRequestAlternativeCmd.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + * Copyright (C) 2020 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 MODIFYRESOURCEREQUESTALTERNATIVECMD_H +#define MODIFYRESOURCEREQUESTALTERNATIVECMD_H + +#include "plankernel_export.h" + +#include "NamedCommand.h" + + +/// The main namespace +namespace KPlato +{ + +class Resource; +class ResourceRequest; +class Appointment; + +class PLANKERNEL_EXPORT ModifyResourceRequestAlternativeCmd : public NamedCommand +{ +public: + ModifyResourceRequestAlternativeCmd(ResourceRequest *request, const QList &value, const KUndo2MagicString& name = KUndo2MagicString()); + void execute() override; + void unexecute() override; + +private: + ResourceRequest *m_request; + QList m_oldvalue, m_newvalue; +}; + +} + +#endif diff --git a/src/libs/kernel/kptresourcerequest.cpp b/src/libs/kernel/kptresourcerequest.cpp index cdff4488..e3d8b742 100644 --- a/src/libs/kernel/kptresourcerequest.cpp +++ b/src/libs/kernel/kptresourcerequest.cpp @@ -1,903 +1,904 @@ /* 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_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_dynamic(r.m_dynamic), - m_required(r.m_required) + m_required(r.m_required), + m_alternativeRequests(r.m_alternativeRequests) { } ResourceRequest::~ResourceRequest() { if (m_resource) { m_resource->unregisterRequest(this); } m_resource = 0; 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) { Duration e; if (m_resource->type() == Resource::Type_Team) { for (ResourceRequest *rr : teamMembers()) { e += rr->effort(time, duration, ns, backward); } } else { setCurrentSchedulePtr(ns); 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; } QList ResourceRequest::alternativeRequests() const { return m_alternativeRequests; } void ResourceRequest::setAlternativeRequests(const QList requests) { for (ResourceRequest *r : m_alternativeRequests) { removeAlternativeRequest(r); } for (ResourceRequest *r : requests) { addAlternativeRequest(r); } } bool ResourceRequest::addAlternativeRequest(ResourceRequest *request) { emitAlternativeRequestToBeAdded(this, m_alternativeRequests.count()); m_alternativeRequests.append(request); emitAlternativeRequestAdded(request); return true; } bool ResourceRequest::removeAlternativeRequest(ResourceRequest *request) { emitAlternativeRequestToBeRemoved(this, m_alternativeRequests.indexOf(request), request); m_alternativeRequests.removeAll(request); emitAlternativeRequestRemoved(); return true; } void ResourceRequest::emitAlternativeRequestToBeAdded(ResourceRequest *request, int row) { if (m_collection) { emit m_collection->alternativeRequestToBeAdded(request, row); } } void ResourceRequest::emitAlternativeRequestAdded(ResourceRequest *alternative) { if (m_collection) { emit m_collection->alternativeRequestAdded(alternative); } } void ResourceRequest::emitAlternativeRequestToBeRemoved(ResourceRequest *request, int row, ResourceRequest *alternative) { if (m_collection) { emit m_collection->alternativeRequestToBeRemoved(request, row, alternative); } } void ResourceRequest::emitAlternativeRequestRemoved() { if (m_collection) { emit m_collection->alternativeRequestRemoved(); } } ///////// ResourceRequestCollection::ResourceRequestCollection(Task *task) : QObject() , m_task(task) , m_lastResourceId(0) { //debugPlan<setCollection(nullptr); } qDeleteAll(m_resourceRequests); // removes themselves from possible group } 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); } } Task *ResourceRequestCollection::task() const { return m_task; } void ResourceRequestCollection::setTask(Task *t) { m_task = t; } void ResourceRequestCollection::addResourceRequest(ResourceRequest *request) { int id = request->id(); m_lastResourceId = std::max(m_lastResourceId, id); Q_ASSERT(!m_resourceRequests.contains(id)); if (id == 0) { while (m_resourceRequests.contains(++m_lastResourceId)) { } request->setId(m_lastResourceId); } Q_ASSERT(!m_resourceRequests.contains(request->id())); request->setCollection(this); request->registerRequest(); m_resourceRequests.insert(request->id(), 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)); } 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() const { QStringList lst; for (ResourceRequest *r : m_resourceRequests) { lst << r->resource()->name(); } return lst; } QList ResourceRequestCollection::requestedResources() const { QList lst; for (ResourceRequest *r : m_resourceRequests) { lst += r->resource(); } return lst; } QList ResourceRequestCollection::resourceRequests(bool resolveTeam) const { QList lst = m_resourceRequests.values(); if (resolveTeam) { for (ResourceRequest *r : m_resourceRequests) { lst += r->teamMembers(); } } return lst; } bool ResourceRequestCollection::contains(const QString &identity) const { QStringList lst = requestNameList(); return lst.indexOf(QRegExp(identity, Qt::CaseSensitive, QRegExp::FixedString)) != -1; } // bool ResourceRequestCollection::load(KoXmlElement &element, Project &project) { // //debugPlan; // return true; // } void ResourceRequestCollection::save(QDomElement &element) const { //debugPlan; for (ResourceRequest *r : m_resourceRequests) { 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; // TODO: Alternatives for (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<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; // TODO: Alternatives for (ResourceRequest *r : m_resourceRequests) { if (r->resource()->type() != Resource::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<resource()->type() != Resource::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; // TODO: Alternatives for (ResourceRequest *r : m_resourceRequests) { r->makeAppointment(schedule); } } void ResourceRequestCollection::reserve(const DateTime &start, const Duration &duration) { //debugPlan; // TODO: Alternatives for (ResourceRequest *r : m_resourceRequests) { // r->reserve(start, duration); //FIXME } } bool ResourceRequestCollection::isEmpty() const { return m_resourceRequests.isEmpty(); } void ResourceRequestCollection::changed() { //debugPlan<changed(Node::ResourceRequestProperty); } } void ResourceRequestCollection::resetDynamicAllocations() { //TODO } 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) 2019 Dag Andersen + * Copyright (C) 2006 - 2007, 2012 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +// clazy:excludeall=qstring-arg +#include "AlternativeResourceDelegate.h" + +#include "ResourceItemModel.h" +#include "ResourceItemSFModel.h" +#include "kptresourceallocationmodel.h" +#include +#include + + +using namespace KPlato; + + +//--------------------------- +AlternativeResourceDelegate::AlternativeResourceDelegate(QObject *parent) + : ItemDelegate(parent) +{ +} + +QWidget *AlternativeResourceDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const +{ + TreeComboBox *editor = new TreeComboBox(parent); + editor->installEventFilter(const_cast(this)); + ResourceItemSFModel *m = new ResourceItemSFModel(editor); + editor->setModel(m); + return editor; +} + +void AlternativeResourceDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + TreeComboBox *box = static_cast(editor); + ResourceItemSFModel *pm = static_cast(box->model()); + const ResourceAllocationItemModel *model = qobject_cast(index.model()); + Q_ASSERT(model); + pm->setProject(model->project()); + pm->setFilteredResources(QList() << model->resource(index)); + pm->setFilterKeyColumn(ResourceModel::ResourceType); + pm->setFilterRole(Qt::EditRole); + pm->setFilterRegularExpression(index.sibling(index.row(), ResourceAllocationModel::RequestType).data(Qt::EditRole).toString()); +} + +void AlternativeResourceDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + TreeComboBox *box = static_cast(editor); + + ResourceItemSFModel *pm = static_cast(box->model()); + QList lst; + foreach (const QModelIndex &i, box->currentIndexes()) { + lst << pm->resource(i); + } + ResourceAllocationItemModel *mdl = qobject_cast(model); + Q_ASSERT(mdl); + mdl->setAlternativeRequests(index, lst); +} + +void AlternativeResourceDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const +{ + debugPlan<sizeHint(); + QRect r = option.rect; + r.setWidth(qMax(100, r.width())); + editor->setGeometry(r); +} diff --git a/src/libs/models/RequieredResourceDelegate.h b/src/libs/models/AlternativeResourceDelegate.h similarity index 85% copy from src/libs/models/RequieredResourceDelegate.h copy to src/libs/models/AlternativeResourceDelegate.h index dacc5955..4f317d36 100644 --- a/src/libs/models/RequieredResourceDelegate.h +++ b/src/libs/models/AlternativeResourceDelegate.h @@ -1,52 +1,53 @@ /* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen * Copyright (C) 2019 Dag Andersen * Copyright (C) 2006 - 2009 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 REQUIEREDRESOURCEDELEGATE_H -#define REQUIEREDRESOURCEDELEGATE_H +#ifndef ALTERNATIVERESOURCEDELEGATE_H +#define ALTERNATIVERESOURCEDELEGATE_H #include "planmodels_export.h" #include "kptitemmodelbase.h" class KUndo2Command; /// The main namespace namespace KPlato { //------------------------------------ -class PLANMODELS_EXPORT RequieredResourceDelegate : public ItemDelegate +class PLANMODELS_EXPORT AlternativeResourceDelegate : public ItemDelegate { Q_OBJECT public: - explicit RequieredResourceDelegate(QObject *parent = 0); + explicit AlternativeResourceDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } // namespace KPlato #endif diff --git a/src/libs/models/CMakeLists.txt b/src/libs/models/CMakeLists.txt index 74f5e63a..d5d6951c 100644 --- a/src/libs/models/CMakeLists.txt +++ b/src/libs/models/CMakeLists.txt @@ -1,87 +1,88 @@ include_directories( ${PLANKERNEL_INCLUDES} ${PLANWIDGETS_INCLUDES} ${PLANODF_INCLUDES} ${PLANKUNDO2_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR}/commands ) if(BUILD_TESTING) add_subdirectory( tests ) endif() if (KF5Contacts_FOUND) add_definitions(-DPLAN_KCONTACTS_FOUND) endif () ########### KPlato data models library ############### set(planmodels_LIB_SRCS reportgenerator/ReportGenerator.cpp reportgenerator/ReportGeneratorOdt.cpp kptcommonstrings.cpp kpttreecombobox.cpp commands/InsertProjectXmlCommand.cpp commands/InsertTaskModuleCommand.cpp kcalendar/kdatetable.cpp kcalendar/kdatepicker.cpp RequieredResourceDelegate.cpp + AlternativeResourceDelegate.cpp kptitemmodelbase.cpp kptnodechartmodel.cpp kptflatproxymodel.cpp kptrelationmodel.cpp kptworkpackagemodel.cpp kptdocumentmodel.cpp kptnodeitemmodel.cpp kptdurationspinbox.cpp kpttaskstatusmodel.cpp kptcalendarmodel.cpp kptschedulemodel.cpp kptaccountsmodel.cpp kptpertcpmmodel.cpp AllocatedResourceItemModel.cpp ResourceGroupModel.cpp ResourceModel.cpp ResourceGroupItemModel.cpp ResourceItemModel.cpp kptresourceappointmentsmodel.cpp kptresourceallocationmodel.cpp ResourceItemSFModel.cpp # GroupAllocationItemModel.cpp kpttaskcompletedelegate.cpp ) add_library(planmodels SHARED ${planmodels_LIB_SRCS}) generate_export_header(planmodels) target_link_libraries(planmodels PUBLIC plankernel planodf KChart KGantt KF5::KIOWidgets PRIVATE Qt5::Gui KF5::Notifications KF5::TextWidgets planwidgets ) if(KF5Contacts_FOUND) target_link_libraries(planmodels PRIVATE KF5::Contacts) endif() set_target_properties(planmodels PROPERTIES VERSION ${GENERIC_PLAN_LIB_VERSION} SOVERSION ${GENERIC_PLAN_LIB_SOVERSION} ) install(TARGETS planmodels ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/libs/models/RequieredResourceDelegate.cpp b/src/libs/models/RequieredResourceDelegate.cpp index 3285ae3d..07e66220 100644 --- a/src/libs/models/RequieredResourceDelegate.cpp +++ b/src/libs/models/RequieredResourceDelegate.cpp @@ -1,122 +1,120 @@ /* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen * Copyright (C) 2019 Dag Andersen * Copyright (C) 2006 - 2007, 2012 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "RequieredResourceDelegate.h" -#include "ResourceGroupItemModel.h" +#include "ResourceItemModel.h" #include "ResourceItemSFModel.h" #include "kptresourceallocationmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPlato; //--------------------------- RequieredResourceDelegate::RequieredResourceDelegate(QObject *parent) : ItemDelegate(parent) { } QWidget *RequieredResourceDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const { if (index.data(Qt::CheckStateRole).toInt() == Qt::Unchecked) { return 0; } TreeComboBox *editor = new TreeComboBox(parent); editor->installEventFilter(const_cast(this)); ResourceItemSFModel *m = new ResourceItemSFModel(editor); + + m->setFilterKeyColumn(ResourceModel::ResourceType); + m->setFilterRole(Role::EnumListValue); + m->setFilterRegularExpression(QString::number(Resource::Type_Material)); + editor->setModel(m); return editor; } void RequieredResourceDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { TreeComboBox *box = static_cast(editor); ResourceItemSFModel *pm = static_cast(box->model()); - ResourceGroupItemModel *rm = qobject_cast(pm->sourceModel()); - Q_ASSERT(rm); const ResourceAllocationItemModel *model = qobject_cast(index.model()); Q_ASSERT(model); - rm->setProject(model->project()); - pm->addFilteredResource(model->resource(index)); + pm->setProject(model->project()); + pm->setFilteredResources(QList() << model->resource(index)); QItemSelectionModel *sm = box->view()->selectionModel(); sm->clearSelection(); - foreach (const Resource *r, model->required(index)) { - // TODO -// QModelIndex i = pm->mapFromSource(rm->index(r)); -// sm->select(i, QItemSelectionModel::Select | QItemSelectionModel::Rows); - } box->setCurrentIndexes(sm->selectedRows()); box->view()->expandAll(); } -void RequieredResourceDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const +void RequieredResourceDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { TreeComboBox *box = static_cast(editor); QAbstractProxyModel *pm = static_cast(box->model()); - ResourceGroupItemModel *rm = qobject_cast(pm->sourceModel()); + ResourceItemModel *rm = qobject_cast(pm->sourceModel()); QList lst; foreach (const QModelIndex &i, box->currentIndexes()) { lst << rm->resource(pm->mapToSource(i)); } ResourceAllocationItemModel *mdl = qobject_cast(model); Q_ASSERT(mdl); mdl->setRequired(index, lst); } void RequieredResourceDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { debugPlan<sizeHint(); QRect r = option.rect; r.setWidth(qMax(100, r.width())); editor->setGeometry(r); } diff --git a/src/libs/models/RequieredResourceDelegate.h b/src/libs/models/RequieredResourceDelegate.h index dacc5955..29bc4a53 100644 --- a/src/libs/models/RequieredResourceDelegate.h +++ b/src/libs/models/RequieredResourceDelegate.h @@ -1,52 +1,53 @@ /* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen * Copyright (C) 2019 Dag Andersen * Copyright (C) 2006 - 2009 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 REQUIEREDRESOURCEDELEGATE_H #define REQUIEREDRESOURCEDELEGATE_H #include "planmodels_export.h" #include "kptitemmodelbase.h" class KUndo2Command; /// The main namespace namespace KPlato { //------------------------------------ class PLANMODELS_EXPORT RequieredResourceDelegate : public ItemDelegate { Q_OBJECT public: explicit RequieredResourceDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } // namespace KPlato #endif diff --git a/src/libs/models/ResourceItemModel.cpp b/src/libs/models/ResourceItemModel.cpp index 7516913c..6ec2b442 100644 --- a/src/libs/models/ResourceItemModel.cpp +++ b/src/libs/models/ResourceItemModel.cpp @@ -1,976 +1,986 @@ /* This file is part of the KDE project * Copyright (C) 2007 Dag Andersen * Copyright (C) 2011, 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 "ResourceItemModel.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include #include #include "kptdatetime.h" #include "kptdebug.h" #include #include #include #include #include #include #include #ifdef PLAN_KCONTACTS_FOUND #include #include #endif using namespace KPlato; ResourceItemModel::ResourceItemModel(QObject *parent) : ItemModelBase(parent) , m_groupsEnabled(false) , m_teamsEnabled(false) , m_requiredEnabled(false) { } ResourceItemModel::~ResourceItemModel() { } void ResourceItemModel::slotResourceToBeAdded(Project *project, int row) { Q_UNUSED(project) debugPlan<indexOf(resource), 0); beginInsertRows(idx, row, row); } void ResourceItemModel::slotResourceGroupAdded(ResourceGroup *group) { connectSignals(group, true); endInsertRows(); } void ResourceItemModel::slotResourceGroupToBeRemoved(Resource *resource, int row, KPlato::ResourceGroup *group) { //debugPlan<name(); QModelIndex idx = createIndex(m_project->indexOf(resource), 0); beginRemoveRows(idx, row, row); connectSignals(group, false); } void ResourceItemModel::slotResourceGroupRemoved() { //debugPlan<name(); endRemoveRows(); } void ResourceItemModel::setProject(Project *project) { beginResetModel(); if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceItemModel::projectDeleted); disconnect(m_project, &Project::localeChanged, this, &ResourceItemModel::slotLayoutChanged); disconnect(m_project, &Project::defaultCalendarChanged, this, &ResourceItemModel::slotCalendarChanged); disconnect(m_project, &Project::resourceChanged, this, &ResourceItemModel::slotResourceChanged); disconnect(m_project, &Project::resourceToBeAdded, this, &ResourceItemModel::slotResourceToBeAdded); disconnect(m_project, &Project::resourceAdded, this, &ResourceItemModel::slotResourceAdded); disconnect(m_project, &Project::resourceToBeRemoved, this, &ResourceItemModel::slotResourceToBeRemoved); disconnect(m_project, &Project::resourceRemoved, this, &ResourceItemModel::slotResourceRemoved); for (Resource *r : m_project->resourceList()) { connectSignals(r, false); } } m_project = project; if (m_project) { connect(m_project, &Project::aboutToBeDeleted, this, &ResourceItemModel::projectDeleted); connect(m_project, &Project::localeChanged, this, &ResourceItemModel::slotLayoutChanged); connect(m_project, &Project::defaultCalendarChanged, this, &ResourceItemModel::slotCalendarChanged); connect(m_project, &Project::resourceChanged, this, &ResourceItemModel::slotResourceChanged); connect(m_project, &Project::resourceToBeAdded, this, &ResourceItemModel::slotResourceToBeAdded); connect(m_project, &Project::resourceAdded, this, &ResourceItemModel::slotResourceAdded); connect(m_project, &Project::resourceToBeRemoved, this, &ResourceItemModel::slotResourceToBeRemoved); connect(m_project, &Project::resourceRemoved, this, &ResourceItemModel::slotResourceRemoved); for (Resource *r : m_project->resourceList()) { connectSignals(r, true); } } m_resourceModel.setProject(m_project); endResetModel(); } void ResourceItemModel::connectSignals(Resource *resource, bool enable) { if (enable) { connect(resource, &Resource::resourceGroupToBeAdded, this, &ResourceItemModel::slotResourceGroupToBeAdded); connect(resource, &Resource::resourceGroupAdded, this, &ResourceItemModel::slotResourceGroupAdded); connect(resource, &Resource::resourceGroupToBeRemoved, this, &ResourceItemModel::slotResourceGroupToBeRemoved); connect(resource, &Resource::resourceGroupRemoved, this, &ResourceItemModel::slotResourceGroupRemoved); } else { disconnect(resource, &Resource::resourceGroupToBeAdded, this, &ResourceItemModel::slotResourceGroupToBeAdded); disconnect(resource, &Resource::resourceGroupAdded, this, &ResourceItemModel::slotResourceGroupAdded); disconnect(resource, &Resource::resourceGroupToBeRemoved, this, &ResourceItemModel::slotResourceGroupToBeRemoved); disconnect(resource, &Resource::resourceGroupRemoved, this, &ResourceItemModel::slotResourceGroupRemoved); } for (ResourceGroup *g : resource->parentGroups()) { connectSignals(g, enable); } } void ResourceItemModel::connectSignals(ResourceGroup *group, bool enable) { if (enable) { connect(group, &ResourceGroup::dataChanged, this, &ResourceItemModel::slotResourceGroupChanged, Qt::ConnectionType(Qt::AutoConnection|Qt::UniqueConnection)); } else { disconnect(group, &ResourceGroup::dataChanged, this, &ResourceItemModel::slotResourceGroupChanged); } } Qt::ItemFlags ResourceItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = ItemModelBase::flags(index); if (!m_readWrite) { //debugPlan<<"read only"<isShared()) { flags &= ~Qt::ItemIsEditable; if (index.column() == ResourceModel::ResourceName) { flags |= Qt::ItemIsUserCheckable; } return flags; } switch (index.column()) { case ResourceModel::ResourceName: flags |= Qt::ItemIsEditable | Qt::ItemIsUserCheckable; break; case ResourceModel::ResourceScope: flags &= ~Qt::ItemIsEditable; break; case ResourceModel::ResourceType: if (! r->isBaselined()) { flags |= Qt::ItemIsEditable; } break; case ResourceModel::ResourceAccount: if (! r->isBaselined()) { flags |= Qt::ItemIsEditable; } break; case ResourceModel::ResourceNormalRate: if (! r->isBaselined()) { flags |= Qt::ItemIsEditable; } break; case ResourceModel::ResourceOvertimeRate: if (! r->isBaselined()) { flags |= Qt::ItemIsEditable; } break; default: flags |= Qt::ItemIsEditable; } //debugPlan<<"resource"<isShared()) { flags &= ~Qt::ItemIsEditable; return flags; } flags |= Qt::ItemIsDropEnabled; switch (index.column()) { case ResourceModel::ResourceName: flags |= Qt::ItemIsEditable; break; case ResourceModel::ResourceType: flags |= Qt::ItemIsEditable; break; default: flags &= ~Qt::ItemIsEditable; } //debugPlan<<"group"<(index.internalPointer()); Q_ASSERT(r); int row = m_project->indexOf(r); return createIndex(row, index.column()); } QModelIndex ResourceItemModel::index(int row, int column, const QModelIndex &parent) const { if (m_project == 0 || column < 0 || column >= columnCount() || row < 0) { return QModelIndex(); } if (!parent.isValid()) { // top level resource if (row < m_project->resourceCount()) { return createIndex(row, column); } return QModelIndex(); } Q_ASSERT(parent.internalPointer() == nullptr); if (parent.internalPointer()) { return QModelIndex(); } Resource *r = resource(parent); Q_ASSERT(r); return createIndex(row, column, r); } int ResourceItemModel::columnCount(const QModelIndex &/*parent*/) const { return m_resourceModel.propertyCount(); } int ResourceItemModel::rowCount(const QModelIndex &parent) const { int rows = 0; if (m_project == nullptr) { return rows; } if (parent.isValid()) { if (parent.internalPointer() == nullptr) { Resource *r = m_project->resourceAt(parent.row()); if (r) { if (m_groupsEnabled) { rows = r->groupCount(); } if (m_teamsEnabled) { rows += r->teamCount(); } } } } else { rows = m_project->resourceCount(); } return rows; } QVariant ResourceItemModel::name(const ResourceGroup *res, int role) const { //debugPlan<name()<<","<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool ResourceItemModel::setName(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toString() == res->name()) { return false; } emit executeCommand(new ModifyResourceNameCmd(res, value.toString(), kundo2_i18n("Modify resource name"))); return true; case Qt::CheckStateRole: emit executeCommand(new ModifyResourceAutoAllocateCmd(res, value.toBool(), kundo2_i18n("Modify resource auto allocate"))); return true; } return false; } bool ResourceItemModel::setType(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { Resource::Type v; QStringList lst = res->typeToStringList(false); if (lst.contains(value.toString())) { v = static_cast(lst.indexOf(value.toString())); } else { v = static_cast(value.toInt()); } if (v == res->type()) { return false; } emit executeCommand(new ModifyResourceTypeCmd(res, v, kundo2_i18n("Modify resource type"))); return true; } } return false; } bool ResourceItemModel::setInitials(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toString() == res->initials()) { return false; } emit executeCommand(new ModifyResourceInitialsCmd(res, value.toString(), kundo2_i18n("Modify resource initials"))); return true; } return false; } bool ResourceItemModel::setEmail(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toString() == res->email()) { return false; } emit executeCommand(new ModifyResourceEmailCmd(res, value.toString(), kundo2_i18n("Modify resource email"))); return true; } return false; } bool ResourceItemModel::setCalendar(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { Calendar *c = 0; if (value.toInt() > 0) { QStringList lst = m_resourceModel.calendar(res, Role::EnumList).toStringList(); if (value.toInt() < lst.count()) { c = m_project->calendarByName(lst.at(value.toInt())); } } if (c == res->calendar(true)) { return false; } emit executeCommand(new ModifyResourceCalendarCmd(res, c, kundo2_i18n("Modify resource calendar"))); return true; } } return false; } bool ResourceItemModel::setUnits(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toInt() == res->units()) { return false; } emit executeCommand(new ModifyResourceUnitsCmd(res, value.toInt(), kundo2_i18n("Modify resource available units"))); return true; } return false; } bool ResourceItemModel::setAvailableFrom(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toDateTime() == res->availableFrom()) { return false; } emit executeCommand(new ModifyResourceAvailableFromCmd(res, value.toDateTime(), kundo2_i18n("Modify resource available from"))); return true; } return false; } bool ResourceItemModel::setAvailableUntil(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toDateTime() == res->availableUntil()) { return false; } emit executeCommand(new ModifyResourceAvailableUntilCmd(res, value.toDateTime(), kundo2_i18n("Modify resource available until"))); return true; } return false; } bool ResourceItemModel::setNormalRate(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toDouble() == res->normalRate()) { return false; } emit executeCommand(new ModifyResourceNormalRateCmd(res, value.toDouble(), kundo2_i18n("Modify resource normal rate"))); return true; } return false; } bool ResourceItemModel::setOvertimeRate(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (value.toDouble() == res->overtimeRate()) { return false; } emit executeCommand(new ModifyResourceOvertimeRateCmd(res, value.toDouble(), kundo2_i18n("Modify resource overtime rate"))); return true; } return false; } bool ResourceItemModel::setAccount(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { Account *a = 0; if (value.type() == QVariant::Int) { QStringList lst = m_resourceModel.account(res, Role::EnumList).toStringList(); if (value.toInt() >= lst.count()) { return false; } a = m_project->accounts().findAccount(lst.at(value.toInt())); } else if (value.type() == QVariant::String) { a = m_project->accounts().findAccount(value.toString()); } Account *old = res->account(); if (old != a) { emit executeCommand(new ResourceModifyAccountCmd(*res, old, a, kundo2_i18n("Modify resource account"))); return true; } } default: break; } return false; } QVariant ResourceItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QModelIndex(); } if (index.internalPointer() == nullptr) { // top level resource Resource *r = m_project->resourceAt(index.row()); return m_resourceModel.data(r, index.column(), role); } if (m_groupsEnabled) { Resource *r = static_cast(index.internalPointer()); int groupCount = r->groupCount(); if (index.row() < groupCount) { ResourceGroup *g = r->parentGroups().value(index.row()); Q_ASSERT(g); return m_groupModel.data(g, index.column(), role); } } if (m_teamsEnabled) { Resource *r = static_cast(index.internalPointer()); int row = index.row(); if (m_groupsEnabled) { row -= r->groupCount(); } return m_resourceModel.data(r->teamMembers().value(row), index.column()); } return QVariant(); } bool ResourceItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (ItemModelBase::setData(index, value, role)) { return true; } if (! index.isValid()) { return false; } if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } if ((flags(index) & (Qt::ItemIsEditable | Qt::ItemIsUserCheckable)) == 0) { return false; } Resource *r =resource(index); if (r) { switch (index.column()) { case ResourceModel::ResourceName: return setName(r, value, role); case ResourceModel::ResourceScope: return false; // Not editable case ResourceModel::ResourceType: return setType(r, value, role); case ResourceModel::ResourceInitials: return setInitials(r, value, role); case ResourceModel::ResourceEmail: return setEmail(r, value, role); case ResourceModel::ResourceCalendar: return setCalendar(r, value, role); case ResourceModel::ResourceLimit: return setUnits(r, value, role); case ResourceModel::ResourceAvailableFrom: return setAvailableFrom(r, value, role); case ResourceModel::ResourceAvailableUntil: return setAvailableUntil(r, value, role); case ResourceModel::ResourceNormalRate: return setNormalRate(r, value, role); case ResourceModel::ResourceOvertimeRate: return setOvertimeRate(r, value, role); case ResourceModel::ResourceAccount: return setAccount(r, value, role); default: qWarning("data: invalid display value column %d", index.column()); return false; } } return false; } QVariant ResourceItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole) { return m_resourceModel.headerData(section, role); } } if (role == Qt::ToolTipRole) { return m_resourceModel.headerData(section, role); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *ResourceItemModel::createDelegate(int col, QWidget *parent) const { switch (col) { case ResourceModel::ResourceType: return new EnumDelegate(parent); case ResourceModel::ResourceCalendar: return new EnumDelegate(parent); case ResourceModel::ResourceAvailableFrom: return new DateTimeCalendarDelegate(parent); case ResourceModel::ResourceAvailableUntil: return new DateTimeCalendarDelegate(parent); case ResourceModel::ResourceAccount: return new EnumDelegate(parent); default: break; } return 0; } ResourceGroup *ResourceItemModel::group(const QModelIndex &index) const { if (index.internalPointer() == nullptr) { return nullptr; } Resource *r = static_cast(index.internalPointer()); return r->parentGroups().value(index.row()); } +QModelIndex ResourceItemModel::index(Resource *resource) const +{ + QModelIndex idx; + int row = m_project->indexOf(resource); + if (row >= 0) { + idx = index(row, 0); + } + return idx; +} + Resource *ResourceItemModel::resource(const QModelIndex &index) const { if (index.internalPointer() == nullptr) { return m_project->resourceAt(index.row()); } if (m_teamsEnabled) { Resource *r = static_cast(index.internalPointer()); int row = index.row(); if (m_groupsEnabled) { row -= r->groupCount(); } return r->teamMembers().value(row); } return nullptr; } void ResourceItemModel::slotCalendarChanged(Calendar*) { foreach (Resource *r, m_project->resourceList()) { if (r->calendar(true) == 0) { slotResourceChanged(r); } } } void ResourceItemModel::slotResourceChanged(Resource *resource) { int row = m_project->indexOf(resource); emit dataChanged(createIndex(row, 0), createIndex(row, columnCount() - 1)); if (m_teamsEnabled) { // TODO } } void ResourceItemModel::slotResourceGroupChanged(ResourceGroup *group) { for (Resource *r : group->resources()) { int row = r->groupIndexOf(group); emit dataChanged(createIndex(row, 0, r), createIndex(row, columnCount() - 1, r)); } } bool ResourceItemModel::groupsEnabled() const { return m_groupsEnabled; } void ResourceItemModel::setGroupsEnabled(bool enable) { m_groupsEnabled = enable; } bool ResourceItemModel::teamsEnabled() const { return m_teamsEnabled; } void ResourceItemModel::setTeamsEnabled(bool enable) { m_teamsEnabled = enable; } bool ResourceItemModel::requiredEnabled() const { return m_requiredEnabled; } void ResourceItemModel::setRequiredEnabled(bool enable) { m_requiredEnabled = enable; } Qt::DropActions ResourceItemModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool ResourceItemModel::dropAllowed(const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data) { if (data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal")) { QByteArray encodedData = data->data("application/x-vnd.kde.plan.resourceitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); int i = 0; foreach (Resource *r, resourceList(stream)) { if (r->isShared()) { return false; } } } //debugPlan<error() || ! m_dropDataMap.contains(job)) { // debugPlan<<(job->error() ? "Job error":"Error: no such job"); // } else if (QMimeDatabase().mimeTypeForData(m_dropDataMap[ job ].data).inherits(QStringLiteral("text/x-vcard"))) { // ResourceGroup *g = 0; // if (m_dropDataMap[ job ].parent.isValid()) { // g = qobject_cast(object(m_dropDataMap[ job ].parent)); // } else { // g = qobject_cast(object(index(m_dropDataMap[ job ].row, m_dropDataMap[ job ].column, m_dropDataMap[ job ].parent))); // } // if (g == 0) { // debugPlan<<"No group"<findResource(uid)) { r->setId(uid); } r->setName(lst[a].formattedName()); r->setEmail(lst[a].preferredEmail()); m->addCommand(new AddResourceCmd(group, r)); } if (m->isEmpty()) { delete m; return false; } emit executeCommand(m); return true; #else Q_UNUSED(group); Q_UNUSED(data); return false; #endif } bool ResourceItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { #if 0 debugPlan< 0) { return false; } ResourceGroup *g = 0; if (parent.isValid()) { g = qobject_cast(object(parent)); } else { g = qobject_cast(object(index(row, column, parent))); } if (g == 0) { debugPlan<<"No group"<formats()<name(); if (data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal")) { debugPlan<data("application/x-vnd.kde.plan.resourceitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); int i = 0; foreach (Resource *r, resourceList(stream)) { if (r->parentGroups().value(0) == g) { continue; } if (m == 0) m = new MacroCommand(KUndo2MagicString()); m->addCommand(new MoveResourceCmd(g, r)); ++i; } if (m) { KUndo2MagicString msg = kundo2_i18np("Move resource", "Move %1 resources", i); MacroCommand *c = new MacroCommand(msg); c->addCommand(m); emit executeCommand(c); } return true; } if (action == Qt::CopyAction) { MacroCommand *m = 0; QByteArray encodedData = data->data("application/x-vnd.kde.plan.resourceitemmodel.internal"); QDataStream stream(&encodedData, QIODevice::ReadOnly); int i = 0; foreach (Resource *r, resourceList(stream)) { Resource *nr = new Resource(r); if (m == 0) m = new MacroCommand(KUndo2MagicString()); m->addCommand(new AddResourceCmd(g, nr)); ++i; } if (m) { KUndo2MagicString msg = kundo2_i18np("Copy resource", "Copy %1 resources", i); MacroCommand *c = new MacroCommand(msg); c->addCommand(m); emit executeCommand(c); } return true; } return true; } if (data->hasFormat("text/x-vcard") || data->hasFormat("text/directory")) { if (action != Qt::CopyAction) { return false; } QString f = data->hasFormat("text/x-vcard") ? "text/x-vcard" : "text/directory"; return createResources(g, data->data(f)); } if (data->hasFormat("text/uri-list")) { const QList urls = data->urls(); if (urls.isEmpty()) { return false; } bool result = false; foreach (const QUrl &url, urls) { if (url.scheme() != "akonadi") { debugPlan<setSide(KIO::StatJob::SourceSide); const bool isUrlReadable = statJob->exec(); if (! isUrlReadable) { debugPlan<start(); result = true; } return result; } #endif return false; } QList ResourceItemModel::resourceList(QDataStream &stream) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Resource *r = m_project->findResource(id); if (r) { lst << r; } } debugPlan< rows; foreach (const QModelIndex &index, indexes) { if (index.isValid() && !rows.contains(index.row())) { //debugPlan<(object(index)); if (r) { rows << index.row(); stream << r->id(); } else if (group(index)) { rows.clear(); break; } } } if (!rows.isEmpty()) { m->setData("application/x-vnd.kde.plan.resourceitemmodel.internal", encodedData); } #endif return m; } QModelIndex ResourceItemModel::insertResource(Resource *resource, Resource * /*after*/) { //debugPlan; emit executeCommand(new AddResourceCmd(m_project, resource, kundo2_i18n("Add resource"))); int row = m_project->indexOf(resource); if (row != -1) { return createIndex(row, ResourceModel::ResourceName); } return QModelIndex(); } int ResourceItemModel::sortRole(int column) const { switch (column) { case ResourceModel::ResourceAvailableFrom: case ResourceModel::ResourceAvailableUntil: return Qt::EditRole; default: break; } return Qt::DisplayRole; } diff --git a/src/libs/models/ResourceItemModel.h b/src/libs/models/ResourceItemModel.h index 2f2e09ac..2abddb4a 100644 --- a/src/libs/models/ResourceItemModel.h +++ b/src/libs/models/ResourceItemModel.h @@ -1,173 +1,174 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * Copyright (C) 2007 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef RESOURCEITEMMODEL_H #define RESOURCEITEMMODEL_H #include "planmodels_export.h" #include #include "ResourceModel.h" #include "ResourceGroupModel.h" #include class QByteArray; namespace KIO { class Job; } class KJob; namespace KPlato { class Project; class Resource; class ResourceGroup; class Calendar; class Task; class Node; /** * The ResourceItemModel is organized as follows: * . Resource are the top level index * . Resources can be members of multiple groups which are shown as children to the top level resource * . A Team resource can also have the team member resources as children * * The top level resource has an index with internalPointer == nullptr. * A group has an index with internalPointer == parent resource. * A team member resource has an index with internalPointer == parent resource. */ class PLANMODELS_EXPORT ResourceItemModel : public ItemModelBase { Q_OBJECT public: explicit ResourceItemModel(QObject *parent = nullptr); ~ResourceItemModel() override; const QMetaEnum columnMap() const override { return m_resourceModel.columnMap(); } void setProject(Project *project) override; Qt::ItemFlags flags(const QModelIndex & index) const override; QModelIndex parent(const QModelIndex & index) const override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; int columnCount(const QModelIndex & parent = QModelIndex()) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QStringList mimeTypes () const override; Qt::DropActions supportedDropActions() const override; bool dropAllowed(const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data) override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; QMimeData *mimeData(const QModelIndexList & indexes) const override; QAbstractItemDelegate *createDelegate(int col, QWidget *parent) const override; QModelIndex insertResource(Resource *resource, Resource *after = 0); int sortRole(int column) const override; + QModelIndex index(Resource *resource) const; Resource *resource(const QModelIndex &idx) const; ResourceGroup *group(const QModelIndex &idx) const; bool groupsEnabled() const; void setGroupsEnabled(bool enable); bool teamsEnabled() const; void setTeamsEnabled(bool enable); bool requiredEnabled() const; void setRequiredEnabled(bool enable); protected Q_SLOTS: void slotResourceChanged(KPlato::Resource*); void slotResourceGroupChanged(KPlato::ResourceGroup *); void slotResourceGroupToBeAdded(KPlato::Resource *resource, int row); void slotResourceGroupAdded(KPlato::ResourceGroup *group); void slotResourceGroupToBeRemoved(KPlato::Resource *resource, int row, KPlato::ResourceGroup *group); void slotResourceGroupRemoved(); void slotResourceToBeAdded(KPlato::Project *project, int row); void slotResourceAdded(KPlato::Resource *resource); void slotResourceToBeRemoved(KPlato::Project *project, int row, KPlato::Resource *resource); void slotResourceRemoved(); void slotCalendarChanged(KPlato::Calendar* cal); void slotDataArrived(KIO::Job *job, const QByteArray &data ); void slotJobFinished(KJob *job); protected: QVariant notUsed(const ResourceGroup *res, int role) const; QVariant name(const ResourceGroup *res, int role) const; bool setName(Resource *res, const QVariant &value, int role); bool setName(ResourceGroup *res, const QVariant &value, int role); QVariant type(const ResourceGroup *res, int role) const; bool setType(Resource *res, const QVariant &value, int role); bool setType(ResourceGroup *res, const QVariant &value, int role); bool setInitials(Resource *res, const QVariant &value, int role); bool setEmail(Resource *res, const QVariant &value, int role); bool setCalendar(Resource *res, const QVariant &value, int role); bool setUnits(Resource *res, const QVariant &value, int role); bool setAvailableFrom(Resource *res, const QVariant &value, int role); bool setAvailableUntil(Resource *res, const QVariant &value, int role); bool setNormalRate(Resource *res, const QVariant &value, int role); bool setOvertimeRate(Resource *res, const QVariant &value, int role); bool setAccount(Resource *res, const QVariant &value, int role); QList resourceList(QDataStream &stream); bool createResources(ResourceGroup *group, const QByteArray &data); void connectSignals(Resource *resource, bool enable); void connectSignals(ResourceGroup *group, bool enable); private: ResourceModel m_resourceModel; ResourceGroupModel m_groupModel; bool m_groupsEnabled; bool m_teamsEnabled; bool m_requiredEnabled; struct DropData { Qt::DropAction action; int row; int column; QModelIndex parent; QByteArray data; } m_dropData; QMap m_dropDataMap; }; } //KPlato namespace #endif diff --git a/src/libs/models/ResourceItemSFModel.cpp b/src/libs/models/ResourceItemSFModel.cpp index 0c3872d9..03040dab 100644 --- a/src/libs/models/ResourceItemSFModel.cpp +++ b/src/libs/models/ResourceItemSFModel.cpp @@ -1,109 +1,108 @@ /* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen * Copyright (C) 2019 Dag Andersen * Copyright (C) 2006 - 2007, 2012 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "ResourceItemSFModel.h" -#include "ResourceGroupItemModel.h" -#include "kptresourceallocationmodel.h" -#include "RequieredResourceDelegate.h" +#include "ResourceItemModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPlato; ResourceItemSFModel::ResourceItemSFModel(QObject *parent) -: QSortFilterProxyModel(parent) + : QSortFilterProxyModel(parent) + , m_project(nullptr) { setDynamicSortFilter(true); - setSourceModel(new ResourceGroupItemModel(this)); + setSourceModel(new ResourceItemModel(this)); } void ResourceItemSFModel::setProject(Project *project) { - static_cast(sourceModel())->setProject(project); + static_cast(sourceModel())->setProject(project); + m_project = project; } Resource *ResourceItemSFModel::resource(const QModelIndex &idx) const { - return static_cast(sourceModel())->resource(mapToSource(idx)); + return static_cast(sourceModel())->resource(mapToSource(idx)); } QModelIndex ResourceItemSFModel::index(Resource *r) const { - return QModelIndex();//TODO mapFromSource(static_cast(sourceModel())->index(r)); + return mapFromSource(static_cast(sourceModel())->index(r)); } -Qt::ItemFlags ResourceItemSFModel::flags(const QModelIndex & index) const +Qt::ItemFlags ResourceItemSFModel::flags(const QModelIndex &index) const { Qt::ItemFlags f = QSortFilterProxyModel::flags(index); - if (index.isValid() && ! parent(index).isValid()) { - // group, not selectable - f &= ~Qt::ItemIsSelectable; - } return f; } -void ResourceItemSFModel::addFilteredResource(const Resource *r) +void ResourceItemSFModel::setFilteredResources(const QList &lst) +{ + m_filteredResources = lst; +} + +void ResourceItemSFModel::addFilteredResource(Resource *r) { - if (! m_filteredResources.contains(r)) { + if (!m_filteredResources.contains(r)) { m_filteredResources << r; } } bool ResourceItemSFModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const { - //TODO make this general filter - ResourceGroupItemModel *m = static_cast(sourceModel()); - if (m->index(source_row, ResourceModel::ResourceType, source_parent).data(Role::EnumListValue).toInt() == ResourceGroup::Type_Work) { + if (m_project && m_filteredResources.contains(m_project->resourceAt(source_row))) { return false; } - QModelIndex idx = m->index(source_row, 0, source_parent); - return ! m_filteredResources.contains(m->resource(idx)); + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } diff --git a/src/libs/models/ResourceItemSFModel.h b/src/libs/models/ResourceItemSFModel.h index 9929a12f..f37260e1 100644 --- a/src/libs/models/ResourceItemSFModel.h +++ b/src/libs/models/ResourceItemSFModel.h @@ -1,61 +1,64 @@ /* This file is part of the KDE project + * Copyright (C) 2020 Dag Andersen * Copyright (C) 2019 Dag Andersen * Copyright (C) 2006 - 2009 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 RESOURCEITEMSFMODEL_H #define RESOURCEITEMSFMODEL_H #include "planmodels_export.h" #include class KUndo2Command; /// The main namespace namespace KPlato { class Project; -class ScheduleManager; class Resource; class PLANMODELS_EXPORT ResourceItemSFModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ResourceItemSFModel(QObject *parent = 0); void setProject(Project *project); Resource *resource(const QModelIndex &index) const; using QAbstractProxyModel::index; QModelIndex index(Resource *r) const; - + Qt::ItemFlags flags(const QModelIndex & index) const override; - - void addFilteredResource(const Resource *r); + + void setFilteredResources(const QList &lst = QList()); + void addFilteredResource(Resource *r); + protected: bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override; - QList m_filteredResources; + QList m_filteredResources; + Project *m_project; }; } // namespace KPlato #endif diff --git a/src/libs/models/kptresourceallocationmodel.cpp b/src/libs/models/kptresourceallocationmodel.cpp index ef03f42a..aedb7e6e 100644 --- a/src/libs/models/kptresourceallocationmodel.cpp +++ b/src/libs/models/kptresourceallocationmodel.cpp @@ -1,698 +1,802 @@ /* This file is part of the KDE project Copyright (C) 2009, 2012 Dag Andersen danders@get2net> 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 "kptresourceallocationmodel.h" +#include "AlternativeResourceDelegate.h" #include "RequieredResourceDelegate.h" +#include #include "kptcommonstrings.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptdebug.h" #include #include using namespace KPlato; //-------------------------------------- ResourceAllocationModel::ResourceAllocationModel(QObject *parent) : QObject(parent), m_project(0), m_task(0) { } ResourceAllocationModel::~ResourceAllocationModel() { } const QMetaEnum ResourceAllocationModel::columnMap() const { return metaObject()->enumerator(metaObject()->indexOfEnumerator("Properties")); } void ResourceAllocationModel::setProject(Project *project) { m_project = project; } void ResourceAllocationModel::setTask(Task *task) { m_task = task; } int ResourceAllocationModel::propertyCount() const { return columnMap().keyCount(); } QVariant ResourceAllocationModel::name(const Resource *res, int role) const { //debugPlan<name()<<","<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceAllocationModel::type(const Resource *res, int role) const { switch (role) { case Qt::DisplayRole: - case Qt::EditRole: case Qt::ToolTipRole: return res->typeToString(true); + case Qt::EditRole: + return res->typeToString(false); case Role::EnumList: return res->typeToStringList(true); case Role::EnumListValue: return (int)res->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceAllocationModel::allocation(const Resource *res, int role) const { if (m_project == nullptr || m_task == nullptr) { return QVariant(); } const ResourceRequest *rr = m_task->requests().find(res); switch (role) { case Qt::DisplayRole: { int units = rr ? rr->units() : 0; // xgettext: no-c-format return i18nc("%", "%1%", units); } case Qt::EditRole: return rr ? rr->units() : 0; case Qt::ToolTipRole: { int units = rr ? rr->units() : 0; if (units == 0) { return xi18nc("@info:tooltip", "Not allocated"); } // xgettext: no-c-format return xi18nc("@info:tooltip", "Allocated units: %1%", units); } case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Minimum: return 0; case Role::Maximum: return 100; case Qt::CheckStateRole: return Qt::Unchecked; } return QVariant(); } QVariant ResourceAllocationModel::maximum(const Resource *res, int role) const { switch (role) { case Qt::DisplayRole: // xgettext: no-c-format return i18nc("%", "%1%", res->units()); case Qt::EditRole: return res->units(); case Qt::ToolTipRole: // xgettext: no-c-format return i18n("Maximum units available: %1%", res->units()); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceAllocationModel::required(const Resource *res, int role) const { switch (role) { case Qt::DisplayRole: { QStringList lst; foreach (Resource *r, res->requiredResources()) { lst << r->name(); } return lst.join(","); } case Qt::EditRole: return QVariant();//Not used case Qt::ToolTipRole: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: return QVariant(); case Qt::WhatsThisRole: return xi18nc("@info:whatsthis", "Required Resources" "A working resource can be assigned to one or more required resources." " A required resource is a material resource that the working resource depends on" " in order to do the work." "To be able to use a material resource as a required resource, the material resource" " must be part of a group of type Material."); } return QVariant(); } +QVariant ResourceAllocationModel::alternative(const Resource *res, int role) const +{ + switch (role) { + case Qt::DisplayRole: { + QStringList lst; + if (m_task) { + ResourceRequest *rr = m_task->requests().find(res); + if (rr) { + for (ResourceRequest *r : rr->alternativeRequests()) { + lst << r->resource()->name(); + } + } + } + return lst.join(","); + } + case Qt::EditRole: + return QVariant();//Not used + case Qt::ToolTipRole: + return QVariant(); + case Qt::TextAlignmentRole: + return Qt::AlignCenter; + case Qt::StatusTipRole: + return QVariant(); + case Qt::WhatsThisRole: + return xi18nc("@info:whatsthis", "Alternative Resource Allocation" + "A working resource can be assigned to one or more required resources." + " A required resource is a material resource that the working resource depends on" + " in order to do the work." + "To be able to use a material resource as a required resource, the material resource" + " must be part of a group of type Material."); + case Qt::CheckStateRole: + break; + } + return QVariant(); +} + QVariant ResourceAllocationModel::data(const Resource *resource, int property, int role) const { QVariant result; if (resource == nullptr) { return result; } switch (property) { case RequestName: result = name(resource, role); break; case RequestType: result = type(resource, role); break; case RequestAllocation: result = allocation(resource, role); break; case RequestMaximum: result = maximum(resource, role); break; + case RequestAlternative: result = alternative(resource, role); break; case RequestRequired: result = required(resource, role); break; default: debugPlan<<"data: invalid display value: property="<Required Resources" "A working resource can be assigned to one or more required resources." " A required resource is a material resource that the working resource depends on" " in order to do the work." "To be able to use a material resource as a required resource, the material resource" " must be part of a group of type Material."); default: return QVariant(); } } return QVariant(); } //-------------------------------------- ResourceAllocationItemModel::ResourceAllocationItemModel(QObject *parent) : ItemModelBase(parent) { } ResourceAllocationItemModel::~ResourceAllocationItemModel() { } void ResourceAllocationItemModel::slotResourceToBeAdded(KPlato::Project *project, int row) { Q_UNUSED(project) beginInsertRows(QModelIndex(), row, row); } void ResourceAllocationItemModel::slotResourceAdded(KPlato::Resource *resource) { Q_UNUSED(resource) endInsertRows(); } void ResourceAllocationItemModel::slotResourceToBeRemoved(KPlato::Project *project, int row, KPlato::Resource *resource) { Q_UNUSED(project) Q_UNUSED(resource) beginRemoveRows(QModelIndex(), row, row); } void ResourceAllocationItemModel::slotResourceRemoved() { endRemoveRows(); } void ResourceAllocationItemModel::setProject(Project *project) { beginResetModel(); if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceAllocationItemModel::projectDeleted); disconnect(m_project, &Project::resourceChanged, this, &ResourceAllocationItemModel::slotResourceChanged); disconnect(m_project, &Project::resourceToBeAdded, this, &ResourceAllocationItemModel::slotResourceToBeAdded); disconnect(m_project, &Project::resourceAdded, this, &ResourceAllocationItemModel::slotResourceAdded); disconnect(m_project, &Project::resourceToBeRemoved, this, &ResourceAllocationItemModel::slotResourceToBeRemoved); disconnect(m_project, &Project::resourceRemoved, this, &ResourceAllocationItemModel::slotResourceRemoved); } m_project = project; if (m_project) { disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceAllocationItemModel::projectDeleted); disconnect(m_project, &Project::resourceChanged, this, &ResourceAllocationItemModel::slotResourceChanged); disconnect(m_project, &Project::resourceToBeAdded, this, &ResourceAllocationItemModel::slotResourceToBeAdded); disconnect(m_project, &Project::resourceAdded, this, &ResourceAllocationItemModel::slotResourceAdded); disconnect(m_project, &Project::resourceToBeRemoved, this, &ResourceAllocationItemModel::slotResourceToBeRemoved); disconnect(m_project, &Project::resourceRemoved, this, &ResourceAllocationItemModel::slotResourceRemoved); } m_model.setProject(m_project); endResetModel(); } void ResourceAllocationItemModel::setTask(Task *task) { if (task == m_model.task()) { return; } if (m_model.task() == nullptr) { beginResetModel(); filldata(task); m_model.setTask(task); endResetModel(); return; } if (task) { emit layoutAboutToBeChanged(); filldata(task); m_model.setTask(task); emit layoutChanged(); } } void ResourceAllocationItemModel::filldata(Task *task) { qDeleteAll(m_resourceCache); m_resourceCache.clear(); m_requiredChecked.clear(); if (m_project && task) { for (ResourceRequest *rr : task->requests().resourceRequests()) { const Resource *r = rr->resource(); m_resourceCache[r] = new ResourceRequest(*rr); if (!m_resourceCache[r]->requiredResources().isEmpty()) { m_requiredChecked[r] = Qt::Checked; } } } } bool ResourceAllocationItemModel::hasMaterialResources() const { if (!m_project) { return false; } foreach (const Resource *r, m_project->resourceList()) { if (r->type() == Resource::Type_Material) { return true; } } return false; } Qt::ItemFlags ResourceAllocationItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = ItemModelBase::flags(index); if (!m_readWrite) { //debugPlan<<"read only"<type() != Resource::Type_Work) { flags &= ~(Qt::ItemIsEditable | Qt::ItemIsUserCheckable); } else if (m_resourceCache.contains(r) && m_resourceCache[r]->units() > 0) { flags |= (Qt::ItemIsEditable | Qt::ItemIsUserCheckable); if (!hasMaterialResources()) { flags &= ~Qt::ItemIsEnabled; } } break; } default: flags &= ~Qt::ItemIsEditable; break; } return flags; } QModelIndex ResourceAllocationItemModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } QModelIndex ResourceAllocationItemModel::index(int row, int column, const QModelIndex &parent) const { if (m_project == nullptr || column < 0 || column >= columnCount() || row < 0) { return QModelIndex(); } if (parent.isValid()) { return QModelIndex(); } Resource *r = m_project->resourceAt(row); if (r) { return createIndex(row, column, r); } return QModelIndex(); } QModelIndex ResourceAllocationItemModel::index(Resource *resource) const { if (m_project == nullptr || resource == nullptr) { return QModelIndex(); } int row = m_project->indexOf(resource); if (row >= 0) { return createIndex(row, 0, resource); } return QModelIndex(); } int ResourceAllocationItemModel::columnCount(const QModelIndex &/*parent*/) const { return m_model.propertyCount(); } int ResourceAllocationItemModel::rowCount(const QModelIndex &parent) const { if (m_project == nullptr || m_model.task() == nullptr) { return 0; } return parent.isValid() ? 0 : m_project->resourceCount(); } QVariant ResourceAllocationItemModel::allocation(const Resource *res, int role) const { if (m_model.task() == nullptr) { return QVariant(); } if (!m_resourceCache.contains(res)) { if (role == Qt::EditRole) { ResourceRequest *req = m_model.task()->requests().find(res); if (req == nullptr) { req = new ResourceRequest(const_cast(res), 0); } const_cast(this)->m_resourceCache.insert(res, req); return req->units(); } return m_model.allocation(res, role); } switch (role) { case Qt::DisplayRole: { // xgettext: no-c-format return i18nc("%", "%1%", m_resourceCache[res]->units()); } case Qt::EditRole: return m_resourceCache[res]->units(); case Qt::ToolTipRole: { if (res->units() == 0) { return xi18nc("@info:tooltip", "Not allocated"); } return xi18nc("@info:tooltip", "%1 allocated out of %2 available", allocation(res, Qt::DisplayRole).toString(), m_model.maximum(res, Qt::DisplayRole).toString()); } case Qt::CheckStateRole: return m_resourceCache[res]->units() == 0 ? Qt::Unchecked : Qt::Checked; default: return m_model.allocation(res, role); } return QVariant(); } bool ResourceAllocationItemModel::setAllocation(Resource *res, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { m_resourceCache[res]->setUnits(value.toInt()); QModelIndex idx = index(res); emit dataChanged(idx, idx.sibling(idx.row(), ResourceAllocationModel::RequestAllocation)); return true; } case Qt::CheckStateRole: { if (!m_resourceCache.contains(res)) { m_resourceCache[res] = new ResourceRequest(res, 0); } if (m_resourceCache[res]->units() == 0) { m_resourceCache[res]->setUnits(100); } else { m_resourceCache[res]->setUnits(0); } QModelIndex idx = index(res); emit dataChanged(idx, idx); return true; } } return false; } QVariant ResourceAllocationItemModel::required(const QModelIndex &idx, int role) const { if (m_model.task() == nullptr) { return QVariant(); } Resource *res = resource(idx); if (res == nullptr) { return QVariant(); } switch (role) { case Qt::DisplayRole: { if (res->type() == Resource::Type_Work) { QStringList lst; if (m_requiredChecked[res]) { foreach (const Resource *r, required(idx)) { lst << r->name(); } } return lst.isEmpty() ? i18n("None") : lst.join(","); } break; } case Qt::EditRole: break; case Qt::ToolTipRole: switch (res->type()) { case Resource::Type_Work: { if (!hasMaterialResources()) { return xi18nc("@info:tooltip", "No material resources available"); } QStringList lst; if (m_requiredChecked[res]) { foreach (const Resource *r, required(idx)) { lst << r->name(); } } return lst.isEmpty() ? xi18nc("@info:tooltip", "No required resources") : lst.join("\n"); } case Resource::Type_Material: return xi18nc("@info:tooltip", "Material resources cannot have required resources"); case Resource::Type_Team: return xi18nc("@info:tooltip", "Team resources cannot have required resources"); } break; case Qt::CheckStateRole: if (res->type() == Resource::Type_Work) { return m_requiredChecked[res]; } break; default: return m_model.required(res, role); } return QVariant(); } bool ResourceAllocationItemModel::setRequired(const QModelIndex &idx, const QVariant &value, int role) { Resource *res = resource(idx); if (res == nullptr) { return false; } switch (role) { case Qt::CheckStateRole: m_requiredChecked[res] = value.toInt(); if (value.toInt() == Qt::Unchecked) { m_resourceCache[res]->setRequiredResources(QList()); } emit dataChanged(idx, idx); return true; } return false; } +QVariant ResourceAllocationItemModel::alternative(const QModelIndex &idx, int role) const +{ + if (m_model.task() == nullptr) { + return QVariant(); + } + Resource *res = resource(idx); + if (res == nullptr) { + return QVariant(); + } + switch (role) { + case Qt::DisplayRole: { + QStringList lst; + ResourceRequest *rr = m_resourceCache.value(res); + if (rr) { + for (ResourceRequest *r : rr->alternativeRequests()) { + lst << r->resource()->name(); + } + } + return lst.isEmpty() ? i18n("None") : lst.join(","); + } + case Qt::EditRole: break; + case Qt::ToolTipRole: break; + default: break; + } + return QVariant(); +} + QVariant ResourceAllocationItemModel::data(const QModelIndex &index, int role) const { if (role == Qt::TextAlignmentRole) { // use same alignment as in header (headers always horizontal) return headerData(index.column(), Qt::Horizontal, role); } QVariant result; Resource *r = resource(index); if (r) { if (index.column() == ResourceAllocationModel::RequestAllocation) { return allocation(r, role); } if (index.column() == ResourceAllocationModel::RequestRequired) { return required(index, role); } + if (index.column() == ResourceAllocationModel::RequestAlternative) { + return alternative(index, role); + } result = m_model.data(r, index.column(), role); } return result; } bool ResourceAllocationItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return ItemModelBase::setData(index, value, role); } if (!(flags(index) & Qt::ItemIsEditable)) { return false; } QObject *obj = object(index); Resource *r = qobject_cast(obj); if (r) { switch (index.column()) { case ResourceAllocationModel::RequestAllocation: if (setAllocation(r, value, role)) { emit dataChanged(index, index); QModelIndex idx = this->index(index.row(), ResourceAllocationModel::RequestAllocation, parent(parent(index))); emit dataChanged(idx, idx); return true; } return false; + case ResourceAllocationModel::RequestAlternative: + return false; case ResourceAllocationModel::RequestRequired: return setRequired(index, value, role); default: //qWarning("data: invalid display value column %d", index.column()); return false; } } return false; } QVariant ResourceAllocationItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { return m_model.headerData(section, role); } if (role == Qt::TextAlignmentRole) { switch (section) { case 0: return QVariant(); default: return Qt::AlignCenter; } return Qt::AlignCenter; } } return m_model.headerData(section, role); } QAbstractItemDelegate *ResourceAllocationItemModel::createDelegate(int col, QWidget *parent) const { + QAbstractItemDelegate *del = nullptr; switch (col) { - case ResourceAllocationModel::RequestAllocation: return new SpinBoxDelegate(parent); - case ResourceAllocationModel::RequestRequired: return new RequieredResourceDelegate(parent); + case ResourceAllocationModel::RequestAllocation: del = new SpinBoxDelegate(parent); break; + case ResourceAllocationModel::RequestAlternative: del = new AlternativeResourceDelegate(parent); break; + case ResourceAllocationModel::RequestRequired: del = new RequieredResourceDelegate(parent); break; default: break; } - return 0; + return del; } QObject *ResourceAllocationItemModel::object(const QModelIndex &index) const { QObject *o = 0; if (index.isValid()) { o = static_cast(index.internalPointer()); Q_ASSERT(o); } return o; } void ResourceAllocationItemModel::slotResourceChanged(Resource *res) { QModelIndex idx = index(res); emit dataChanged(idx, idx.sibling(idx.row(), columnCount()-1)); } Resource *ResourceAllocationItemModel::resource(const QModelIndex &idx) const { return qobject_cast(object(idx)); } void ResourceAllocationItemModel::setRequired(const QModelIndex &idx, const QList &lst) { Resource *r = resource(idx); Q_ASSERT(r); if (m_resourceCache.contains(r)) { m_resourceCache[r]->setRequiredResources(lst); emit dataChanged(idx, idx); } } QList ResourceAllocationItemModel::required(const QModelIndex &idx) const { Resource *r = resource(idx); Q_ASSERT(r); if (m_resourceCache.contains(r)) { ResourceRequest* request = m_resourceCache[r]; return request->requiredResources(); } return r->requiredResources(); } + +void ResourceAllocationItemModel::setAlternativeRequests(const QModelIndex &idx, const QList &lst) +{ + Resource *r = resource(idx); + Q_ASSERT(r); + if (m_resourceCache.contains(r)) { + QList requests; + for (Resource *r : lst) { + requests << new ResourceRequest(r); + } + m_resourceCache[r]->setAlternativeRequests(requests); + emit dataChanged(idx, idx); + } +} + +QList ResourceAllocationItemModel::alternativeRequests(const QModelIndex &idx) const +{ + QList alternatives; + ResourceRequest *request = m_resourceCache.value(resource(idx)); + if (request) { + alternatives = request->alternativeRequests(); + } + return alternatives; +} diff --git a/src/libs/models/kptresourceallocationmodel.h b/src/libs/models/kptresourceallocationmodel.h index e1b12175..78cb72a1 100644 --- a/src/libs/models/kptresourceallocationmodel.h +++ b/src/libs/models/kptresourceallocationmodel.h @@ -1,156 +1,163 @@ /* This file is part of the KDE project Copyright (C) 2009 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 KPTRESOURCEALLOCATIONMODEL_H #define KPTRESOURCEALLOCATIONMODEL_H #include "planmodels_export.h" #include #include #include namespace KPlato { class Project; class Task; class Resource; class ResourceGroup; class ResourceRequest; class ResourceGroupRequest; /** The ResourceAllocationModel gives access to resource requests */ class PLANMODELS_EXPORT ResourceAllocationModel : public QObject { Q_OBJECT public: explicit ResourceAllocationModel(QObject *parent = 0); ~ResourceAllocationModel() override; enum Properties { RequestName = 0, RequestType, RequestAllocation, RequestMaximum, + RequestAlternative, RequestRequired }; Q_ENUM(Properties) const QMetaEnum columnMap() const; void setProject(Project *project); Task *task() const { return m_task; } void setTask(Task *task); int propertyCount() const; QVariant data(const Resource *resource, int property, int role = Qt::DisplayRole) const; static QVariant headerData(int section, int role = Qt::DisplayRole); QVariant name(const Resource *res, int role) const; QVariant type(const Resource *res, int role) const; QVariant allocation(const Resource *res, int role) const; QVariant maximum(const Resource *res, int role) const; QVariant required(const Resource *res, int role) const; + QVariant alternative(const Resource *res, int role) const; private: Project *m_project; Task *m_task; }; /** The ResourceAllocationItemModel facilitates viewing and modifying resource allocations for a task. */ class PLANMODELS_EXPORT ResourceAllocationItemModel : public ItemModelBase { Q_OBJECT public: explicit ResourceAllocationItemModel(QObject *parent = 0); ~ResourceAllocationItemModel() override; const QMetaEnum columnMap() const override { return m_model.columnMap(); } void setProject(Project *project) override; Task *task() const { return m_model.task(); } Qt::ItemFlags flags(const QModelIndex & index) const override; QModelIndex parent(const QModelIndex & index) const override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; QModelIndex index(Resource *resource) const; int columnCount(const QModelIndex & parent = QModelIndex()) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QAbstractItemDelegate *createDelegate(int col, QWidget *parent) const override; QObject *object(const QModelIndex &index) const; const QHash &resourceCache() const { return m_resourceCache; } Resource *resource(const QModelIndex &idx) const; void setRequired(const QModelIndex &idx, const QList &lst); QList required(const QModelIndex &idx) const; + void setAlternativeRequests(const QModelIndex &idx, const QList &lst); + QList alternativeRequests(const QModelIndex &idx) const; + public Q_SLOTS: void setTask(KPlato::Task *task); protected Q_SLOTS: void slotResourceChanged(KPlato::Resource*); void slotResourceToBeAdded(KPlato::Project *project, int row); void slotResourceAdded(KPlato::Resource *resource); void slotResourceToBeRemoved(KPlato::Project *project, int row, KPlato::Resource *resource); void slotResourceRemoved(); protected: void filldata(Task *task); QVariant allocation(const Resource *res, int role) const; bool setAllocation(Resource *res, const QVariant &value, int role); bool setRequired(const QModelIndex &idx, const QVariant &value, int role); QVariant required(const QModelIndex &idx, int role) const; + QVariant alternative(const QModelIndex &idx, int role) const; + private: int requestedResources(const ResourceGroup *res) const; bool hasMaterialResources() const; private: ResourceAllocationModel m_model; QHash m_resourceCache; QHash m_requiredChecked; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kptrequestresourcespanel.cpp b/src/libs/ui/kptrequestresourcespanel.cpp index 3a9c1a3f..897f111f 100644 --- a/src/libs/ui/kptrequestresourcespanel.cpp +++ b/src/libs/ui/kptrequestresourcespanel.cpp @@ -1,106 +1,113 @@ /* This file is part of the KDE project * Copyright (C) 2003 - 2009 Dag Andersen * Copyright (C) 2020 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 "kptrequestresourcespanel.h" #include #include #include #include +#include #include #include #include #include #include using namespace KPlato; RequestResourcesPanel::RequestResourcesPanel(QWidget *parent, Project &project, Task &task, bool) : QWidget(parent) { m_model.setReadWrite(true); m_model.setProject(&project); m_model.setTask(&task); QVBoxLayout *l = new QVBoxLayout(this); l->setMargin(0); m_view = new QTreeView(this); m_view->setModel(&m_model); m_view->setRootIsDecorated(false); l->addWidget(m_view); m_view->header()->resizeSections(QHeaderView::ResizeToContents); + for (int i = 0; i < m_model.columnCount(); ++i) { + m_view->setItemDelegateForColumn(i, m_model.createDelegate(i, m_view)); + } connect(&m_model, &ResourceAllocationItemModel::dataChanged, this, &RequestResourcesPanel::changed); } bool RequestResourcesPanel::ok() { return true; } MacroCommand *RequestResourcesPanel::buildCommand() { return buildCommand(m_model.task()); } MacroCommand *RequestResourcesPanel::buildCommand(Task *task) { if (task == nullptr) { return nullptr; } MacroCommand *cmd = new MacroCommand(kundo2_i18n("Modify resource allocations")); const QHash &rmap = m_model.resourceCache(); // First remove all that should be removed for(QHash::const_iterator rit = rmap.constBegin(); rit != rmap.constEnd(); ++rit) { if (rit.value()->units() == 0) { ResourceRequest *rr = task->requests().find(rit.key()); if (rr) { cmd->addCommand(new RemoveResourceRequestCmd(rr)); } } } for(QHash::const_iterator rit = rmap.constBegin(); rit != rmap.constEnd(); ++rit) { Resource *resource = const_cast(rit.key()); if (rit.value()->units() > 0) { ResourceRequest *rr = task->requests().find(resource); if (rr == nullptr) { ResourceRequest *rr = new ResourceRequest(resource, rit.value()->units()); rr->setRequiredResources(rit.value()->requiredResources()); cmd->addCommand(new AddResourceRequestCmd(&task->requests(), rr)); } else { if (rit.value()->units() != rr->units()) { cmd->addCommand(new ModifyResourceRequestUnitsCmd(rr, rr->units(), rit.value()->units())); } if (rit.value()->requiredResources() != rr->requiredResources()) { cmd->addCommand(new ModifyResourceRequestRequiredCmd(rr, rit.value()->requiredResources())); } + if (rit.value()->alternativeRequests() != rr->alternativeRequests()) { + cmd->addCommand(new ModifyResourceRequestAlternativeCmd(rr, rit.value()->alternativeRequests())); + } } } } if (cmd->isEmpty()) { delete cmd; cmd = nullptr; } return cmd; }