diff --git a/src/kptconfig.cpp b/src/kptconfig.cpp index a4849287..6e9d1a9e 100644 --- a/src/kptconfig.cpp +++ b/src/kptconfig.cpp @@ -1,367 +1,366 @@ /* This file is part of the KDE project Copyright (C) 2004, 2007 Dag Andersen Copyright (C) 2011, 2012 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 "kptconfig.h" #include "calligraplansettings.h" #include "kptconfigskeleton.h" #include "kptfactory.h" #include "kptproject.h" #include "kptdebug.h" #include namespace KPlato { Config::Config() : ConfigBase() { debugPlan<<"Leader:"<save(); } bool Config::isWorkingday(int day) const { switch (day) { case Qt::Monday: return KPlatoSettings::monday(); break; case Qt::Tuesday: return KPlatoSettings::tuesday(); break; case Qt::Wednesday: return KPlatoSettings::wednesday(); break; case Qt::Thursday: return KPlatoSettings::thursday(); break; case Qt::Friday: return KPlatoSettings::friday(); break; case Qt::Saturday: return KPlatoSettings::saturday(); break; case Qt::Sunday: return KPlatoSettings::sunday(); break; default: break; }; return false; } QTime Config::dayStartTime(int day) const { switch (day) { case Qt::Monday: return QTime::fromString(KPlatoSettings::mondayStart()); break; case Qt::Tuesday: return QTime::fromString(KPlatoSettings::tuesdayStart()); break; case Qt::Wednesday: return QTime::fromString(KPlatoSettings::wednesdayStart()); break; case Qt::Thursday: return QTime::fromString(KPlatoSettings::thursdayStart()); break; case Qt::Friday: return QTime::fromString(KPlatoSettings::fridayStart()); break; case Qt::Saturday: return QTime::fromString(KPlatoSettings::saturdayStart()); break; case Qt::Sunday: return QTime::fromString(KPlatoSettings::sundayStart()); break; default: break; }; return QTime(); } int Config::dayLength(int day) const { QTime start = dayStartTime(day); QTime end; int value = 0; switch (day) { case Qt::Monday: end = QTime::fromString(KPlatoSettings::mondayEnd()); break; case Qt::Tuesday: end = QTime::fromString(KPlatoSettings::tuesdayEnd()); break; case Qt::Wednesday: end = QTime::fromString(KPlatoSettings::wednesdayEnd()); break; case Qt::Thursday: end = QTime::fromString(KPlatoSettings::thursdayEnd()); break; case Qt::Friday: end = QTime::fromString(KPlatoSettings::fridayEnd()); break; case Qt::Saturday: end = QTime::fromString(KPlatoSettings::saturdayEnd()); break; case Qt::Sunday: end = QTime::fromString(KPlatoSettings::sundayEnd()); break; default: break; }; value = start.msecsTo(end); if (value < 0) { value = (24*60*60*1000) + value; } else if (value == 0 && start == QTime(0, 0)) { value = 24*60*60*1000; } return value; } void Config::setDefaultValues(Project &project) const { project.setLeader(KPlatoSettings::manager()); project.setUseSharedResources(KPlatoSettings::useSharedResources()); project.setSharedResourcesFile(KPlatoSettings::sharedResourcesFile()); project.setSharedProjectsUrl(QUrl(KPlatoSettings::sharedProjectsPlace())); project.setDescription(KPlatoSettings::projectDescription()); StandardWorktime *v = project.standardWorktime(); Q_ASSERT(v); if (v) { v->setYear(KPlatoSettings::hoursPrYear()); v->setMonth(KPlatoSettings::hoursPrMonth()); v->setWeek(KPlatoSettings::hoursPrWeek()); v->setDay(KPlatoSettings::hoursPrDay()); } Project::WorkPackageInfo wpi; wpi.checkForWorkPackages = KPlatoSettings::checkForWorkPackages(); wpi.retrieveUrl = KPlatoSettings::retrieveUrl(); wpi.deleteAfterRetrieval = KPlatoSettings::deleteFile(); wpi.archiveAfterRetrieval = !KPlatoSettings::deleteFile(); wpi.archiveUrl = KPlatoSettings::saveUrl(); #if 0 // not used atm wpi.publishUrl = KPlatoSettings::publishUrl(); #endif project.setWorkPackageInfo(wpi); - project.setUseLocalTaskModules(useLocalTaskModules()); QList urls; for (const QString &s : taskModulePaths()) { urls << QUrl::fromUserInput(s); } - project.setTaskModules(urls); + project.setTaskModules(urls, useLocalTaskModules()); } void Config::setDefaultValues(Task &task) { task.setLeader(KPlatoSettings::leader()); task.setDescription(KPlatoSettings::description()); task.setConstraint((Node::ConstraintType) KPlatoSettings::constraintType()); // avoid problems with start <= end & end >= start task.setConstraintStartTime(DateTime()); task.setConstraintEndTime(DateTime()); switch (KPlatoSettings::startTimeUsage()) { case KPlatoSettings::EnumStartTimeUsage::CurrentdateTime: task.setConstraintStartTime(DateTime(QDateTime::currentDateTime())); break; case KPlatoSettings::EnumStartTimeUsage::CurrentDate: task.setConstraintStartTime(DateTime(QDate::currentDate(), KPlatoSettings::constraintStartTime().time())); break; case KPlatoSettings::EnumStartTimeUsage::SpecifiedDateTime: //fall through default: task.setConstraintStartTime(DateTime(KPlatoSettings::constraintStartTime())); break; } switch (KPlatoSettings::endTimeUsage()) { case KPlatoSettings::EnumEndTimeUsage::CurrentdateTime: task.setConstraintEndTime(DateTime(QDateTime::currentDateTime())); break; case KPlatoSettings::EnumEndTimeUsage::CurrentDate: task.setConstraintEndTime(DateTime(QDate::currentDate(), KPlatoSettings::constraintEndTime().time())); break; case KPlatoSettings::EnumEndTimeUsage::SpecifiedDateTime: //fall through default: task.setConstraintEndTime(DateTime(KPlatoSettings::constraintEndTime())); break; } task.estimate()->setType((Estimate::Type) KPlatoSettings::estimateType()); task.estimate()->setUnit((Duration::Unit) KPlatoSettings::unit()); task.estimate()->setExpectedEstimate(KPlatoSettings::expectedEstimate()); task.estimate()->setPessimisticRatio(KPlatoSettings::pessimisticRatio()); task.estimate()->setOptimisticRatio(KPlatoSettings::optimisticRatio()); } int Config::minimumDurationUnit() const { return KPlatoSettings::minimumDurationUnit(); } int Config::maximumDurationUnit() const { return KPlatoSettings::maximumDurationUnit(); } QBrush Config::summaryTaskDefaultColor() const { QColor c = KPlatoSettings::summaryTaskDefaultColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } bool Config::summaryTaskLevelColorsEnabled() const { return KPlatoSettings::summaryTaskLevelColorsEnabled(); } QBrush Config::summaryTaskLevelColor_1() const { QColor c = KPlatoSettings::summaryTaskLevelColor_1(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::summaryTaskLevelColor_2() const { QColor c = KPlatoSettings::summaryTaskLevelColor_2(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::summaryTaskLevelColor_3() const { QColor c = KPlatoSettings::summaryTaskLevelColor_3(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::summaryTaskLevelColor_4() const { QColor c = KPlatoSettings::summaryTaskLevelColor_4(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::taskNormalColor() const { QColor c = KPlatoSettings::taskNormalColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::taskErrorColor() const { QColor c = KPlatoSettings::taskErrorColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::taskCriticalColor() const { QColor c = KPlatoSettings::taskCriticalColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::taskFinishedColor() const { QColor c = KPlatoSettings::taskFinishedColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::milestoneNormalColor() const { QColor c = KPlatoSettings::milestoneNormalColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::milestoneErrorColor() const { QColor c = KPlatoSettings::milestoneErrorColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::milestoneCriticalColor() const { QColor c = KPlatoSettings::milestoneCriticalColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QBrush Config::milestoneFinishedColor() const { QColor c = KPlatoSettings::milestoneFinishedColor(); if (KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear) { return gradientBrush(c); } return c; } QString Config::documentationPath() const { return KPlatoSettings::documentationPath(); } QString Config::contextPath() const { return KPlatoSettings::contextPath(); } QString Config::contextLanguage() const { return KPlatoSettings::contextLanguage(); } bool Config::useLocalTaskModules() const { return KPlatoSettings::useLocalTaskModules(); } QStringList Config::taskModulePaths() const { return KPlatoSettings::taskModulePaths(); } QStringList Config::projectTemplatePaths() const { return KPlatoSettings::projectTemplatePaths(); } } //KPlato namespace diff --git a/src/libs/kernel/CMakeLists.txt b/src/libs/kernel/CMakeLists.txt index 09f310b0..3877e83b 100644 --- a/src/libs/kernel/CMakeLists.txt +++ b/src/libs/kernel/CMakeLists.txt @@ -1,60 +1,64 @@ if(BUILD_TESTING) add_subdirectory( tests ) endif() -include_directories(${PLANODF_INCLUDES} - ${PLANKUNDO2_INCLUDES}) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR/commands} + ${PLANODF_INCLUDES} + ${PLANKUNDO2_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 kptresource.cpp kpttask.cpp kptduration.cpp kptdatetime.cpp kptcalendar.cpp kptschedule.cpp kptwbsdefinition.cpp kptcommand.cpp kptpackage.cpp kptdebug.cpp + commands/SetTaskModulesCommand.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 diff --git a/src/libs/kernel/commands/SetTaskModulesCommand.cpp b/src/libs/kernel/commands/SetTaskModulesCommand.cpp new file mode 100644 index 00000000..d28fa0df --- /dev/null +++ b/src/libs/kernel/commands/SetTaskModulesCommand.cpp @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + * 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 "SetTaskModulesCommand.h" + +#include "kptaccount.h" +#include "kptappointment.h" +#include "kptproject.h" +#include "kpttask.h" +#include "kptcalendar.h" +#include "kptrelation.h" +#include "kptresource.h" +#include "kptdocuments.h" +#include "kptlocale.h" +#include "kptdebug.h" + +#include + + +const QLoggingCategory &PLANCMDSETTASKMODULES_LOG() +{ + static const QLoggingCategory category("calligra.plan.command.settaskmodules"); + return category; +} + +#define debugPlanSetTaskModules qCDebug(PLANCMDSETTASKMODULES_LOG) +#define warnPlanSetTaskModules qCWarning(PLANCMDSETTASKMODULES_LOG) +#define errorPlanSetTaskModules qCCritical(PLANCMDSETTASKMODULES_LOG) + +using namespace KPlato; + +SetTaskModulesCommand::SetTaskModulesCommand(Project *project, const QList &modules, bool useLocalTaskModules, const KUndo2MagicString& name) + : MacroCommand(name) + , m_project(project) + , newModules(modules) + , newUseLocalTaskModules(useLocalTaskModules) +{ + oldModules = project->taskModules(false); + oldUseLocalTaskModules = project->useLocalTaskModules(); +} + +SetTaskModulesCommand::~SetTaskModulesCommand() +{ +} + +void SetTaskModulesCommand::execute() +{ + m_project->setTaskModules(newModules, newUseLocalTaskModules); +} + +void SetTaskModulesCommand::unexecute() +{ + m_project->setTaskModules(oldModules, oldUseLocalTaskModules); +} diff --git a/src/libs/kernel/commands/SetTaskModulesCommand.h b/src/libs/kernel/commands/SetTaskModulesCommand.h new file mode 100644 index 00000000..eb8729e0 --- /dev/null +++ b/src/libs/kernel/commands/SetTaskModulesCommand.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + * Copyright (C) 2019 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SETTASKMODULESCOMMAND_H +#define SETTASKMODULESCOMMAND_H + +#include "plankernel_export.h" + +#include "kptcommand.h" +#include "kptxmlloaderobject.h" +#include "KoXmlReader.h" + +#include + + +class QString; + +/// The main namespace +namespace KPlato +{ + +class Project; +class Node; + + +class PLANKERNEL_EXPORT SetTaskModulesCommand : public MacroCommand +{ +public: + SetTaskModulesCommand(Project *project, const QList &modules, bool useLocalTaskModules, const KUndo2MagicString& name = KUndo2MagicString()); + ~SetTaskModulesCommand() override; + void execute() override; + void unexecute() override; + +private: + Project *m_project; + QList oldModules; + bool oldUseLocalTaskModules; + QList newModules; + bool newUseLocalTaskModules; +}; + + +} //KPlato namespace + +#endif diff --git a/src/libs/kernel/kptproject.cpp b/src/libs/kernel/kptproject.cpp index 6d84fad7..95b4af1e 100644 --- a/src/libs/kernel/kptproject.cpp +++ b/src/libs/kernel/kptproject.cpp @@ -1,3101 +1,3104 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2010, 2012 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard 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 "kptproject.h" #include "kptlocale.h" #include "kptappointment.h" #include "kpttask.h" #include "kptdatetime.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptwbsdefinition.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "kptschedulerplugin.h" #include "kptdebug.h" #include #include #include #include #include #include namespace KPlato { QString generateId() { return QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMddHHmmss")) + KRandom::randomString(10); } Project::Project(Node *parent) : Node(parent), m_accounts(*this), m_defaultCalendar(0), m_config(&emptyConfig), m_schedulerPlugins(), m_useSharedResources(false), m_sharedResourcesLoaded(false), m_loadProjectsAtStartup(false), m_useLocalTaskModules(true) { //debugPlan<<"("<setDefaultValues(*this); } Project::Project(ConfigBase &config, bool useDefaultValues, Node *parent) : Node(parent), m_accounts(*this), m_defaultCalendar(0), m_config(&config), m_schedulerPlugins(), m_useSharedResources(false), m_sharedResourcesLoaded(false), m_loadProjectsAtStartup(false), m_useLocalTaskModules(true) { debugPlan<<"("<setDefaultValues(*this); } } void Project::init() { m_refCount = 1; // always used by creator m_constraint = Node::MustStartOn; m_standardWorktime = new StandardWorktime(); m_timeZone = QTimeZone::systemTimeZone(); // local timezone as default //debugPlan<= 0); if (m_refCount <= 0) { emit aboutToBeDeleted(); deleteLater(); } } Project::~Project() { debugPlan<<"("<blockChanged(); } for (Resource *r : qAsConst(resourceIdDict)) { r->blockChanged(); } for (ResourceGroup *g : qAsConst(resourceGroupIdDict)) { g->blockChanged(); } delete m_standardWorktime; while (!m_resourceGroups.isEmpty()) delete m_resourceGroups.takeFirst(); while (!m_calendars.isEmpty()) delete m_calendars.takeFirst(); while (!m_managers.isEmpty()) delete m_managers.takeFirst(); m_config = 0; //not mine, don't delete } int Project::type() const { return Node::Type_Project; } void Project::generateUniqueNodeIds() { foreach (Node *n, nodeIdDict) { debugPlan<name()<<"old"<id(); QString uid = uniqueNodeId(); nodeIdDict.remove(n->id()); n->setId(uid); nodeIdDict[ uid ] = n; debugPlan<name()<<"new"<id(); } } void Project::generateUniqueIds() { generateUniqueNodeIds(); foreach (ResourceGroup *g, resourceGroupIdDict) { if (g->isShared()) { continue; } resourceGroupIdDict.remove(g->id()); g->setId(uniqueResourceGroupId()); resourceGroupIdDict[ g->id() ] = g; } foreach (Resource *r, resourceIdDict) { if (r->isShared()) { continue; } resourceIdDict.remove(r->id()); r->setId(uniqueResourceId()); resourceIdDict[ r->id() ] = r; } foreach (Calendar *c, calendarIdDict) { if (c->isShared()) { continue; } calendarIdDict.remove(c->id()); c->setId(uniqueCalendarId()); calendarIdDict[ c->id() ] = c; } } void Project::calculate(Schedule *schedule, const DateTime &dt) { if (schedule == 0) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate(dt); } void Project::calculate(const DateTime &dt) { if (m_currentSchedule == 0) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; QLocale locale; DateTime time = dt.isValid() ? dt : DateTime(QDateTime::currentDateTime()); MainSchedule *cs = static_cast(m_currentSchedule); Estimate::Use estType = (Estimate::Use) cs->type(); if (type() == Type_Project) { cs->setPhaseName(0, i18n("Init")); cs->logInfo(i18n("Schedule project from: %1", locale.toString(dt, QLocale::ShortFormat)), 0); initiateCalculation(*cs); initiateCalculationLists(*cs); // must be after initiateCalculation() !! propagateEarliestStart(time); // Calculate lateFinish from time. If a task has started, remainingEffort is used. cs->setPhaseName(1, i18nc("Schedule project forward", "Forward")); cs->logInfo(i18n("Calculate finish"), 1); cs->lateFinish = calculateForward(estType); cs->lateFinish = checkEndConstraints(cs->lateFinish); propagateLatestFinish(cs->lateFinish); // Calculate earlyFinish. If a task has started, remainingEffort is used. cs->setPhaseName(2, i18nc("Schedule project backward","Backward")); cs->logInfo(i18n("Calculate start"), 2); calculateBackward(estType); // Schedule. If a task has started, remainingEffort is used and appointments are copied from parent cs->setPhaseName(3, i18n("Schedule")); cs->logInfo(i18n("Schedule tasks forward"), 3); cs->endTime = scheduleForward(cs->startTime, estType); cs->logInfo(i18n("Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat)), 3); if (cs->endTime > m_constraintEndTime) { cs->logError(i18n("Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else if (cs->endTime == m_constraintEndTime) { cs->logWarning(i18n("Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else { cs->logInfo(i18n("Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } calcCriticalPath(false); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged(cs); emit projectChanged(); } else if (type() == Type_Subproject) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::calculate(ScheduleManager &sm) { emit sigCalculationStarted(this, &sm); sm.setScheduling(true); m_progress = 0; int nodes = 0; foreach (Node *n, nodeIdDict) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { nodes++; } } int maxprogress = nodes * 3; if (sm.recalculate()) { emit maxProgress(maxprogress); sm.setMaxProgress(maxprogress); incProgress(); if (sm.parentManager()) { sm.expected()->startTime = sm.parentManager()->expected()->startTime; sm.expected()->earlyStart = sm.parentManager()->expected()->earlyStart; } incProgress(); calculate(sm.expected(), sm.recalculateFrom()); } else { emit maxProgress(maxprogress); sm.setMaxProgress(maxprogress); calculate(sm.expected()); emit scheduleChanged(sm.expected()); setCurrentSchedule(sm.expected()->id()); } emit sigProgress(maxprogress); emit sigCalculationFinished(this, &sm); emit scheduleManagerChanged(&sm); emit projectCalculated(&sm); emit projectChanged(); sm.setScheduling(false); } void Project::calculate(Schedule *schedule) { if (schedule == 0) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate(); } void Project::calculate() { if (m_currentSchedule == 0) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; MainSchedule *cs = static_cast(m_currentSchedule); bool backwards = false; if (cs->manager()) { backwards = cs->manager()->schedulingDirection(); } QLocale locale; Estimate::Use estType = (Estimate::Use) cs->type(); if (type() == Type_Project) { QTime timer; timer.start(); initiateCalculation(*cs); initiateCalculationLists(*cs); // must be after initiateCalculation() !! if (! backwards) { cs->setPhaseName(0, i18n("Init")); cs->logInfo(i18n("Schedule project forward from: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 0); cs->startTime = m_constraintStartTime; cs->earlyStart = m_constraintStartTime; // Calculate from start time propagateEarliestStart(cs->earlyStart); cs->setPhaseName(1, i18nc("Schedule project forward", "Forward")); cs->logInfo(i18n("Calculate late finish"), 1); cs->lateFinish = calculateForward(estType); // cs->lateFinish = checkEndConstraints(cs->lateFinish); cs->logInfo(i18n("Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat)), 1); propagateLatestFinish(cs->lateFinish); cs->setPhaseName(2, i18nc("Schedule project backward", "Backward")); cs->logInfo(i18n("Calculate early start"), 2); calculateBackward(estType); cs->setPhaseName(3, i18n("Schedule")); cs->logInfo(i18n("Schedule tasks forward"), 3); cs->endTime = scheduleForward(cs->startTime, estType); cs->duration = cs->endTime - cs->startTime; cs->logInfo(i18n("Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat)), 3); if (cs->endTime > m_constraintEndTime) { cs->constraintError = true; cs->logError(i18n("Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else if (cs->endTime == m_constraintEndTime) { cs->logWarning(i18n("Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } else { cs->logInfo(i18n("Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3); } calcCriticalPath(false); } else { cs->setPhaseName(0, i18n("Init")); cs->logInfo(i18n("Schedule project backward from: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 0); // Calculate from end time propagateLatestFinish(m_constraintEndTime); cs->setPhaseName(1, i18nc("Schedule project backward", "Backward")); cs->logInfo(i18n("Calculate early start"), 1); cs->earlyStart = calculateBackward(estType); // cs->earlyStart = checkStartConstraints(cs->earlyStart); cs->logInfo(i18n("Early start calculated: %1", locale.toString(cs->earlyStart, QLocale::ShortFormat)), 1); propagateEarliestStart(cs->earlyStart); cs->setPhaseName(2, i18nc("Schedule project forward", "Forward")); cs->logInfo(i18n("Calculate late finish"), 2); cs->lateFinish = qMax(m_constraintEndTime, calculateForward(estType)); cs->logInfo(i18n("Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat)), 2); cs->setPhaseName(3, i18n("Schedule")); cs->logInfo(i18n("Schedule tasks backward"), 3); cs->startTime = scheduleBackward(cs->lateFinish, estType); cs->endTime = cs->startTime; foreach (Node *n, allNodes()) { if (n->type() == Type_Task || n->type() == Type_Milestone) { DateTime e = n->endTime(cs->id()); if (cs->endTime < e) { cs->endTime = e; } } } if (cs->endTime > m_constraintEndTime) { cs->constraintError = true; cs->logError(i18n("Failed to finish project within target time"), 3); } cs->duration = cs->endTime - cs->startTime; cs->logInfo(i18n("Scheduled start: %1, target time: %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->logError(i18n("Must start project early in order to finish in time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); } else if (cs->startTime == m_constraintStartTime) { cs->logWarning(i18n("Start project exactly on time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); } else { cs->logInfo(i18n("Can start project later than time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3); } calcCriticalPath(true); } cs->logInfo(i18n("Calculation took: %1", KFormat().formatDuration(timer.elapsed()))); // TODO: fix this uncertainty, manager should *always* be available if (cs->manager()) { finishCalculation(*(cs->manager())); } } else if (type() == Type_Subproject) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::finishCalculation(ScheduleManager &sm) { MainSchedule *cs = sm.expected(); if (nodeIdDict.count() > 1) { // calculate project duration cs->startTime = m_constraintEndTime; cs->endTime = m_constraintStartTime; for (const Node *n : qAsConst(nodeIdDict)) { cs->startTime = qMin(cs->startTime, n->startTime(cs->id())); cs->endTime = qMax(cs->endTime, n->endTime(cs->id())); } cs->duration = cs->endTime - cs->startTime; } calcCriticalPath(false); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged(cs); emit projectChanged(); debugPlan<startTime<endTime<<"-------------------------"; } void Project::setProgress(int progress, ScheduleManager *sm) { m_progress = progress; if (sm) { sm->setProgress(progress); } emit sigProgress(progress); } void Project::setMaxProgress(int max, ScheduleManager *sm) { if (sm) { sm->setMaxProgress(max); } emitMaxProgress(max); } void Project::incProgress() { m_progress += 1; emit sigProgress(m_progress); } void Project::emitMaxProgress(int value) { emit maxProgress(value); } bool Project::calcCriticalPath(bool fromEnd) { //debugPlan; MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0) { return false; } if (fromEnd) { QListIterator startnodes = cs->startNodes(); while (startnodes.hasNext()) { startnodes.next() ->calcCriticalPath(fromEnd); } } else { QListIterator endnodes = cs->endNodes(); while (endnodes.hasNext()) { endnodes.next() ->calcCriticalPath(fromEnd); } } calcCriticalPathList(cs); return false; } void Project::calcCriticalPathList(MainSchedule *cs) { //debugPlan<name(); cs->clearCriticalPathList(); foreach (Node *n, allNodes()) { if (n->numDependParentNodes() == 0 && n->inCriticalPath(cs->id())) { cs->addCriticalPath(); cs->addCriticalPathNode(n); calcCriticalPathList(cs, n); } } cs->criticalPathListCached = true; //debugPlan<<*(criticalPathList(cs->id())); } void Project::calcCriticalPathList(MainSchedule *cs, Node *node) { //debugPlan<name()<<", "<id(); bool newPath = false; QList lst = *(cs->currentCriticalPath()); foreach (Relation *r, node->dependChildNodes()) { if (r->child()->inCriticalPath(cs->id())) { if (newPath) { cs->addCriticalPath(&lst); //debugPlan<name()<<" new path"; } cs->addCriticalPathNode(r->child()); calcCriticalPathList(cs, r->child()); newPath = true; } } } const QList< QList > *Project::criticalPathList(long id) { Schedule *s = schedule(id); if (s == 0) { //debugPlan<<"No schedule with id="<(s); if (! ms->criticalPathListCached) { initiateCalculationLists(*ms); calcCriticalPathList(ms); } return ms->criticalPathList(); } QList Project::criticalPath(long id, int index) { Schedule *s = schedule(id); if (s == 0) { //debugPlan<<"No schedule with id="<(); } MainSchedule *ms = static_cast(s); if (! ms->criticalPathListCached) { initiateCalculationLists(*ms); calcCriticalPathList(ms); } return ms->criticalPath(index); } DateTime Project::startTime(long id) const { Schedule *s = schedule(id); return s ? s->startTime : m_constraintStartTime; } DateTime Project::endTime(long id) const { Schedule *s = schedule(id); return s ? s->endTime : m_constraintEndTime; } Duration Project::duration(long id) const { Schedule *s = schedule(id); return s ? s->duration : Duration::zeroDuration; } Duration *Project::getRandomDuration() { return 0L; } DateTime Project::checkStartConstraints(const DateTime &dt) const { DateTime t = dt; foreach (Node *n, nodeIdDict) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { switch (n->constraint()) { case Node::FixedInterval: case Node::StartNotEarlier: case Node::MustStartOn: t = qMin(t, qMax(n->constraintStartTime(), m_constraintStartTime)); break; default: break; } } } return t; } DateTime Project::checkEndConstraints(const DateTime &dt) const { DateTime t = dt; foreach (Node *n, nodeIdDict) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { switch (n->constraint()) { case Node::FixedInterval: case Node::FinishNotLater: case Node::MustFinishOn: t = qMax(t, qMin(n->constraintEndTime(), m_constraintEndTime)); break; default: break; } } } return t; } #ifndef PLAN_NLOGDEBUG bool Project::checkParent(Node *n, const QList &list, QList &checked) { if (n->isStartNode()) { debugPlan< lst = list; lst << n; foreach (Relation *r, n->dependParentNodes()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if (! checkParent(r->parent(), lst, checked)) { return false; } } Task *t = static_cast(n); foreach (Relation *r, t->parentProxyRelations()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; debugPlan<<"Proxy:"<parent()<<":"<parent(), lst, checked)) { return false; } } return true; } bool Project::checkChildren(Node *n, const QList &list, QList &checked) { if (n->isEndNode()) { debugPlan< lst = list; lst << n; foreach (Relation *r, n->dependChildNodes()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if (! checkChildren(r->child(), lst, checked)) { return false; } } Task *t = static_cast(n); foreach (Relation *r, t->childProxyRelations()) { if (checked.contains(r)) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } debugPlan<<"Proxy:"<parent()<<":"<child(), lst, checked)) { return false; } } return true; } #endif void Project::tasksForward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); // Do these in reverse order to get tasks with same prio in wbs order const QList tasks = allTasks(); for (int i = tasks.count() -1; i >= 0; --i) { Task *t = tasks.at(i); switch (t->constraint()) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.prepend(t); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.prepend(t); break; default: if (t->isEndNode()) { m_terminalNodes.insert(-t->priority(), t); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"End nodes:"< lst; QList rel; Q_ASSERT(checkParent(n, lst, rel)); Q_UNUSED(n); } #endif } void Project::tasksBackward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); // Do these in reverse order to get tasks with same prio in wbs order const QList tasks = allTasks(); for (int i = tasks.count() -1; i >= 0; --i) { Task *t = tasks.at(i); switch (t->constraint()) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.prepend(t); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.prepend(t); break; default: if (t->isStartNode()) { m_terminalNodes.insert(-t->priority(), t); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"Start nodes:"< lst; QList rel; Q_ASSERT(checkChildren(n, lst, rel)); Q_UNUSED(n); } #endif } DateTime Project::calculateForward(int use) { //debugPlan<(m_currentSchedule); if (cs == 0) { return finish; } if (type() == Node::Type_Project) { QTime timer; timer.start(); cs->logInfo(i18n("Start calculating forward")); m_visitedForward = true; if (! m_visitedBackward) { // setup tasks tasksForward(); // Do all hard constrained first foreach (Node *n, m_hardConstraints) { cs->logDebug("Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateEarlyFinish(use); // do not do predeccessors if (time > finish) { finish = time; } } // do the predeccessors foreach (Node *n, m_hardConstraints) { cs->logDebug("Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } // now try to schedule soft constrained *with* predeccessors foreach (Node *n, m_softConstraints) { cs->logDebug("Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } // and then the rest using the end nodes to calculate everything (remaining) foreach (Task *n, m_terminalNodes) { cs->logDebug("Calculate using end task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } } else { // tasks have been calculated backwards in this order foreach (Node *n, cs->backwardNodes()) { DateTime time = n->calculateForward(use); if (time > finish) { finish = time; } } } cs->logInfo(i18n("Finished calculating forward: %1 ms", timer.elapsed())); } else { //TODO: subproject } return finish; } DateTime Project::calculateBackward(int use) { //debugPlan<(m_currentSchedule); if (cs == 0) { return start; } if (type() == Node::Type_Project) { QTime timer; timer.start(); cs->logInfo(i18n("Start calculating backward")); m_visitedBackward = true; if (! m_visitedForward) { // setup tasks tasksBackward(); // Do all hard constrained first foreach (Task *n, m_hardConstraints) { cs->logDebug("Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateLateStart(use); // do not do predeccessors if (! start.isValid() || time < start) { start = time; } } // then do the predeccessors foreach (Task *n, m_hardConstraints) { cs->logDebug("Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } // now try to schedule soft constrained *with* predeccessors foreach (Task *n, m_softConstraints) { cs->logDebug("Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } // and then the rest using the start nodes to calculate everything (remaining) foreach (Task *n, m_terminalNodes) { cs->logDebug("Calculate using start task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } } else { // tasks have been calculated forwards in this order foreach (Node *n, cs->forwardNodes()) { DateTime time = n->calculateBackward(use); if (! start.isValid() || time < start) { start = time; } } } cs->logInfo(i18n("Finished calculating backward: %1 ms", timer.elapsed())); } else { //TODO: subproject } return start; } DateTime Project::scheduleForward(const DateTime &earliest, int use) { DateTime end; MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0 || stopcalculation) { return DateTime(); } QTime timer; timer.start(); cs->logInfo(i18n("Start scheduling forward")); resetVisited(); // Schedule in the same order as calculated forward // Do all hard constrained first foreach (Node *n, m_hardConstraints) { cs->logDebug("Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleFromStartTime(use); // do not do predeccessors if (time > end) { end = time; } } foreach (Node *n, cs->forwardNodes()) { cs->logDebug("Schedule task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleForward(earliest, use); if (time > end) { end = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo(i18n("Finished scheduling forward: %1 ms", timer.elapsed())); foreach (Node *n, allNodes()) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { Q_ASSERT(n->isScheduled()); } } return end; } DateTime Project::scheduleBackward(const DateTime &latest, int use) { DateTime start; MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0 || stopcalculation) { return start; } QTime timer; timer.start(); cs->logInfo(i18n("Start scheduling backward")); resetVisited(); // Schedule in the same order as calculated backward // Do all hard constrained first foreach (Node *n, m_hardConstraints) { cs->logDebug("Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleFromEndTime(use); // do not do predeccessors if (! start.isValid() || time < start) { start = time; } } foreach (Node *n, cs->backwardNodes()) { cs->logDebug("Schedule task:" + n->name() + " : " + n->constraintToString()); DateTime time = n->scheduleBackward(latest, use); if (! start.isValid() || time < start) { start = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo(i18n("Finished scheduling backward: %1 ms", timer.elapsed())); foreach (Node *n, allNodes()) { if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) { Q_ASSERT(n->isScheduled()); } } return start; } void Project::adjustSummarytask() { MainSchedule *cs = static_cast(m_currentSchedule); if (cs == 0 || stopcalculation) { return; } QListIterator it(cs->summaryTasks()); while (it.hasNext()) { it.next() ->adjustSummarytask(); } } void Project::initiateCalculation(MainSchedule &sch) { //debugPlan< git(m_resourceGroups); while (git.hasNext()) { git.next() ->initiateCalculation(sch); } Node::initiateCalculation(sch); } void Project::initiateCalculationLists(MainSchedule &sch) { //debugPlan< it = childNodeIterator(); while (it.hasNext()) { it.next() ->initiateCalculationLists(sch); } } else { //TODO: subproject } } bool Project::load(KoXmlElement &element, XMLLoaderObject &status) { //debugPlan<<"--->"; m_useSharedResources = false; // default should off in case old project // load locale first KoXmlNode n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "locale") { Locale *l = locale(); l->setCurrencySymbol(e.attribute("currency-symbol", "")); if (e.hasAttribute("currency-digits")) { l->setMonetaryDecimalPlaces(e.attribute("currency-digits").toInt()); } QLocale::Language language = QLocale::AnyLanguage; QLocale::Country country = QLocale::AnyCountry; if (e.hasAttribute("language")) { language = static_cast(e.attribute("language").toInt()); } if (e.hasAttribute("country")) { country = static_cast(e.attribute("country").toInt()); } l->setCurrencyLocale(language, country); } else if (e.tagName() == "shared-resources") { m_useSharedResources = e.attribute("use", "0").toInt(); m_sharedResourcesFile = e.attribute("file"); m_sharedProjectsUrl = QUrl(e.attribute("projects-url")); m_loadProjectsAtStartup = (bool)e.attribute("projects-loadatstartup", "0").toInt(); } else if (e.tagName() == QLatin1String("documents")) { m_documents.load(e, status); } else if (e.tagName() == QLatin1String("workpackageinfo")) { if (e.hasAttribute("check-for-workpackages")) { m_workPackageInfo.checkForWorkPackages = e.attribute("check-for-workpackages").toInt(); } if (e.hasAttribute("retrieve-url")) { m_workPackageInfo.retrieveUrl = QUrl(e.attribute("retrieve-url")); } if (e.hasAttribute("delete-after-retrieval")) { m_workPackageInfo.deleteAfterRetrieval = e.attribute("delete-after-retrieval").toInt(); } if (e.hasAttribute("archive-after-retrieval")) { m_workPackageInfo.archiveAfterRetrieval = e.attribute("archive-after-retrieval").toInt(); } if (e.hasAttribute("archive-url")) { m_workPackageInfo.archiveUrl = QUrl(e.attribute("archive-url")); } if (e.hasAttribute("publish-url")) { m_workPackageInfo.publishUrl = QUrl(e.attribute("publish-url")); } } else if (e.tagName() == QLatin1String("task-modules")) { m_useLocalTaskModules = false; QList urls; for (KoXmlNode child = e.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement path = child.toElement(); if (path.isNull()) { continue; } QString s = path.attribute("url"); if (!s.isEmpty()) { QUrl url = QUrl::fromUserInput(s); if (!urls.contains(url)) { urls << url; } } } m_taskModules = urls; // If set adds local path to taskModules() setUseLocalTaskModules((bool)e.attribute("use-local-task-modules").toInt()); } } QList cals; QString s; bool ok = false; setName(element.attribute("name")); removeId(m_id); m_id = element.attribute("id"); registerNodeId(this); m_priority = element.attribute(QStringLiteral("priority"), "0").toInt(); m_leader = element.attribute("leader"); m_description = element.attribute("description"); QTimeZone tz(element.attribute("timezone").toLatin1()); if (tz.isValid()) { m_timeZone = tz; } else warnPlan<<"No timezone specified, using default (local)"; status.setProjectTimeZone(m_timeZone); // Allow for both numeric and text s = element.attribute("scheduling", "0"); m_constraint = (Node::ConstraintType) s.toInt(&ok); if (!ok) setConstraint(s); if (m_constraint != Node::MustStartOn && m_constraint != Node::MustFinishOn) { errorPlan << "Illegal constraint: " << constraintToString(); setConstraint(Node::MustStartOn); } s = element.attribute("start-time"); if (!s.isEmpty()) m_constraintStartTime = DateTime::fromString(s, m_timeZone); s = element.attribute("end-time"); if (!s.isEmpty()) m_constraintEndTime = DateTime::fromString(s, m_timeZone); status.setProgress(10); // Load the project children // Do calendars first, they only reference other calendars //debugPlan<<"Calendars--->"; n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "calendar") { // Load the calendar. // Referenced by resources Calendar * child = new Calendar(); child->setProject(this); if (child->load(e, status)) { cals.append(child); // temporary, reorder later } else { // TODO: Complain about this errorPlan << "Failed to load calendar"; delete child; } } else if (e.tagName() == "standard-worktime") { // Load standard worktime StandardWorktime * child = new StandardWorktime(); if (child->load(e, status)) { setStandardWorktime(child); } else { errorPlan << "Failed to load standard worktime"; delete child; } } } // calendars references calendars in arbitrary saved order bool added = false; do { added = false; QList lst; while (!cals.isEmpty()) { Calendar *c = cals.takeFirst(); c->m_blockversion = true; if (c->parentId().isEmpty()) { addCalendar(c, status.baseCalendar()); // handle pre 0.6 version added = true; //debugPlan<<"added to project:"<name(); } else { Calendar *par = calendar(c->parentId()); if (par) { par->m_blockversion = true; addCalendar(c, par); added = true; //debugPlan<<"added:"<name()<<" to parent:"<name(); par->m_blockversion = false; } else { lst.append(c); // treat later //debugPlan<<"treat later:"<name(); } } c->m_blockversion = false; } cals = lst; } while (added); if (! cals.isEmpty()) { errorPlan<<"All calendars not saved!"; } //debugPlan<<"Calendars<---"; status.setProgress(15); // Resource groups and resources, can reference calendars n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "resource-group") { // Load the resources // References calendars ResourceGroup * child = new ResourceGroup(); if (child->load(e, status)) { addResourceGroup(child); } else { // TODO: Complain about this delete child; } } } status.setProgress(20); // The main stuff n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { if (! n.isElement()) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() == "project") { //debugPlan<<"Sub project--->"; /* // Load the subproject Project * child = new Project(this); if (child->load(e)) { if (!addTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if (e.tagName() == "task") { //debugPlan<<"Task--->"; // Load the task (and resourcerequests). // Depends on resources already loaded Task * child = new Task(this); if (child->load(e, status)) { if (!addTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } status.setProgress(70); // These go last n = element.firstChild(); for (; ! n.isNull(); n = n.nextSibling()) { //debugPlan<"; // Load accounts // References tasks if (!m_accounts.load(e, *this)) { errorPlan << "Failed to load accounts"; } } else if (e.tagName() == "relation") { //debugPlan<<"Relation--->"; // Load the relation // References tasks Relation * child = new Relation(); if (!child->load(e, *this)) { // TODO: Complain about this errorPlan << "Failed to load relation"; delete child; } //debugPlan<<"Relation<---"; } else if (e.tagName() == "schedules") { //debugPlan<<"Project schedules & task appointments--->"; // References tasks and resources KoXmlNode sn = e.firstChild(); for (; ! sn.isNull(); sn = sn.nextSibling()) { if (! sn.isElement()) { continue; } KoXmlElement el = sn.toElement(); //debugPlan<loadXML(el, status)) { if (add) addScheduleManager(sm); } else { errorPlan << "Failed to load schedule manager"; delete sm; } } else { debugPlan<<"No schedule manager ?!"; } } //debugPlan<<"Node schedules<---"; } else if (e.tagName() == "resource-teams") { //debugPlan<<"Resource teams--->"; // References other resources KoXmlNode tn = e.firstChild(); for (; ! tn.isNull(); tn = tn.nextSibling()) { if (! tn.isElement()) { continue; } KoXmlElement el = tn.toElement(); if (el.tagName() == "team") { Resource *r = findResource(el.attribute("team-id")); Resource *tm = findResource(el.attribute("member-id")); if (r == 0 || tm == 0) { errorPlan<<"resource-teams: cannot find resources"; continue; } if (r == tm) { errorPlan<<"resource-teams: a team cannot be a member of itself"; continue; } r->addTeamMemberId(tm->id()); } else { errorPlan<<"resource-teams: unhandled tag"<currencySymbol()); loc.setAttribute("currency-digits", l->monetaryDecimalPlaces()); loc.setAttribute("language", l->currencyLanguage()); loc.setAttribute("country", l->currencyCountry()); QDomElement share = me.ownerDocument().createElement("shared-resources"); me.appendChild(share); share.setAttribute("use", m_useSharedResources); share.setAttribute("file", m_sharedResourcesFile); share.setAttribute("projects-url", QString(m_sharedProjectsUrl.toEncoded())); share.setAttribute("projects-loadatstartup", m_loadProjectsAtStartup); QDomElement wpi = me.ownerDocument().createElement("workpackageinfo"); me.appendChild(wpi); wpi.setAttribute("check-for-workpackages", m_workPackageInfo.checkForWorkPackages); wpi.setAttribute("retrieve-url", m_workPackageInfo.retrieveUrl.toString(QUrl::None)); wpi.setAttribute("delete-after-retrieval", m_workPackageInfo.deleteAfterRetrieval); wpi.setAttribute("archive-after-retrieval", m_workPackageInfo.archiveAfterRetrieval); wpi.setAttribute("archive-url", m_workPackageInfo.archiveUrl.toString(QUrl::None)); wpi.setAttribute("publish-url", m_workPackageInfo.publishUrl.toString(QUrl::None)); QDomElement tm = me.ownerDocument().createElement("task-modules"); me.appendChild(tm); tm.setAttribute("use-local-task-modules", m_useLocalTaskModules); for (const QUrl &url : taskModules(false/*no local*/)) { QDomElement e = tm.ownerDocument().createElement("task-module"); tm.appendChild(e); e.setAttribute("url", url.toString()); } m_documents.save(me); if (context.saveAll(this)) { m_accounts.save(me); // save calendars foreach (Calendar *c, calendarIdDict) { c->save(me); } // save standard worktime if (m_standardWorktime) m_standardWorktime->save(me); // save project resources, must be after calendars QListIterator git(m_resourceGroups); while (git.hasNext()) { git.next() ->save(me); } } // Only save parent relations QListIterator it(m_dependParentNodes); while (it.hasNext()) { Relation *r = it.next(); if (context.saveNode(r->parent()) && context.saveNode(r->child())) { r->save(me, context); } } if (context.saveAll(this)) { for (int i = 0; i < numChildren(); i++) // Save all children childNode(i)->save(me, context); } // Now we can save relations assuming no tasks have relations outside the project QListIterator nodes(m_nodes); while (nodes.hasNext()) { nodes.next()->saveRelations(me, context); } if (context.saveAll(this)) { if (!m_managers.isEmpty()) { QDomElement el = me.ownerDocument().createElement("schedules"); me.appendChild(el); foreach (ScheduleManager *sm, m_managers) { sm->saveXML(el); } } // save resource teams QDomElement el = me.ownerDocument().createElement("resource-teams"); me.appendChild(el); foreach (Resource *r, resourceIdDict) { if (r->type() != Resource::Type_Team) { continue; } foreach (const QString &id, r->teamMemberIds()) { QDomElement e = el.ownerDocument().createElement("team"); el.appendChild(e); e.setAttribute("team-id", r->id()); e.setAttribute("member-id", id); } } } } void Project::saveWorkPackageXML(QDomElement &element, const Node *node, long id) const { QDomElement me = element.ownerDocument().createElement("project"); element.appendChild(me); me.setAttribute("name", m_name); me.setAttribute("leader", m_leader); me.setAttribute("id", m_id); me.setAttribute("description", m_description); me.setAttribute("timezone", m_timeZone.isValid() ? QString::fromLatin1(m_timeZone.id()) : QString()); me.setAttribute("scheduling", constraintToString()); me.setAttribute("start-time", m_constraintStartTime.toString(Qt::ISODate)); me.setAttribute("end-time", m_constraintEndTime.toString(Qt::ISODate)); QListIterator git(m_resourceGroups); while (git.hasNext()) { git.next() ->saveWorkPackageXML(me, node->assignedResources(id)); } if (node == 0) { return; } node->saveWorkPackageXML(me, id); foreach (ScheduleManager *sm, m_managerIdMap) { if (sm->scheduleId() == id) { QDomElement el = me.ownerDocument().createElement("schedules"); me.appendChild(el); sm->saveWorkPackageXML(el, *node); break; } } } void Project::setParentSchedule(Schedule *sch) { QListIterator it = m_nodes; while (it.hasNext()) { it.next() ->setParentSchedule(sch); } } void Project::addResourceGroup(ResourceGroup *group, int index) { int i = index == -1 ? m_resourceGroups.count() : index; emit resourceGroupToBeAdded(group, i); m_resourceGroups.insert(i, group); setResourceGroupId(group); group->setProject(this); foreach (Resource *r, group->resources()) { setResourceId(r); r->setProject(this); } emit resourceGroupAdded(group); emit projectChanged(); } ResourceGroup *Project::takeResourceGroup(ResourceGroup *group) { int i = m_resourceGroups.indexOf(group); Q_ASSERT(i != -1); if (i == -1) { return 0; } emit resourceGroupToBeRemoved(group); ResourceGroup *g = m_resourceGroups.takeAt(i); Q_ASSERT(group == g); g->setProject(0); removeResourceGroupId(g->id()); foreach (Resource *r, g->resources()) { r->setProject(0); removeResourceId(r->id()); } emit resourceGroupRemoved(g); emit projectChanged(); return g; } QList &Project::resourceGroups() { return m_resourceGroups; } void Project::addResource(ResourceGroup *group, Resource *resource, int index) { int i = index == -1 ? group->numResources() : index; emit resourceToBeAdded(group, i); group->addResource(i, resource, 0); setResourceId(resource); emit resourceAdded(resource); emit projectChanged(); } Resource *Project::takeResource(ResourceGroup *group, Resource *resource) { emit resourceToBeRemoved(resource); bool result = removeResourceId(resource->id()); Q_ASSERT(result == true); if (!result) { warnPlan << "Could not remove resource with id" << resource->id(); } resource->removeRequests(); // not valid anymore Resource *r = group->takeResource(resource); Q_ASSERT(resource == r); if (resource != r) { warnPlan << "Could not take resource from group"; } emit resourceRemoved(resource); emit projectChanged(); return r; } void Project::moveResource(ResourceGroup *group, Resource *resource) { if (group == resource->parentGroup()) { return; } takeResource(resource->parentGroup(), resource); addResource(group, resource); return; } QMap< QString, QString > Project::externalProjects() const { QMap< QString, QString > map; foreach (Resource *r, resourceList()) { for(QMapIterator it(r->externalProjects()); it.hasNext();) { it.next(); if (! map.contains(it.key())) { map[ it.key() ] = it.value(); } } } return map; } bool Project::addTask(Node* task, Node* position) { // we want to add a task at the given position. => the new node will // become next sibling right after position. if (0 == position) { return addSubTask(task, this); } //debugPlan<<"Add"<name()<<" after"<name(); // in case we want to add to the main project, we make it child element // of the root element. if (Node::Type_Project == position->type()) { return addSubTask(task, position); } // find the position // we have to tell the parent that we want to delete one of its children Node* parentNode = position->parentNode(); if (!parentNode) { debugPlan <<"parent node not found???"; return false; } int index = parentNode->findChildNode(position); if (-1 == index) { // ok, it does not exist debugPlan <<"Task not found???"; return false; } return addSubTask(task, index + 1, parentNode); } bool Project::addSubTask(Node* task, Node* parent) { // append task to parent return addSubTask(task, -1, parent); } bool Project::addSubTask(Node* task, int index, Node* parent, bool emitSignal) { // we want to add a subtask to the node "parent" at the given index. // If parent is 0, add to this Node *p = parent; if (0 == p) { p = this; } if (!registerNodeId(task)) { errorPlan << "Failed to register node id, can not add subtask: " << task->name(); return false; } int i = index == -1 ? p->numChildren() : index; if (emitSignal) emit nodeToBeAdded(p, i); p->insertChildNode(i, task); connect(this, &Project::standardWorktimeChanged, task, &Node::slotStandardWorktimeChanged); if (emitSignal) { emit nodeAdded(task); emit projectChanged(); if (p != this && p->numChildren() == 1) { emit nodeChanged(p, TypeProperty); } } return true; } void Project::takeTask(Node *node, bool emitSignal) { //debugPlan<name(); Node * parent = node->parentNode(); if (parent == 0) { debugPlan <<"Node must have a parent!"; return; } removeId(node->id()); if (emitSignal) emit nodeToBeRemoved(node); disconnect(this, &Project::standardWorktimeChanged, node, &Node::slotStandardWorktimeChanged); parent->takeChildNode(node); if (emitSignal) { emit nodeRemoved(node); emit projectChanged(); if (parent != this && parent->type() != Node::Type_Summarytask) { emit nodeChanged(parent, TypeProperty); } } } bool Project::canMoveTask(Node* node, Node *newParent, bool checkBaselined) { //debugPlan<name()<<" to"<name(); if (node == this) { return false; } if (checkBaselined) { if (node->isBaselined() || (newParent->type() != Node::Type_Summarytask && newParent->isBaselined())) { return false; } } Node *p = newParent; while (p && p != this) { if (! node->canMoveTo(p)) { return false; } p = p->parentNode(); } return true; } bool Project::moveTask(Node* node, Node *newParent, int newPos) { //debugPlan<name()<<" to"<name()<<","<parentNode(); int oldPos = oldParent->indexOf(node); int i = newPos < 0 ? newParent->numChildren() : newPos; if (oldParent == newParent && i == oldPos) { // no need to move to where it already is return false; } int newRow = i; if (oldParent == newParent && newPos > oldPos) { ++newRow; // itemmodels wants new row *before* node is removed from old position } debugPlan<name()<<"at"<indexOf(node)<<"to"<name()<numChildren() == 0) { emit nodeChanged(oldParent, TypeProperty); } if (newParent != this && newParent->numChildren() == 1) { emit nodeChanged(newParent, TypeProperty); } return true; } bool Project::canIndentTask(Node* node) { if (0 == node) { // should always be != 0. At least we would get the Project, // but you never know who might change that, so better be careful return false; } if (node->type() == Node::Type_Project) { //debugPlan<<"The root node cannot be indented"; return false; } // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if (!parentNode) { return false; } if (parentNode->findChildNode(node) == -1) { errorPlan << "Tasknot found???"; return false; } Node *sib = node->siblingBefore(); if (!sib) { //debugPlan<<"new parent node not found"; return false; } if (node->findParentRelation(sib) || node->findChildRelation(sib)) { //debugPlan<<"Cannot have relations to parent"; return false; } return true; } bool Project::indentTask(Node* node, int index) { if (canIndentTask(node)) { Node * newParent = node->siblingBefore(); int i = index == -1 ? newParent->numChildren() : index; moveTask(node, newParent, i); //debugPlan; return true; } return false; } bool Project::canUnindentTask(Node* node) { if (0 == node) { // is always != 0. At least we would get the Project, but you // never know who might change that, so better be careful return false; } if (Node::Type_Project == node->type()) { //debugPlan<<"The root node cannot be unindented"; return false; } // we have to find the parent of task to manipulate its list of children // and we need the parent's parent too Node* parentNode = node->parentNode(); if (!parentNode) { return false; } Node* grandParentNode = parentNode->parentNode(); if (!grandParentNode) { //debugPlan<<"This node already is at the top level"; return false; } int index = parentNode->findChildNode(node); if (-1 == index) { errorPlan << "Tasknot found???"; return false; } return true; } bool Project::unindentTask(Node* node) { if (canUnindentTask(node)) { Node * parentNode = node->parentNode(); Node *grandParentNode = parentNode->parentNode(); int i = grandParentNode->indexOf(parentNode) + 1; if (i == 0) { i = grandParentNode->numChildren(); } moveTask(node, grandParentNode, i); //debugPlan; return true; } return false; } bool Project::canMoveTaskUp(Node* node) { if (node == 0) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if (!parentNode) { //debugPlan<<"No parent found"; return false; } if (parentNode->findChildNode(node) == -1) { errorPlan << "Tasknot found???"; return false; } if (node->siblingBefore()) { return true; } return false; } bool Project::moveTaskUp(Node* node) { if (canMoveTaskUp(node)) { moveTask(node, node->parentNode(), node->parentNode()->indexOf(node) - 1); return true; } return false; } bool Project::canMoveTaskDown(Node* node) { if (node == 0) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if (!parentNode) { return false; } if (parentNode->findChildNode(node) == -1) { errorPlan << "Tasknot found???"; return false; } if (node->siblingAfter()) { return true; } return false; } bool Project::moveTaskDown(Node* node) { if (canMoveTaskDown(node)) { moveTask(node, node->parentNode(), node->parentNode()->indexOf(node) + 1); return true; } return false; } Task *Project::createTask() { Task * node = new Task(); node->setId(uniqueNodeId()); reserveId(node->id(), node); return node; } Task *Project::createTask(const Task &def) { Task * node = new Task(def); node->setId(uniqueNodeId()); reserveId(node->id(), node); return node; } Node *Project::findNode(const QString &id) const { if (m_parent == 0) { if (nodeIdDict.contains(id)) { return nodeIdDict[ id ]; } return 0; } return m_parent->findNode(id); } bool Project::nodeIdentExists(const QString &id) const { return nodeIdDict.contains(id) || nodeIdReserved.contains(id); } QString Project::uniqueNodeId(int seed) const { Q_UNUSED(seed); QString ident = generateId(); while (nodeIdentExists(ident)) { ident = generateId(); } return ident; } QString Project::uniqueNodeId(const QList &existingIds, int seed) { QString id = uniqueNodeId(seed); while (existingIds.contains(id)) { id = uniqueNodeId(seed); } return id; } bool Project::removeId(const QString &id) { //debugPlan <<"id=" << id; if (m_parent) { return m_parent->removeId(id); } //debugPlan << "id=" << id<< nodeIdDict.contains(id); return nodeIdDict.remove(id); } void Project::reserveId(const QString &id, Node *node) { //debugPlan <<"id=" << id << node->name(); nodeIdReserved.insert(id, node); } bool Project::registerNodeId(Node *node) { nodeIdReserved.remove(node->id()); if (node->id().isEmpty()) { warnPlan << "Node id is empty, cannot register it"; return false; } Node *rn = findNode(node->id()); if (rn == 0) { //debugPlan <<"id=" << node->id() << node->name(); nodeIdDict.insert(node->id(), node); return true; } if (rn != node) { errorPlan << "Id already exists for different task: " << node->id(); return false; } //debugPlan<<"Already exists" <<"id=" << node->id() << node->name(); return true; } QList Project::allNodes(bool ordered, Node* parent) const { QList lst; if (ordered) { const Node *p = parent ? parent : this; foreach (Node *n, p->childNodeIterator()) { if (n->type() == Node::Type_Task || n->type() == Type_Milestone || n->type() == Node::Type_Summarytask) { lst << static_cast(n); lst += allNodes(ordered, n); } } } else { lst = nodeIdDict.values(); int me = lst.indexOf(const_cast(this)); if (me != -1) { lst.removeAt(me); } } return lst; } QList Project::allTasks(const Node *parent) const { QList lst; const Node *p = parent ? parent : this; foreach (Node *n, p->childNodeIterator()) { if (n->type() == Node::Type_Task || n->type() == Type_Milestone) { lst << static_cast(n); } lst += allTasks(n); } return lst; } bool Project::isStarted() const { const QList tasks = allTasks(); for (const Task *t : tasks) { if (t->isStarted()) { return true; } } return false; } bool Project::setResourceGroupId(ResourceGroup *group) { if (group == 0) { return false; } if (! group->id().isEmpty()) { ResourceGroup *g = findResourceGroup(group->id()); if (group == g) { return true; } else if (g == 0) { insertResourceGroupId(group->id(), group); return true; } } QString id = uniqueResourceGroupId(); group->setId(id); if (id.isEmpty()) { return false; } insertResourceGroupId(id, group); return true; } QString Project::uniqueResourceGroupId() const { QString id = generateId(); while (resourceGroupIdDict.contains(id)) { id = generateId(); } return id; } ResourceGroup *Project::group(const QString& id) { return findResourceGroup(id); } ResourceGroup *Project::groupByName(const QString& name) const { foreach (ResourceGroup *g, resourceGroupIdDict) { if (g->name() == name) { return g; } } return 0; } QList Project::autoAllocateResources() const { QList lst; foreach (Resource *r, resourceIdDict) { if (r->autoAllocate()) { lst << r; } } return lst; } void Project::insertResourceId(const QString &id, Resource *resource) { resourceIdDict.insert(id, resource); } bool Project::removeResourceId(const QString &id) { return resourceIdDict.remove(id); } bool Project::setResourceId(Resource *resource) { if (resource == 0) { return false; } if (! resource->id().isEmpty()) { Resource *r = findResource(resource->id()); if (resource == r) { return true; } else if (r == 0) { insertResourceId(resource->id(), resource); return true; } } QString id = uniqueResourceId(); resource->setId(id); if (id.isEmpty()) { return false; } insertResourceId(id, resource); return true; } QString Project::uniqueResourceId() const { QString id = generateId(); while (resourceIdDict.contains(id)) { id = generateId(); } return id; } Resource *Project::resource(const QString& id) { return findResource(id); } Resource *Project::resourceByName(const QString& name) const { QHash::const_iterator it; for (it = resourceIdDict.constBegin(); it != resourceIdDict.constEnd(); ++it) { Resource *r = it.value(); if (r->name() == name) { Q_ASSERT(it.key() == r->id()); return r; } } return 0; } QStringList Project::resourceNameList() const { QStringList lst; foreach (Resource *r, resourceIdDict) { lst << r->name(); } return lst; } EffortCostMap Project::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->plannedEffortCostPrDay(start, end, id, typ); } return ec; } EffortCostMap Project::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->plannedEffortCostPrDay(resource, start, end, id, typ); } return ec; } EffortCostMap Project::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->actualEffortCostPrDay(start, end, id, typ); } return ec; } EffortCostMap Project::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const { //debugPlan< it(childNodeIterator()); while (it.hasNext()) { ec += it.next() ->actualEffortCostPrDay(resource, start, end, id, typ); } return ec; } // Returns the total planned effort for this project (or subproject) Duration Project::plannedEffort(long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->plannedEffort(id, typ); } return eff; } // Returns the total planned effort for this project (or subproject) on date Duration Project::plannedEffort(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->plannedEffort(date, id, typ); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date Duration Project::plannedEffortTo(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->plannedEffortTo(date, id, typ); } return eff; } // Returns the total actual effort for this project (or subproject) upto and including date Duration Project::actualEffortTo(QDate date) const { //debugPlan; Duration eff; QListIterator it(childNodeIterator()); while (it.hasNext()) { eff += it.next() ->actualEffortTo(date); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date double Project::plannedCostTo(QDate date, long id, EffortCostCalculationType typ) const { //debugPlan; double c = 0; QListIterator it(childNodeIterator()); while (it.hasNext()) { c += it.next() ->plannedCostTo(date, id, typ); } return c; } // Returns the total actual cost for this project (or subproject) upto and including date EffortCost Project::actualCostTo(long int id, QDate date) const { //debugPlan; EffortCost c; QListIterator it(childNodeIterator()); while (it.hasNext()) { c += it.next() ->actualCostTo(id, date); } return c; } Duration Project::budgetedWorkPerformed(QDate date, long id) const { //debugPlan; Duration e; foreach (Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed(date, id); } return e; } double Project::budgetedCostPerformed(QDate date, long id) const { //debugPlan; double c = 0.0; foreach (Node *n, childNodeIterator()) { c += n->budgetedCostPerformed(date, id); } return c; } double Project::effortPerformanceIndex(QDate date, long id) const { //debugPlan; debugPlan< 0.0) { r = p / s; } debugPlan< date ? end : date), id); double budgetAtCompletion; double plannedCompleted; double budgetedCompleted; bool useEffort = false; //FIXME if (useEffort) { budgetAtCompletion = plan.totalEffort().toDouble(Duration::Unit_h); plannedCompleted = plan.effortTo(date).toDouble(Duration::Unit_h); //actualCompleted = actual.effortTo(date).toDouble(Duration::Unit_h); budgetedCompleted = budgetedWorkPerformed(date, id).toDouble(Duration::Unit_h); } else { budgetAtCompletion = plan.totalCost(); plannedCompleted = plan.costTo(date); budgetedCompleted = budgetedCostPerformed(date, id); } double c = 0.0; if (budgetAtCompletion > 0.0) { double percentageCompletion = budgetedCompleted / budgetAtCompletion; c = budgetAtCompletion * percentageCompletion; //?? debugPlan<name()<<","<<(parent?parent->name():"No parent"); int row = parent == 0 ? m_calendars.count() : parent->calendars().count(); if (index >= 0 && index < row) { row = index; } emit calendarToBeAdded(parent, row); calendar->setProject(this); if (parent == 0) { calendar->setParentCal(0); // in case m_calendars.insert(row, calendar); } else { calendar->setParentCal(parent, row); } if (calendar->isDefault()) { setDefaultCalendar(calendar); } setCalendarId(calendar); emit calendarAdded(calendar); emit projectChanged(); } void Project::takeCalendar(Calendar *calendar) { emit calendarToBeRemoved(calendar); removeCalendarId(calendar->id()); if (calendar == m_defaultCalendar) { m_defaultCalendar = 0; } if (calendar->parentCal() == 0) { int i = indexOf(calendar); if (i != -1) { m_calendars.removeAt(i); } } else { calendar->setParentCal(0); } emit calendarRemoved(calendar); calendar->setProject(0); emit projectChanged(); } int Project::indexOf(const Calendar *calendar) const { return m_calendars.indexOf(const_cast(calendar)); } Calendar *Project::calendar(const QString& id) const { return findCalendar(id); } Calendar *Project::calendarByName(const QString& name) const { foreach(Calendar *c, calendarIdDict) { if (c->name() == name) { return c; } } return 0; } const QList &Project::calendars() const { return m_calendars; } QList Project::allCalendars() const { return calendarIdDict.values(); } QStringList Project::calendarNames() const { QStringList lst; foreach(Calendar *c, calendarIdDict) { lst << c->name(); } return lst; } bool Project::setCalendarId(Calendar *calendar) { if (calendar == 0) { return false; } if (! calendar->id().isEmpty()) { Calendar *c = findCalendar(calendar->id()); if (calendar == c) { return true; } else if (c == 0) { insertCalendarId(calendar->id(), calendar); return true; } } QString id = uniqueCalendarId(); calendar->setId(id); if (id.isEmpty()) { return false; } insertCalendarId(id, calendar); return true; } QString Project::uniqueCalendarId() const { QString id = generateId(); while (calendarIdDict.contains(id)) { id = generateId(); } return id; } void Project::setDefaultCalendar(Calendar *cal) { if (m_defaultCalendar) { m_defaultCalendar->setDefault(false); } m_defaultCalendar = cal; if (cal) { cal->setDefault(true); } emit defaultCalendarChanged(cal); emit projectChanged(); } void Project::setStandardWorktime(StandardWorktime * worktime) { if (m_standardWorktime != worktime) { delete m_standardWorktime; m_standardWorktime = worktime; m_standardWorktime->setProject(this); emit standardWorktimeChanged(worktime); } } void Project::emitDocumentAdded(Node *node , Document *doc , int index) { emit documentAdded(node, doc, index); } void Project::emitDocumentRemoved(Node *node , Document *doc , int index) { emit documentRemoved(node, doc, index); } void Project::emitDocumentChanged(Node *node , Document *doc , int index) { emit documentChanged(node, doc, index); } bool Project::linkExists(const Node *par, const Node *child) const { if (par == 0 || child == 0 || par == child || par->isDependChildOf(child)) { return false; } foreach (Relation *r, par->dependChildNodes()) { if (r->child() == child) { return true; } } return false; } bool Project::legalToLink(const Node *par, const Node *child) const { //debugPlan<isDependChildOf(child)) { return false; } if (linkExists(par, child)) { return false; } bool legal = true; // see if par/child is related if (legal && (par->isParentOf(child) || child->isParentOf(par))) { legal = false; } if (legal) legal = legalChildren(par, child); if (legal) legal = legalParents(par, child); if (legal) { foreach (Node *p, par->childNodeIterator()) { if (! legalToLink(p, child)) { return false; } } } return legal; } bool Project::legalParents(const Node *par, const Node *child) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for (int i = 0; i < par->numDependParentNodes() && legal; ++i) { Node *pNode = par->getDependParentNode(i) ->parent(); if (child->isParentOf(pNode) || pNode->isParentOf(child)) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren(pNode, child); } if (legal) legal = legalParents(pNode, child); } return legal; } bool Project::legalChildren(const Node *par, const Node *child) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for (int j = 0; j < child->numDependChildNodes() && legal; ++j) { Node *cNode = child->getDependChildNode(j) ->child(); if (par->isParentOf(cNode) || cNode->isParentOf(par)) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren(par, cNode); } } return legal; } WBSDefinition &Project::wbsDefinition() { return m_wbsDefinition; } void Project::setWbsDefinition(const WBSDefinition &def) { //debugPlan; m_wbsDefinition = def; emit wbsDefinitionChanged(); emit projectChanged(); } QString Project::generateWBSCode(QList &indexes, bool sortable) const { QString code = m_wbsDefinition.projectCode(); if (sortable) { int fw = (nodeIdDict.count() / 10) + 1; QLatin1Char fc('0'); foreach (int index, indexes) { code += ".%1"; code = code.arg(QString::number(index), fw, fc); } //debugPlan< hash = resourceIdDict; foreach (Resource * r, hash) { r->setCurrentSchedule(id); } emit currentScheduleChanged(); emit projectChanged(); } ScheduleManager *Project::scheduleManager(long id) const { foreach (ScheduleManager *sm, m_managers) { if (sm->scheduleId() == id) { return sm; } } return 0; } ScheduleManager *Project::scheduleManager(const QString &id) const { return m_managerIdMap.value(id); } ScheduleManager *Project::findScheduleManagerByName(const QString &name) const { //debugPlan; ScheduleManager *m = 0; foreach(ScheduleManager *sm, m_managers) { m = sm->findManager(name); if (m) { break; } } return m; } QList Project::allScheduleManagers() const { QList lst; foreach (ScheduleManager *sm, m_managers) { lst << sm; lst << sm->allChildren(); } return lst; } QString Project::uniqueScheduleName() const { //debugPlan; QString n = i18n("Plan"); bool unique = findScheduleManagerByName(n) == 0; if (unique) { return n; } n += " %1"; int i = 1; for (; true; ++i) { unique = findScheduleManagerByName(n.arg(i)) == 0; if (unique) { break; } } return n.arg(i); } void Project::addScheduleManager(ScheduleManager *sm, ScheduleManager *parent, int index) { int row = parent == 0 ? m_managers.count() : parent->childCount(); if (index >= 0 && index < row) { row = index; } if (parent == 0) { emit scheduleManagerToBeAdded(parent, row); m_managers.insert(row, sm); } else { emit scheduleManagerToBeAdded(parent, row); sm->setParentManager(parent, row); } if (sm->managerId().isEmpty()) { sm->setManagerId(uniqueScheduleManagerId()); } Q_ASSERT(! m_managerIdMap.contains(sm->managerId())); m_managerIdMap.insert(sm->managerId(), sm); emit scheduleManagerAdded(sm); emit projectChanged(); //debugPlan<<"Added:"<name()<<", now"<children()) { takeScheduleManager(s); } if (sm->scheduling()) { sm->stopCalculation(); } int index = -1; if (sm->parentManager()) { int index = sm->parentManager()->indexOf(sm); if (index >= 0) { emit scheduleManagerToBeRemoved(sm); sm->setParentManager(0); m_managerIdMap.remove(sm->managerId()); emit scheduleManagerRemoved(sm); emit projectChanged(); } } else { index = indexOf(sm); if (index >= 0) { emit scheduleManagerToBeRemoved(sm); m_managers.removeAt(indexOf(sm)); m_managerIdMap.remove(sm->managerId()); emit scheduleManagerRemoved(sm); emit projectChanged(); } } return index; } void Project::swapScheduleManagers(ScheduleManager *from, ScheduleManager *to) { emit scheduleManagersSwapped(from, to); } void Project::moveScheduleManager(ScheduleManager *sm, ScheduleManager *newparent, int newindex) { //debugPlan<name()<parentManager()) { m_managers.removeAt(indexOf(sm)); } sm->setParentManager(newparent, newindex); if (! newparent) { m_managers.insert(newindex, sm); } emit scheduleManagerMoved(sm, newindex); } bool Project::isScheduleManager(void *ptr) const { const ScheduleManager *sm = static_cast(ptr); if (indexOf(sm) >= 0) { return true; } foreach (ScheduleManager *p, m_managers) { if (p->isParentOf(sm)) { return true; } } return false; } ScheduleManager *Project::createScheduleManager(const QString &name) { //debugPlan<isBaselined()) { return true; } } return false; } Schedule *s = schedule(id); return s == 0 ? false : s->isBaselined(); } MainSchedule *Project::createSchedule(const QString& name, Schedule::Type type) { //debugPlan<<"No of schedules:"<setName(name); sch->setType(type); addMainSchedule(sch); return sch; } void Project::addMainSchedule(MainSchedule *sch) { if (sch == 0) { return; } //debugPlan<<"No of schedules:"<setId(i); sch->setNode(this); addSchedule(sch); } bool Project::removeCalendarId(const QString &id) { //debugPlan <<"id=" << id; return calendarIdDict.remove(id); } void Project::insertCalendarId(const QString &id, Calendar *calendar) { //debugPlan <<"id=" << id <<":" << calendar->name(); calendarIdDict.insert(id, calendar); } void Project::changed(Node *node, int property) { if (m_parent == 0) { Node::changed(node, property); // reset cache if (property != Node::TypeProperty) { // add/remove node is handled elsewhere emit nodeChanged(node, property); emit projectChanged(); } return; } Node::changed(node, property); } void Project::changed(ResourceGroup *group) { //debugPlan; emit resourceGroupChanged(group); emit projectChanged(); } void Project::changed(ScheduleManager *sm, int property) { emit scheduleManagerChanged(sm, property); emit projectChanged(); } void Project::changed(MainSchedule *sch) { //debugPlan<id(); emit scheduleChanged(sch); emit projectChanged(); } void Project::sendScheduleToBeAdded(const ScheduleManager *sm, int row) { emit scheduleToBeAdded(sm, row); } void Project::sendScheduleAdded(const MainSchedule *sch) { //debugPlan<id(); emit scheduleAdded(sch); emit projectChanged(); } void Project::sendScheduleToBeRemoved(const MainSchedule *sch) { //debugPlan<id(); emit scheduleToBeRemoved(sch); } void Project::sendScheduleRemoved(const MainSchedule *sch) { //debugPlan<id(); emit scheduleRemoved(sch); emit projectChanged(); } void Project::changed(Resource *resource) { emit resourceChanged(resource); emit projectChanged(); } void Project::changed(Calendar *cal) { emit calendarChanged(cal); emit projectChanged(); } void Project::changed(StandardWorktime *w) { emit standardWorktimeChanged(w); emit projectChanged(); } bool Project::addRelation(Relation *rel, bool check) { if (rel->parent() == 0 || rel->child() == 0) { return false; } if (check && !legalToLink(rel->parent(), rel->child())) { return false; } emit relationToBeAdded(rel, rel->parent()->numDependChildNodes(), rel->child()->numDependParentNodes()); rel->parent()->addDependChildNode(rel); rel->child()->addDependParentNode(rel); emit relationAdded(rel); emit projectChanged(); return true; } void Project::takeRelation(Relation *rel) { emit relationToBeRemoved(rel); rel->parent() ->takeDependChildNode(rel); rel->child() ->takeDependParentNode(rel); emit relationRemoved(rel); emit projectChanged(); } void Project::setRelationType(Relation *rel, Relation::Type type) { emit relationToBeModified(rel); rel->setType(type); emit relationModified(rel); emit projectChanged(); } void Project::setRelationLag(Relation *rel, const Duration &lag) { emit relationToBeModified(rel); rel->setLag(lag); emit relationModified(rel); emit projectChanged(); } QList Project::flatNodeList(Node *parent) { QList lst; Node *p = parent == 0 ? this : parent; //debugPlan<name()<childNodeIterator()) { lst.append(n); if (n->numChildren() > 0) { lst += flatNodeList(n); } } return lst; } void Project::setSchedulerPlugins(const QMap &plugins) { m_schedulerPlugins = plugins; debugPlan< Project::taskModules(bool includeLocal) const { if (!includeLocal && m_useLocalTaskModules) { QList lst = m_taskModules; lst.removeAll(m_localTaskModulesPath); return lst; } return m_taskModules; } -void Project::setTaskModules(const QList modules) +void Project::setTaskModules(const QList modules, bool useLocalTaskModules) { m_taskModules = modules; + m_useLocalTaskModules = useLocalTaskModules; if (m_useLocalTaskModules && m_localTaskModulesPath.isValid()) { m_taskModules.prepend(m_localTaskModulesPath); } emit taskModulesChanged(m_taskModules); } bool Project::useLocalTaskModules() const { return m_useLocalTaskModules; } void Project::setUseLocalTaskModules(bool value, bool emitChanged) { if (m_useLocalTaskModules) { m_taskModules.removeAll(m_localTaskModulesPath); } m_useLocalTaskModules = value; if (m_useLocalTaskModules && m_localTaskModulesPath.isValid()) { m_taskModules.prepend(m_localTaskModulesPath); } - emit taskModulesChanged(m_taskModules); + if (emitChanged) { + emit taskModulesChanged(m_taskModules); + } } void Project::setLocalTaskModulesPath(const QUrl &url) { m_taskModules.removeAll(m_localTaskModulesPath); m_localTaskModulesPath = url; if (m_useLocalTaskModules && url.isValid()) { m_taskModules.prepend(url); } emit taskModulesChanged(m_taskModules); } } //KPlato namespace diff --git a/src/libs/kernel/kptproject.h b/src/libs/kernel/kptproject.h index c1e03251..c9a5ad4b 100644 --- a/src/libs/kernel/kptproject.h +++ b/src/libs/kernel/kptproject.h @@ -1,765 +1,765 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas Zander zander@kde.org Copyright (C) 2004 - 2010 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTPROJECT_H #define KPTPROJECT_H #include "plankernel_export.h" #include "kptnode.h" #include "kptglobal.h" #include "kptaccount.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptduration.h" #include "kptresource.h" #include "kptwbsdefinition.h" #include "kptconfigbase.h" #include #include #include #include #include /// The main namespace. namespace KPlato { class Locale; class Schedule; class StandardWorktime; class ScheduleManager; class XMLLoaderObject; class Task; class SchedulerPlugin; class KPlatoXmlLoaderBase; class XmlSaveContext; /** * Project is the main node in a project, it contains child nodes and * possibly sub-projects. A sub-project is just another instantiation of this * node however. * * A note on timezones: * To be able to handle resources working in different timezones and * to facilitate data exchange with other applications like PIMs or * and groupware servers, the project has a timezone that is used for * all datetimes in nodes and schedules. * By default the local timezone is used. * * A resources timezone is defined by the associated calendar. * * Note that a projects datetimes are always displayed/modified in the timezone * it was originally created, not necessarily in your current local timezone. */ class PLANKERNEL_EXPORT Project : public Node { Q_OBJECT public: explicit Project(Node *parent = 0); explicit Project(ConfigBase &config, Node *parent = 0); explicit Project(ConfigBase &config, bool useDefaultValues, Node *parent = 0); ~Project() override; /// Reference this project. void ref() { ++m_refCount; } /// De-reference this project. Deletes project of ref count <= 0 void deref(); /// Returns the node type. Can be Type_Project or Type_Subproject. int type() const override; /** * Calculate the schedules managed by the schedule manager * * @param sm Schedule manager */ void calculate(ScheduleManager &sm); /** * Re-calculate the schedules managed by the schedule manager * * @param sm Schedule manager * @param dt The datetime from when the schedule shall be re-calculated */ void calculate(ScheduleManager &sm, const DateTime &dt); DateTime startTime(long id = -1) const override; DateTime endTime(long id = -1) const override; /// Returns the calculated duration for schedule @p id Duration duration(long id = -1) const; using Node::duration; /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ Duration *getRandomDuration() override; bool load(KoXmlElement &element, XMLLoaderObject &status) override; void save(QDomElement &element, const XmlSaveContext &context) const override; using Node::saveWorkPackageXML; /// Save a workpackage document containing @p node with schedule identity @p id void saveWorkPackageXML(QDomElement &element, const Node *node, long id) const; /** * Add the node @p task to the project, after node @p position * If @p position is zero or the project node, it will be added to this project. */ bool addTask(Node* task, Node* position); /** * Add the node @p task to the @p parent */ bool addSubTask(Node* task, Node* parent); /** * Add the node @p task to @p parent, in position @p index * If @p parent is zero, it will be added to this project. */ bool addSubTask(Node* task, int index, Node* parent, bool emitSignal = true); /** * Remove the @p node. * The node is not deleted. */ void takeTask(Node *node, bool emitSignal = true); bool canMoveTask(Node* node, Node *newParent, bool checkBaselined = false); bool moveTask(Node* node, Node *newParent, int newPos); bool canIndentTask(Node* node); bool indentTask(Node* node, int index = -1); bool canUnindentTask(Node* node); bool unindentTask(Node* node); bool canMoveTaskUp(Node* node); bool moveTaskUp(Node* node); bool canMoveTaskDown(Node* node); bool moveTaskDown(Node* node); /** * Create a task with a unique id. * The task is not added to the project. Do this with addSubTask(). */ Task *createTask(); /** * Create a copy of @p def with a unique id. * The task is not added to the project. Do this with addSubTask(). */ Task *createTask(const Task &def); /// @return true if any of the tasks has been started bool isStarted() const; int resourceGroupCount() const { return m_resourceGroups.count(); } QList &resourceGroups(); /// Adds the resource group to the project. virtual void addResourceGroup(ResourceGroup *resource, int index = -1); /** * Removes the resource group @p resource from the project. * The resource group is not deleted. */ ResourceGroup *takeResourceGroup(ResourceGroup *resource); int indexOf(ResourceGroup *resource) const { return m_resourceGroups.indexOf(resource); } ResourceGroup *resourceGroupAt(int pos) const { return m_resourceGroups.value(pos); } int numResourceGroups() const { return m_resourceGroups.count(); } /// Returns the resourcegroup with identity id. ResourceGroup *group(const QString& id); /// Returns the resource group with the matching name, 0 if no match is found. ResourceGroup *groupByName(const QString& name) const; /** * Adds the resource to the project and resource group. * Always use this to add resources. */ void addResource(ResourceGroup *group, Resource *resource, int index = -1); /** * Removes the resource from the project and resource group. * The resource is not deleted. * Always use this to remove resources. */ Resource *takeResource(ResourceGroup *group, Resource *resource); /// Move @p resource to the new @p group. Requests are removed. void moveResource(ResourceGroup *group, Resource *resource); /// Returns the resource with identity id. Resource *resource(const QString& id); /// Returns the resource with matching name, 0 if no match is found. Resource *resourceByName(const QString& name) const; QStringList resourceNameList() const; /// Returns a list of all resources QList resourceList() const { return resourceIdDict.values(); } EffortCostMap plannedEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; using Node::plannedEffort; /// Returns the total planned effort for this project (or subproject) Duration plannedEffort(long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; /// Returns the total planned effort for this project (or subproject) on date Duration plannedEffort(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const override; using Node::plannedEffortTo; /// Returns the planned effort up to and including date Duration plannedEffortTo(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const override; /// Returns the actual effort up to and including @p date Duration actualEffortTo(QDate date) const override; /** * Planned cost up to and including date * @param date The cost is calculated from the start of the project upto including date. * @param id Identity of the schedule to be used. * @param typ the type of calculation. * @sa EffortCostCalculationType */ double plannedCostTo(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const override; /** * Actual cost up to and including @p date * @param id Identity of the schedule to be used. * @param date The cost is calculated from the start of the project upto including date. */ EffortCost actualCostTo(long int id, QDate date) const override; EffortCostMap actualEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; EffortCostMap actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All) const override; double effortPerformanceIndex(QDate date, long id) const override; double schedulePerformanceIndex(QDate date, long id) const override; /// Returns the effort planned to be used to reach the actual percent finished Duration budgetedWorkPerformed(QDate date, long id = CURRENTSCHEDULE) const override; /// Returns the cost planned to be used to reach the actual percent finished double budgetedCostPerformed(QDate date, long id = CURRENTSCHEDULE) const override; /// Budgeted Cost of Work Scheduled (up to @p date) double bcws(QDate date, long id = BASELINESCHEDULE) const override; /// Budgeted Cost of Work Performed double bcwp(long id = BASELINESCHEDULE) const override; /// Budgeted Cost of Work Performed (up to @p date) double bcwp(QDate date, long id = BASELINESCHEDULE) const override; Calendar *defaultCalendar() const { return m_defaultCalendar; } void setDefaultCalendar(Calendar *cal); const QList &calendars() const; void addCalendar(Calendar *calendar, Calendar *parent = 0, int index = -1); void takeCalendar(Calendar *calendar); int indexOf(const Calendar *calendar) const; /// Returns the calendar with identity id. Calendar *calendar(const QString& id) const; /// Returns a list of all calendars QStringList calendarNames() const; /// Find calendar by name Calendar *calendarByName(const QString &name) const; void changed(Calendar *cal); QList allCalendars() const; /// Return number of calendars int calendarCount() const { return m_calendars.count(); } /// Return the calendar at @p index, 0 if index out of bounds Calendar *calendarAt(int index) const { return m_calendars.value(index); } /** * Defines the length of days, weeks, months and years * and the standard working week. * Used for estimation and calculation of effort, * and presentation in gantt chart. */ StandardWorktime *standardWorktime() { return m_standardWorktime; } void setStandardWorktime(StandardWorktime * worktime); void changed(StandardWorktime*); /// Check if a link exists between node @p par and @p child. bool linkExists(const Node *par, const Node *child) const; /// Check if node @p par can be linked to node @p child. bool legalToLink(const Node *par, const Node *child) const override; using Node::legalToLink; virtual const QHash &nodeDict() { return nodeIdDict; } /// Return a list of all nodes in the project (excluding myself) QList allNodes(bool ordered = false, Node *parent = nullptr) const; /// Return the number of all nodes in the project (excluding myself) int nodeCount() const { return nodeIdDict.count() - 1; } /// Return a list of all tasks and milestones int the wbs order QList allTasks(const Node *parent = 0) const; using Node::findNode; /// Find the node with identity id Node *findNode(const QString &id) const override; using Node::removeId; /// Remove the node with identity id from the registers bool removeId(const QString &id) override; /// Reserve @p id for the @p node virtual void reserveId(const QString &id, Node *node); /// Register @p node. The nodes id must be unique and non-empty. bool registerNodeId(Node *node); /// Create a unique id. QString uniqueNodeId(int seed = 1) const; /// Check if node @p id is used bool nodeIdentExists(const QString &id) const; /// Create a unique id. QString uniqueNodeId(const QList &existingIds, int seed = 1); ResourceGroup *findResourceGroup(const QString &id) const { if (resourceGroupIdDict.contains(id) ) return resourceGroupIdDict[ id ]; return 0; } /// Remove the resourcegroup with identity id from the register bool removeResourceGroupId(const QString &id) { if (resourceGroupIdDict.contains(id) ) return resourceGroupIdDict.remove(id); return false; } /// Insert the resourcegroup with identity id void insertResourceGroupId(const QString &id, ResourceGroup* group) { resourceGroupIdDict.insert(id, group); } /// Generate, set and insert unique id bool setResourceGroupId(ResourceGroup *group); /// returns a unique resourcegroup id QString uniqueResourceGroupId() const; /// Return a list of resources that will be allocated to new tasks QList autoAllocateResources() const; Resource *findResource(const QString &id) const { if (resourceIdDict.contains(id) ) return resourceIdDict[ id ]; return 0; } /// Remove the resource with identity id from the register bool removeResourceId(const QString &id); /// Insert the resource with identity id void insertResourceId(const QString &id, Resource *resource); /// Generate, set and insert unique id bool setResourceId(Resource *resource); /// returns a unique resource id QString uniqueResourceId() const; /// Find the calendar with identity id virtual Calendar *findCalendar(const QString &id) const { if (id.isEmpty() || !calendarIdDict.contains(id) ) return 0; return calendarIdDict[ id ]; } /// Remove the calendar with identity id from the register virtual bool removeCalendarId(const QString &id); /// Insert the calendar with identity id virtual void insertCalendarId(const QString &id, Calendar *calendar); /// Set and insert a unique id for calendar bool setCalendarId(Calendar *calendar); /// returns a unique calendar id QString uniqueCalendarId() const; /// Return reference to WBS Definition WBSDefinition &wbsDefinition(); /// Set WBS Definition to @p def void setWbsDefinition(const WBSDefinition &def); /// Generate WBS Code QString generateWBSCode(QList &indexes, bool sortable = false) const override; Accounts &accounts() { return m_accounts; } const Accounts &accounts() const { return m_accounts; } /** * Set current schedule to the schedule with identity @p id, for me and my children * Note that this is used (and may be changed) when calculating schedules */ void setCurrentSchedule(long id) override; /// Create new schedule with unique name and id of type Expected. MainSchedule *createSchedule(); /// Create new schedule with unique id. MainSchedule *createSchedule(const QString& name, Schedule::Type type); /// Add the schedule to the project. A fresh id will be generated for the schedule. void addMainSchedule(MainSchedule *schedule); /// Set parent schedule for my children void setParentSchedule(Schedule *sch) override; /// Find the schedule manager that manages the Schedule with @p id ScheduleManager *scheduleManager(long id) const; /// Find the schedule manager with @p id ScheduleManager *scheduleManager(const QString &id) const; /// Create a unique schedule name (This may later be changed by the user) QString uniqueScheduleName() const; /// Create a unique schedule manager identity QString uniqueScheduleManagerId() const; ScheduleManager *createScheduleManager(); ScheduleManager *createScheduleManager(const QString &name); /// Returns a list of all top level schedule managers QList scheduleManagers() const { return m_managers; } int numScheduleManagers() const { return m_managers.count(); } int indexOf(const ScheduleManager *sm) const { return m_managers.indexOf(const_cast(sm)); } bool isScheduleManager(void* ptr) const; void addScheduleManager(ScheduleManager *sm, ScheduleManager *parent = 0, int index = -1); int takeScheduleManager(ScheduleManager *sm); void moveScheduleManager(ScheduleManager *sm, ScheduleManager *newparent = 0, int newindex = -1); ScheduleManager *findScheduleManagerByName(const QString &name) const; /// Returns a list of all schedule managers QList allScheduleManagers() const; /// Return true if schedule with identity @p id is baselined bool isBaselined(long id = ANYSCHEDULED) const; void changed(ResourceGroup *group); void changed(Resource *resource); void changed(ScheduleManager *sm, int property = -1); void changed(MainSchedule *sch); void sendScheduleAdded(const MainSchedule *sch); void sendScheduleToBeAdded(const ScheduleManager *manager, int row); void sendScheduleRemoved(const MainSchedule *sch); void sendScheduleToBeRemoved(const MainSchedule *sch); /// Return the time zone used in this project QTimeZone timeZone() const { return m_timeZone; } /// Set the time zone to be used in this project void setTimeZone(const QTimeZone &tz) { m_timeZone = tz; } /** * Add a relation between the nodes specified in the relation rel. * Emits signals relationToBeAdded() before the relation is added, * and relationAdded() after it has been added. * @param rel The relation to be added. * @param check If true, the relation is checked for validity * @return true if successful. */ bool addRelation(Relation *rel, bool check=true); /** * Removes the relation @p rel without deleting it. * Emits signals relationToBeRemoved() before the relation is removed, * and relationRemoved() after it has been removed. */ void takeRelation(Relation *rel); /** * Modify the @p type of the @p relation. */ void setRelationType(Relation *relation, Relation::Type type); /** * Modify the @p lag of the @p relation. */ void setRelationLag(Relation *relation, const Duration &lag); void calcCriticalPathList(MainSchedule *cs); void calcCriticalPathList(MainSchedule *cs, Node *node); /** * Returns the list of critical paths for schedule @p id */ const QList< QList > *criticalPathList(long id = CURRENTSCHEDULE); QList criticalPath(long id = CURRENTSCHEDULE, int index = 0); /// Returns a flat list af all nodes QList flatNodeList(Node *parent = 0); void generateUniqueNodeIds(); void generateUniqueIds(); const ConfigBase &config() const { return m_config ? *m_config : emptyConfig; } /// Set configuration data void setConfig(ConfigBase *config) { m_config = config; } const Task &taskDefaults() const { return config().taskDefaults(); } /// Return locale. (Used for currency, everything else is from KGlobal::locale) Locale *locale() { return const_cast(config()).locale(); } /// Return locale. (Used for currency, everything else is from KGlobal::locale) const Locale *locale() const { return config().locale(); } /// Signal that locale data has changed void emitLocaleChanged(); void setSchedulerPlugins(const QMap &plugins); const QMap &schedulerPlugins() const { return m_schedulerPlugins; } void initiateCalculation(MainSchedule &sch) override; void initiateCalculationLists(MainSchedule &sch) override; void finishCalculation(ScheduleManager &sm); void adjustSummarytask() override; /// Increments progress and emits signal sigProgress() void incProgress(); /// Emits signal maxProgress() void emitMaxProgress(int value); bool stopcalculation; /// return a map of all external projects QMap externalProjects() const; void emitDocumentAdded(Node*, Document*, int index) override; void emitDocumentRemoved(Node*, Document*, int index) override; void emitDocumentChanged(Node*, Document*, int index) override; bool useSharedResources() const; void setUseSharedResources(bool on); bool isSharedResourcesLoaded() const; void setSharedResourcesLoaded(bool on); void setSharedResourcesFile(const QString &file); QString sharedResourcesFile() const; void setSharedProjectsUrl(const QUrl &url); QUrl sharedProjectsUrl() const; void setLoadProjectsAtStartup(bool value); bool loadProjectsAtStartup() const; QList taskModules(bool includeLocal = true) const; - void setTaskModules(const QList modules); + void setTaskModules(const QList modules, bool useLocalTaskModules); bool useLocalTaskModules() const; void setUseLocalTaskModules(bool value, bool emitChanged = true); void setLocalTaskModulesPath(const QUrl &url); public Q_SLOTS: /// Sets m_progress to @p progress and emits signal sigProgress() /// If @p sm is not 0, progress is also set for the schedule manager void setProgress(int progress, KPlato::ScheduleManager *sm = 0); /// Sets m_maxprogress to @p max and emits signal maxProgress() /// If @p sm is not 0, max progress is also set for the schedule manager void setMaxProgress(int max, KPlato::ScheduleManager *sm = 0); void swapScheduleManagers(KPlato::ScheduleManager *from, KPlato::ScheduleManager *to); Q_SIGNALS: void scheduleManagersSwapped(KPlato::ScheduleManager *from, KPlato::ScheduleManager *to); /// Emitted when the project is about to be deleted (The destroyed signal is disabled) void aboutToBeDeleted(); /// Emitted when anything in the project is changed (use with care) void projectChanged(); /// Emitted when the WBS code definition has changed. This may change all nodes. void wbsDefinitionChanged(); /// Emitted when a schedule has been calculated void projectCalculated(KPlato::ScheduleManager *sm); /// Emitted when the pointer to the current schedule has been changed void currentScheduleChanged(); /// Use to show progress during calculation void sigProgress(int); /// Use to set the maximum progress (minimum always 0) void maxProgress(int); /// Emitted when calculation starts void sigCalculationStarted(KPlato::Project *project, KPlato::ScheduleManager *sm); /// Emitted when calculation is finished void sigCalculationFinished(KPlato::Project *project, KPlato::ScheduleManager *sm); /// This signal is emitted when one of the nodes members is changed. void nodeChanged(KPlato::Node*, int); /// This signal is emitted when the node is to be added to the project. void nodeToBeAdded(KPlato::Node*, int); /// This signal is emitted when the node has been added to the project. void nodeAdded(KPlato::Node*); /// This signal is emitted when the node is to be removed from the project. void nodeToBeRemoved(KPlato::Node*); /// This signal is emitted when the node has been removed from the project. void nodeRemoved(KPlato::Node*); /// This signal is emitted when the node is to be moved up, moved down, indented or unindented. void nodeToBeMoved(KPlato::Node* node, int pos, KPlato::Node* newParent, int newPos); /// This signal is emitted when the node has been moved up, moved down, indented or unindented. void nodeMoved(KPlato::Node*); /// This signal is emitted when a document is added void documentAdded(KPlato::Node*, KPlato::Document*, int index); /// This signal is emitted when a document is removed void documentRemoved(KPlato::Node*, KPlato::Document*, int index); /// This signal is emitted when a document is changed void documentChanged(KPlato::Node*, KPlato::Document*, int index); void resourceGroupChanged(KPlato::ResourceGroup *group); void resourceGroupAdded(const KPlato::ResourceGroup *group); void resourceGroupToBeAdded(const KPlato::ResourceGroup *group, int row); void resourceGroupRemoved(const KPlato::ResourceGroup *group); void resourceGroupToBeRemoved(const KPlato::ResourceGroup *group); void resourceChanged(KPlato::Resource *resource); void resourceAdded(const KPlato::Resource *resource); void resourceToBeAdded(const KPlato::ResourceGroup *group, int row); void resourceRemoved(const KPlato::Resource *resource); void resourceToBeRemoved(const KPlato::Resource *resource); void scheduleManagerChanged(KPlato::ScheduleManager *sch, int property = -1); void scheduleManagerAdded(const KPlato::ScheduleManager *sch); void scheduleManagerToBeAdded(const KPlato::ScheduleManager *sch, int row); void scheduleManagerRemoved(const KPlato::ScheduleManager *sch); void scheduleManagerToBeRemoved(const KPlato::ScheduleManager *sch); void scheduleManagerMoved(const KPlato::ScheduleManager *sch, int row); void scheduleManagerToBeMoved(const KPlato::ScheduleManager *sch); void scheduleChanged(KPlato::MainSchedule *sch); void scheduleToBeAdded(const KPlato::ScheduleManager *manager, int row); void scheduleAdded(const KPlato::MainSchedule *sch); void scheduleToBeRemoved(const KPlato::MainSchedule *sch); void scheduleRemoved(const KPlato::MainSchedule *sch); // void currentViewScheduleIdChanged(long id); void calendarChanged(KPlato::Calendar *cal); void calendarToBeAdded(const KPlato::Calendar *cal, int row); void calendarAdded(const KPlato::Calendar *cal); void calendarToBeRemoved(const KPlato::Calendar *cal); void calendarRemoved(const KPlato::Calendar *cal); /** * Emitted when the default calendar pointer has changed * @param cal The new default calendar. May be 0. */ void defaultCalendarChanged(KPlato::Calendar *cal); /** * Emitted when the standard worktime has been changed. */ void standardWorktimeChanged(KPlato::StandardWorktime*); /// Emitted when the relation @p rel is about to be added. void relationToBeAdded(KPlato::Relation *rel, int parentIndex, int childIndex); /// Emitted when the relation @p rel has been added. void relationAdded(KPlato::Relation *rel); /// Emitted when the relation @p rel is about to be removed. void relationToBeRemoved(KPlato::Relation *rel); /// Emitted when the relation @p rel has been removed. void relationRemoved(KPlato::Relation *rel); /// Emitted when the relation @p rel shall be modified. void relationToBeModified(KPlato::Relation *rel); /// Emitted when the relation @p rel has been modified. void relationModified(KPlato::Relation *rel); /// Emitted when locale data has changed void localeChanged(); void taskModulesChanged(const QList &modules); protected: /// Calculate the schedule. void calculate(Schedule *scedule); /// Calculate current schedule void calculate(); /// Re-calculate the schedule from @p dt void calculate(Schedule *scedule, const DateTime &dt); /// Calculate current schedule from @p dt (Always calculates forward) void calculate(const DateTime &dt); /// Calculate critical path bool calcCriticalPath(bool fromEnd) override; /// Prepare task lists for scheduling void tasksForward(); /// Prepare task lists for scheduling void tasksBackward(); protected: friend class KPlatoXmlLoaderBase; using Node::changed; void changed(Node *node, int property = -1) override; Accounts m_accounts; QList m_resourceGroups; QList m_calendars; Calendar * m_defaultCalendar; StandardWorktime *m_standardWorktime; DateTime calculateForward(int use) override; DateTime calculateBackward(int use) override; DateTime scheduleForward(const DateTime &earliest, int use) override; DateTime scheduleBackward(const DateTime &latest, int use) override; DateTime checkStartConstraints(const DateTime &dt) const; DateTime checkEndConstraints(const DateTime &dt) const; bool legalParents(const Node *par, const Node *child) const; bool legalChildren(const Node *par, const Node *child) const; #ifndef PLAN_NLOGDEBUG private: static bool checkParent(Node *n, const QList &list, QList &checked); static bool checkChildren(Node *n, const QList &list, QList &checked); #endif private: void init(); QHash resourceGroupIdDict; QHash resourceIdDict; QHash nodeIdDict; QMap nodeIdReserved; QMap calendarIdDict; QMap m_managerIdMap; QList m_managers; QTimeZone m_timeZone; WBSDefinition m_wbsDefinition; ConfigBase emptyConfig; QPointer m_config; // this one is not owned by me, don't delete int m_progress; QMap m_schedulerPlugins; int m_refCount; // make it possible to use the project by different threads QList m_hardConstraints; QList m_softConstraints; QMultiMap m_terminalNodes; bool m_useSharedResources; bool m_sharedResourcesLoaded; QString m_sharedResourcesFile; QUrl m_sharedProjectsUrl; bool m_loadProjectsAtStartup; public: class WorkPackageInfo { public: WorkPackageInfo() : checkForWorkPackages(false), deleteAfterRetrieval(true), archiveAfterRetrieval(false) {} bool checkForWorkPackages; QUrl retrieveUrl; bool deleteAfterRetrieval; bool archiveAfterRetrieval; QUrl archiveUrl; QUrl publishUrl; bool operator!=(const WorkPackageInfo &o) { return !operator==(o); } bool operator==(const WorkPackageInfo &o) { return checkForWorkPackages == o.checkForWorkPackages && deleteAfterRetrieval == o.deleteAfterRetrieval && archiveAfterRetrieval == o.archiveAfterRetrieval && retrieveUrl == o.retrieveUrl && archiveUrl == o.archiveUrl && publishUrl == o.publishUrl; } }; void setWorkPackageInfo(const WorkPackageInfo &wpInfo) { m_workPackageInfo = wpInfo; } WorkPackageInfo workPackageInfo() const { return m_workPackageInfo; } private: WorkPackageInfo m_workPackageInfo; QList m_taskModules; bool m_useLocalTaskModules; QUrl m_localTaskModulesPath; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kptmainprojectpanel.cpp b/src/libs/ui/kptmainprojectpanel.cpp index 1ef4db85..d8c14e63 100644 --- a/src/libs/ui/kptmainprojectpanel.cpp +++ b/src/libs/ui/kptmainprojectpanel.cpp @@ -1,444 +1,443 @@ /* This file is part of the KDE project Copyright (C) 2004-2007, 2011, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptmainprojectpanel.h" - +#include #include "kptdebug.h" #include #ifdef PLAN_KDEPIMLIBS_FOUND #include #include #include #endif #include "kptproject.h" #include "kptcommand.h" #include "kptschedule.h" #include "kpttaskdescriptiondialog.h" #include "kptdocumentspanel.h" #include #include #include #include #include #include namespace KPlato { MainProjectPanel::MainProjectPanel(Project &p, QWidget *parent) : QWidget(parent), project(p) { setupUi(this); #ifndef PLAN_KDEPIMLIBS_FOUND chooseLeader->hide(); #endif // FIXME // [Bug 311940] New: Plan crashes when typing a text in the filter textbox before the textbook is fully loaded when selecting a contact from the addressbook chooseLeader->hide(); QString s = xi18nc("@info:whatsthis", "The Work Breakdown Structure introduces numbering for all tasks in the project, according to the task structure." "The WBS code is auto-generated." "You can define the WBS code pattern using the Project->Define WBS Pattern menu entry."); wbslabel->setWhatsThis(s); wbs->setWhatsThis(s); namefield->setText(project.name()); leaderfield->setText(project.leader()); // useSharedResources->setEnabled(!project.isSharedResourcesLoaded()); useSharedResources->setChecked(project.useSharedResources()); resourcesFile->setText(project.sharedResourcesFile()); projectsPlace->setText(project.sharedProjectsUrl().toDisplayString()); const Project::WorkPackageInfo wpi = p.workPackageInfo(); ui_CheckForWorkPackages->setChecked(wpi.checkForWorkPackages); ui_RetrieveUrl->setUrl(wpi.retrieveUrl); ui_DeleteFile->setChecked(wpi.deleteAfterRetrieval); ui_ArchiveFile->setChecked(wpi.archiveAfterRetrieval); ui_ArchiveUrl->setUrl(wpi.archiveUrl); ui_PublishUrl->setUrl(wpi.publishUrl); ui_RetrieveUrl->setMode(KFile::Directory); ui_ArchiveUrl->setMode(KFile::Directory); ui_PublishUrl->setMode(KFile::Directory); // Disable publish for now // FIXME: Enable when fully implemented ui_publishGroup->hide(); m_documents = new DocumentsPanel(p, ui_documents); ui_documents->layout()->addWidget(m_documents); m_description = new TaskDescriptionPanel(p, ui_description); m_description->namefield->hide(); m_description->namelabel->hide(); ui_description->layout()->addWidget(m_description); wbs->setText(project.wbsCode()); if (wbs->text().isEmpty()) { wbslabel->hide(); wbs->hide(); } DateTime st = project.constraintStartTime(); DateTime et = project.constraintEndTime(); startDate->setDate(st.date()); startTime->setTime(QTime(st.time().hour(), st.time().minute(), 0)); endDate->setDate(et.date()); endTime->setTime(QTime(et.time().hour(), et.time().minute(), 0)); enableDateTime(); namefield->setFocus(); useSharedResources->setToolTip(xi18nc("@info:tooltip", "Enables sharing resources with other projects")); useSharedResources->setWhatsThis(xi18nc("@info:whatsthis", "Shared resources" "Resources can be shared between projects" " to avoid overbooking resources across projects." " Shared resources must be defined in a separate file," " and you must have at least read access to it." " The projects that share the resources must also be" " accessible by you." )); s = xi18nc("@info:tooltip", "File where shared resources are defined"); resourcesLabel->setToolTip(s); resourcesType->setToolTip(s); resourcesFile->setToolTip(s); s = xi18nc("@info:tooltip", "Directory where all the projects that share resources can be found"); projectsLabel->setToolTip(s); projectsType->setToolTip(s); projectsPlace->setToolTip(s); projectsLoadAtStartup->setChecked(project.loadProjectsAtStartup()); projectsLoadAtStartup->setToolTip(xi18nc("@info:tooltip", "Load shared resource assignments at startup")); projectsLoadBtn->setToolTip(xi18nc("@info:tooltip", "Load (or re-load) shared resource assignments")); projectsClearBtn->setToolTip(xi18nc("@info:tooltip", "Clear shared resource assignments")); initTaskModules(); // signals and slots connections connect(m_documents, &DocumentsPanel::changed, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(m_description, &TaskDescriptionPanelImpl::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(endDate, &QDateTimeEdit::dateChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(endTime, &QDateTimeEdit::timeChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(startDate, &QDateTimeEdit::dateChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(startTime, &QDateTimeEdit::timeChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(namefield, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(leaderfield, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(useSharedResources, &QGroupBox::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(resourcesFile, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(projectsPlace, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(projectsLoadAtStartup, &QAbstractButton::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(chooseLeader, &QAbstractButton::clicked, this, &MainProjectPanel::slotChooseLeader); connect(resourcesBrowseBtn, &QAbstractButton::clicked, this, &MainProjectPanel::openResourcesFile); connect(projectsBrowseBtn, &QAbstractButton::clicked, this, &MainProjectPanel::openProjectsPlace); connect(projectsLoadBtn, &QAbstractButton::clicked, this, &MainProjectPanel::loadProjects); connect(projectsClearBtn, &QAbstractButton::clicked, this, &MainProjectPanel::clearProjects); connect(ui_CheckForWorkPackages, &QCheckBox::stateChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_RetrieveUrl, &KUrlRequester::textEdited, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_RetrieveUrl, &KUrlRequester::urlSelected, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_DeleteFile, &QRadioButton::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_ArchiveFile, &QRadioButton::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_ArchiveUrl, &KUrlRequester::textEdited, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_ArchiveUrl, &KUrlRequester::urlSelected, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_PublishUrl, &KUrlRequester::textEdited, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect(ui_PublishUrl, &KUrlRequester::urlSelected, this, &MainProjectPanel::slotCheckAllFieldsFilled); // initiate ok button QTimer::singleShot(0, this, &MainProjectPanel::slotCheckAllFieldsFilled); } void MainProjectPanel::initTaskModules() { QStandardItemModel *m = new QStandardItemModel(0, 1, ui_taskModulesView); const QList lst = project.taskModules(false); for (const QUrl &url : lst) { QStandardItem *item = new QStandardItem(url.toString()); m->appendRow(item); } ui_taskModulesView->setModel(m); ui_useLocalModules->setChecked(project.useLocalTaskModules()); connect(ui_insertModule, &QToolButton::clicked, this, &MainProjectPanel::insertTaskModuleClicked); connect(ui_removeModule, &QToolButton::clicked, this, &MainProjectPanel::removeTaskModuleClicked); connect(ui_taskModulesView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainProjectPanel::taskModulesSelectionChanged); connect(ui_useLocalModules, &QCheckBox::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); } bool MainProjectPanel::ok() { if (useSharedResources->isChecked() && resourcesFile->text().isEmpty()) { return false; } return true; } MacroCommand *MainProjectPanel::buildCommand() { MacroCommand *m = 0; KUndo2MagicString c = kundo2_i18n("Modify main project"); if (project.name() != namefield->text()) { if (!m) m = new MacroCommand(c); m->addCommand(new NodeModifyNameCmd(project, namefield->text())); } if (project.leader() != leaderfield->text()) { if (!m) m = new MacroCommand(c); m->addCommand(new NodeModifyLeaderCmd(project, leaderfield->text())); } if (startDateTime() != project.constraintStartTime()) { if (!m) m = new MacroCommand(c); m->addCommand(new ProjectModifyStartTimeCmd(project, startDateTime())); } if (endDateTime() != project.constraintEndTime()) { if (!m) m = new MacroCommand(c); m->addCommand(new ProjectModifyEndTimeCmd(project, endDateTime())); } if (project.useSharedResources() != useSharedResources->isChecked()) { if (!m) m = new MacroCommand(c); m->addCommand(new UseSharedResourcesCmd(&project, useSharedResources->isChecked())); } if (project.sharedResourcesFile() != resourcesFile->text()) { if (!m) m = new MacroCommand(c); m->addCommand(new SharedResourcesFileCmd(&project, resourcesFile->text())); } QString place = projectsPlace->text(); if (projectsType->currentIndex() == 0 /*dir*/ && !place.isEmpty() && !place.endsWith('/')) { place.append('/'); } QUrl sharedProjectsUrl(place); if (project.sharedProjectsUrl() != sharedProjectsUrl) { if (!m) m = new MacroCommand(c); m->addCommand(new SharedProjectsUrlCmd(&project, sharedProjectsUrl)); } if (project.loadProjectsAtStartup() != projectsLoadAtStartup->isChecked()) { if (!m) m = new MacroCommand(c); m->addCommand(new LoadProjectsAtStartupCmd(&project, projectsLoadAtStartup->isChecked())); } MacroCommand *cmd = m_description->buildCommand(); if (cmd) { if (!m) m = new MacroCommand(c); m->addCommand(cmd); } cmd = m_documents->buildCommand(); if (cmd) { if (!m) m = new MacroCommand(c); m->addCommand(cmd); } cmd = buildTaskModulesCommand(); if (cmd) { if (!m) m = new MacroCommand(c); m->addCommand(cmd); } Project::WorkPackageInfo wpi; wpi.checkForWorkPackages = ui_CheckForWorkPackages->isChecked(); wpi.retrieveUrl = ui_RetrieveUrl->url(); wpi.deleteAfterRetrieval = ui_DeleteFile->isChecked(); wpi.archiveAfterRetrieval = ui_ArchiveFile->isChecked(); wpi.archiveUrl = ui_ArchiveUrl->url(); wpi.publishUrl = ui_PublishUrl->url(); if (wpi != project.workPackageInfo()) { ProjectModifyWorkPackageInfoCmd *cmd = new ProjectModifyWorkPackageInfoCmd(project, wpi); if (!m) m = new MacroCommand(c); m->addCommand(cmd); } return m; } MacroCommand *MainProjectPanel::buildTaskModulesCommand() { MacroCommand *cmd = new MacroCommand(); QAbstractItemModel *m = ui_taskModulesView->model(); QList lst; for (QModelIndex idx = m->index(0,0); idx.isValid(); idx = idx.sibling(idx.row()+1, 0)) { QUrl url = QUrl::fromUserInput(idx.data().toString()); if (url.isValid() && !lst.contains(url)) { lst << url; } } - project.setTaskModules(lst); //TODO command - project.setUseLocalTaskModules(ui_useLocalModules->isChecked()); + cmd->addCommand(new SetTaskModulesCommand(&project, lst, ui_useLocalModules->isChecked())); return cmd; } void MainProjectPanel::slotCheckAllFieldsFilled() { emit changed(); bool state = !namefield->text().isEmpty(); if (state && useSharedResources->isChecked()) { state = !resourcesFile->text().isEmpty(); if (state && projectsLoadAtStartup->isChecked()) { state = !projectsPlace->text().isEmpty(); } } emit obligatedFieldsFilled(state); } void MainProjectPanel::slotChooseLeader() { #ifdef PLAN_KDEPIMLIBS_FOUND QPointer dlg = new Akonadi::EmailAddressSelectionDialog(this); if (dlg->exec() && dlg) { QStringList names; const Akonadi::EmailAddressSelection::List selections = dlg->selectedAddresses(); foreach (const Akonadi::EmailAddressSelection &selection, selections) { QString s = selection.name(); if (! selection.email().isEmpty()) { if (! selection.name().isEmpty()) { s += " <"; } s += selection.email(); if (! selection.name().isEmpty()) { s += '>'; } if (! s.isEmpty()) { names << s; } } } if (! names.isEmpty()) { leaderfield->setText(names.join(", ")); } } #endif } void MainProjectPanel::slotStartDateClicked() { enableDateTime(); } void MainProjectPanel::slotEndDateClicked() { enableDateTime(); } void MainProjectPanel::enableDateTime() { debugPlan; startTime->setEnabled(true); startDate->setEnabled(true); endTime->setEnabled(true); endDate->setEnabled(true); } QDateTime MainProjectPanel::startDateTime() { return QDateTime(startDate->date(), startTime->time(), Qt::LocalTime); } QDateTime MainProjectPanel::endDateTime() { return QDateTime(endDate->date(), endTime->time(), Qt::LocalTime); } void MainProjectPanel::openResourcesFile() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Resources"), "", tr("Resources file (*.plan)")); resourcesFile->setText(fileName); } void MainProjectPanel::openProjectsPlace() { if (projectsType->currentIndex() == 0 /*Directory*/) { debugPlan<<"Directory"; QString dirName = QFileDialog::getExistingDirectory(this, tr("Projects Directory")); projectsPlace->setText(dirName); return; } if (projectsType->currentIndex() == 1 /*File*/) { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Projects"), "", tr("Projects file (*)")); projectsPlace->setText(fileName); return; } Q_ASSERT(false); // Unimplemented projects type } bool MainProjectPanel::loadSharedResources() const { return useSharedResources->isChecked(); } void MainProjectPanel::loadProjects() { QString place = projectsPlace->text(); if (projectsType->currentIndex() == 0 /*dir*/ && !place.isEmpty() && !place.endsWith('/')) { place.append('/'); } QUrl url(place); emit loadResourceAssignments(url); } void MainProjectPanel::clearProjects() { emit clearResourceAssignments(); } void MainProjectPanel::insertTaskModuleClicked() { QString dirName = QFileDialog::getExistingDirectory(this, i18n("Task Modules Path")); if (!dirName.isEmpty()) { dirName = QUrl::fromUserInput(dirName).toString(); QStandardItemModel *m = static_cast(ui_taskModulesView->model()); for (int r = 0; r < m->rowCount(); ++r) { QUrl url1(dirName); QUrl url2 = QUrl::fromUserInput(m->index(r, 0).data().toString()); if (url1.matches(url2, QUrl::StripTrailingSlash|QUrl::NormalizePathSegments)) { break; } QStandardItem *item = new QStandardItem(dirName); m->appendRow(item); slotCheckAllFieldsFilled(); } } } void MainProjectPanel::removeTaskModuleClicked() { QList lst = ui_taskModulesView->selectionModel()->selectedRows(); for (const QModelIndex &idx : lst) { ui_taskModulesView->model()->removeRow(idx.row(), idx.parent()); } if (!lst.isEmpty()) { slotCheckAllFieldsFilled(); } } void MainProjectPanel::taskModulesSelectionChanged() { } } //KPlato namespace