1 /* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2004 - 2010, 2012 Dag Andersen <danders@get2net.dk>
4 Copyright (C) 2007 Florian Piquemal <flotueur@yahoo.fr>
5 Copyright (C) 2007 Alexis Ménard <darktears31@gmail.com>
6 Copyright (C) 2019 Dag Andersen <danders@get2net.dk>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 // clazy:excludeall=qstring-arg
25 #include "kptproject.h"
26
27 #include "kptlocale.h"
28 #include "kptappointment.h"
29 #include "kpttask.h"
30 #include "kptdatetime.h"
31 #include "kpteffortcostmap.h"
32 #include "kptschedule.h"
33 #include "kptwbsdefinition.h"
34 #include "kptxmlloaderobject.h"
35 #include "XmlSaveContext.h"
36 #include "kptschedulerplugin.h"
37 #include "kptdebug.h"
38
39 #include <KoXmlReader.h>
40
41 #include <krandom.h>
42 #include <KFormat>
43 #include <KLocalizedString>
44
45 #include <QDateTime>
46 #include <QLocale>
47
48 namespace KPlato
49 {
50
generateId()51 QString generateId()
52 {
53 return QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMddHHmmss")) + KRandom::randomString(10);
54 }
55
Project(Node * parent)56 Project::Project(Node *parent)
57 : Node(parent),
58 m_accounts(*this),
59 m_defaultCalendar(0),
60 m_config(&emptyConfig),
61 m_schedulerPlugins(),
62 m_useSharedResources(false),
63 m_sharedResourcesLoaded(false),
64 m_loadProjectsAtStartup(false),
65 m_useLocalTaskModules(true)
66 {
67 //debugPlan<<"("<<this<<")";
68 init();
69 }
70
Project(ConfigBase & config,Node * parent)71 Project::Project(ConfigBase &config, Node *parent)
72 : Node(parent),
73 m_accounts(*this),
74 m_defaultCalendar(0),
75 m_config(&config),
76 m_schedulerPlugins(),
77 m_useSharedResources(false),
78 m_sharedResourcesLoaded(false),
79 m_loadProjectsAtStartup(false),
80 m_useLocalTaskModules(true)
81 {
82 debugPlan<<"("<<this<<")";
83 init();
84 m_config->setDefaultValues(*this);
85 }
86
Project(ConfigBase & config,bool useDefaultValues,Node * parent)87 Project::Project(ConfigBase &config, bool useDefaultValues, Node *parent)
88 : Node(parent),
89 m_accounts(*this),
90 m_defaultCalendar(0),
91 m_config(&config),
92 m_schedulerPlugins(),
93 m_useSharedResources(false),
94 m_sharedResourcesLoaded(false),
95 m_loadProjectsAtStartup(false),
96 m_useLocalTaskModules(true)
97 {
98 debugPlan<<"("<<this<<")";
99 init();
100 if (useDefaultValues) {
101 m_config->setDefaultValues(*this);
102 }
103 }
104
init()105 void Project::init()
106 {
107 m_refCount = 1; // always used by creator
108
109 m_constraint = Node::MustStartOn;
110 m_standardWorktime = new StandardWorktime();
111 m_timeZone = QTimeZone::systemTimeZone(); // local timezone as default
112 //debugPlan<<m_timeZone;
113 if (m_parent == 0) {
114 // set sensible defaults for a project wo parent
115 m_constraintStartTime = DateTime(QDate::currentDate());
116 m_constraintEndTime = m_constraintStartTime.addYears(2);
117 }
118 }
119
deref()120 void Project::deref()
121 {
122 --m_refCount;
123 Q_ASSERT(m_refCount >= 0);
124 if (m_refCount <= 0) {
125 emit aboutToBeDeleted();
126 deleteLater();
127 }
128 }
129
~Project()130 Project::~Project()
131 {
132 debugPlan<<"("<<this<<")";
133 disconnect();
134 for(Node *n : qAsConst(nodeIdDict)) {
135 n->blockChanged();
136 }
137 for (Resource *r : qAsConst(resourceIdDict)) {
138 r->blockChanged();
139 }
140 for (ResourceGroup *g : qAsConst(resourceGroupIdDict)) {
141 g->blockChanged();
142 }
143 delete m_standardWorktime;
144 while (!m_resourceGroups.isEmpty())
145 delete m_resourceGroups.takeFirst();
146 while (!m_calendars.isEmpty())
147 delete m_calendars.takeFirst();
148 while (!m_managers.isEmpty())
149 delete m_managers.takeFirst();
150
151 m_config = 0; //not mine, don't delete
152 }
153
type() const154 int Project::type() const { return Node::Type_Project; }
155
generateUniqueNodeIds()156 void Project::generateUniqueNodeIds()
157 {
158 foreach (Node *n, nodeIdDict) {
159 debugPlan<<n->name()<<"old"<<n->id();
160 QString uid = uniqueNodeId();
161 nodeIdDict.remove(n->id());
162 n->setId(uid);
163 nodeIdDict[ uid ] = n;
164 debugPlan<<n->name()<<"new"<<n->id();
165 }
166 }
167
generateUniqueIds()168 void Project::generateUniqueIds()
169 {
170 generateUniqueNodeIds();
171
172 foreach (ResourceGroup *g, resourceGroupIdDict) {
173 if (g->isShared()) {
174 continue;
175 }
176 resourceGroupIdDict.remove(g->id());
177 g->setId(uniqueResourceGroupId());
178 resourceGroupIdDict[ g->id() ] = g;
179 }
180 foreach (Resource *r, resourceIdDict) {
181 if (r->isShared()) {
182 continue;
183 }
184 resourceIdDict.remove(r->id());
185 r->setId(uniqueResourceId());
186 resourceIdDict[ r->id() ] = r;
187 }
188 foreach (Calendar *c, calendarIdDict) {
189 if (c->isShared()) {
190 continue;
191 }
192 calendarIdDict.remove(c->id());
193 c->setId(uniqueCalendarId());
194 calendarIdDict[ c->id() ] = c;
195 }
196 }
197
calculate(Schedule * schedule,const DateTime & dt)198 void Project::calculate(Schedule *schedule, const DateTime &dt)
199 {
200 if (schedule == 0) {
201 errorPlan << "Schedule == 0, cannot calculate";
202 return ;
203 }
204 m_currentSchedule = schedule;
205 calculate(dt);
206 }
207
calculate(const DateTime & dt)208 void Project::calculate(const DateTime &dt)
209 {
210 if (m_currentSchedule == 0) {
211 errorPlan << "No current schedule to calculate";
212 return ;
213 }
214 stopcalculation = false;
215 QLocale locale;
216 DateTime time = dt.isValid() ? dt : DateTime(QDateTime::currentDateTime());
217 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
218 Estimate::Use estType = (Estimate::Use) cs->type();
219 if (type() == Type_Project) {
220 cs->setPhaseName(0, i18n("Init"));
221 cs->logInfo(i18n("Schedule project from: %1", locale.toString(dt, QLocale::ShortFormat)), 0);
222 initiateCalculation(*cs);
223 initiateCalculationLists(*cs); // must be after initiateCalculation() !!
224 propagateEarliestStart(time);
225 // Calculate lateFinish from time. If a task has started, remainingEffort is used.
226 cs->setPhaseName(1, i18nc("Schedule project forward", "Forward"));
227 cs->logInfo(i18n("Calculate finish"), 1);
228 cs->lateFinish = calculateForward(estType);
229 cs->lateFinish = checkEndConstraints(cs->lateFinish);
230 propagateLatestFinish(cs->lateFinish);
231 // Calculate earlyFinish. If a task has started, remainingEffort is used.
232 cs->setPhaseName(2, i18nc("Schedule project backward","Backward"));
233 cs->logInfo(i18n("Calculate start"), 2);
234 calculateBackward(estType);
235 // Schedule. If a task has started, remainingEffort is used and appointments are copied from parent
236 cs->setPhaseName(3, i18n("Schedule"));
237 cs->logInfo(i18n("Schedule tasks forward"), 3);
238 cs->endTime = scheduleForward(cs->startTime, estType);
239 cs->logInfo(i18n("Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat)), 3);
240 if (cs->endTime > m_constraintEndTime) {
241 cs->logError(i18n("Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3);
242 } else if (cs->endTime == m_constraintEndTime) {
243 cs->logWarning(i18n("Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3);
244 } else {
245 cs->logInfo(i18n("Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3);
246 }
247 calcCriticalPath(false);
248 calcResourceOverbooked();
249 cs->notScheduled = false;
250 calcFreeFloat();
251 emit scheduleChanged(cs);
252 emit projectChanged();
253 } else if (type() == Type_Subproject) {
254 warnPlan << "Subprojects not implemented";
255 } else {
256 errorPlan << "Illegal project type: " << type();
257 }
258 }
259
calculate(ScheduleManager & sm)260 void Project::calculate(ScheduleManager &sm)
261 {
262 emit sigCalculationStarted(this, &sm);
263 sm.setScheduling(true);
264 m_progress = 0;
265 int nodes = 0;
266 foreach (Node *n, nodeIdDict) {
267 if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) {
268 nodes++;
269 }
270 }
271 int maxprogress = nodes * 3;
272 if (sm.recalculate()) {
273 emit maxProgress(maxprogress);
274 sm.setMaxProgress(maxprogress);
275 incProgress();
276 if (sm.parentManager()) {
277 sm.expected()->startTime = sm.parentManager()->expected()->startTime;
278 sm.expected()->earlyStart = sm.parentManager()->expected()->earlyStart;
279 }
280 incProgress();
281 calculate(sm.expected(), sm.recalculateFrom());
282 } else {
283 emit maxProgress(maxprogress);
284 sm.setMaxProgress(maxprogress);
285 calculate(sm.expected());
286 emit scheduleChanged(sm.expected());
287 setCurrentSchedule(sm.expected()->id());
288 }
289 emit sigProgress(maxprogress);
290 emit sigCalculationFinished(this, &sm);
291 emit scheduleManagerChanged(&sm);
292 emit projectCalculated(&sm);
293 emit projectChanged();
294 sm.setScheduling(false);
295 }
296
calculate(Schedule * schedule)297 void Project::calculate(Schedule *schedule)
298 {
299 if (schedule == 0) {
300 errorPlan << "Schedule == 0, cannot calculate";
301 return ;
302 }
303 m_currentSchedule = schedule;
304 calculate();
305 }
306
calculate()307 void Project::calculate()
308 {
309 if (m_currentSchedule == 0) {
310 errorPlan << "No current schedule to calculate";
311 return ;
312 }
313 stopcalculation = false;
314 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
315 bool backwards = false;
316 if (cs->manager()) {
317 backwards = cs->manager()->schedulingDirection();
318 }
319 QLocale locale;
320 Estimate::Use estType = (Estimate::Use) cs->type();
321 if (type() == Type_Project) {
322 QTime timer; timer.start();
323 initiateCalculation(*cs);
324 initiateCalculationLists(*cs); // must be after initiateCalculation() !!
325 if (! backwards) {
326 cs->setPhaseName(0, i18n("Init"));
327 cs->logInfo(i18n("Schedule project forward from: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 0);
328 cs->startTime = m_constraintStartTime;
329 cs->earlyStart = m_constraintStartTime;
330 // Calculate from start time
331 propagateEarliestStart(cs->earlyStart);
332 cs->setPhaseName(1, i18nc("Schedule project forward", "Forward"));
333 cs->logInfo(i18n("Calculate late finish"), 1);
334 cs->lateFinish = calculateForward(estType);
335 // cs->lateFinish = checkEndConstraints(cs->lateFinish);
336 cs->logInfo(i18n("Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat)), 1);
337 propagateLatestFinish(cs->lateFinish);
338 cs->setPhaseName(2, i18nc("Schedule project backward", "Backward"));
339 cs->logInfo(i18n("Calculate early start"), 2);
340 calculateBackward(estType);
341 cs->setPhaseName(3, i18n("Schedule"));
342 cs->logInfo(i18n("Schedule tasks forward"), 3);
343 cs->endTime = scheduleForward(cs->startTime, estType);
344 cs->duration = cs->endTime - cs->startTime;
345 cs->logInfo(i18n("Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat)), 3);
346 if (cs->endTime > m_constraintEndTime) {
347 cs->constraintError = true;
348 cs->logError(i18n("Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3);
349 } else if (cs->endTime == m_constraintEndTime) {
350 cs->logWarning(i18n("Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3);
351 } else {
352 cs->logInfo(i18n("Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 3);
353 }
354 calcCriticalPath(false);
355 } else {
356 cs->setPhaseName(0, i18n("Init"));
357 cs->logInfo(i18n("Schedule project backward from: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat)), 0);
358 // Calculate from end time
359 propagateLatestFinish(m_constraintEndTime);
360 cs->setPhaseName(1, i18nc("Schedule project backward", "Backward"));
361 cs->logInfo(i18n("Calculate early start"), 1);
362 cs->earlyStart = calculateBackward(estType);
363 // cs->earlyStart = checkStartConstraints(cs->earlyStart);
364 cs->logInfo(i18n("Early start calculated: %1", locale.toString(cs->earlyStart, QLocale::ShortFormat)), 1);
365 propagateEarliestStart(cs->earlyStart);
366 cs->setPhaseName(2, i18nc("Schedule project forward", "Forward"));
367 cs->logInfo(i18n("Calculate late finish"), 2);
368 cs->lateFinish = qMax(m_constraintEndTime, calculateForward(estType));
369 cs->logInfo(i18n("Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat)), 2);
370 cs->setPhaseName(3, i18n("Schedule"));
371 cs->logInfo(i18n("Schedule tasks backward"), 3);
372 cs->startTime = scheduleBackward(cs->lateFinish, estType);
373 cs->endTime = cs->startTime;
374 foreach (Node *n, allNodes()) {
375 if (n->type() == Type_Task || n->type() == Type_Milestone) {
376 DateTime e = n->endTime(cs->id());
377 if (cs->endTime < e) {
378 cs->endTime = e;
379 }
380 }
381 }
382 if (cs->endTime > m_constraintEndTime) {
383 cs->constraintError = true;
384 cs->logError(i18n("Failed to finish project within target time"), 3);
385 }
386 cs->duration = cs->endTime - cs->startTime;
387 cs->logInfo(i18n("Scheduled start: %1, target time: %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3);
388 if (cs->startTime < m_constraintStartTime) {
389 cs->constraintError = true;
390 cs->logError(i18n("Must start project early in order to finish in time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3);
391 } else if (cs->startTime == m_constraintStartTime) {
392 cs->logWarning(i18n("Start project exactly on time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3);
393 } else {
394 cs->logInfo(i18n("Can start project later than time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat)), 3);
395 }
396 calcCriticalPath(true);
397 }
398 cs->logInfo(i18n("Calculation took: %1", KFormat().formatDuration(timer.elapsed())));
399 // TODO: fix this uncertainty, manager should *always* be available
400 if (cs->manager()) {
401 finishCalculation(*(cs->manager()));
402 }
403 } else if (type() == Type_Subproject) {
404 warnPlan << "Subprojects not implemented";
405 } else {
406 errorPlan << "Illegal project type: " << type();
407 }
408 }
409
finishCalculation(ScheduleManager & sm)410 void Project::finishCalculation(ScheduleManager &sm)
411 {
412 MainSchedule *cs = sm.expected();
413 if (nodeIdDict.count() > 1) {
414 // calculate project duration
415 cs->startTime = m_constraintEndTime;
416 cs->endTime = m_constraintStartTime;
417 for (const Node *n : qAsConst(nodeIdDict)) {
418 cs->startTime = qMin(cs->startTime, n->startTime(cs->id()));
419 cs->endTime = qMax(cs->endTime, n->endTime(cs->id()));
420 }
421 cs->duration = cs->endTime - cs->startTime;
422 }
423
424 calcCriticalPath(false);
425 calcResourceOverbooked();
426 cs->notScheduled = false;
427 calcFreeFloat();
428 emit scheduleChanged(cs);
429 emit projectChanged();
430 debugPlan<<cs->startTime<<cs->endTime<<"-------------------------";
431 }
432
setProgress(int progress,ScheduleManager * sm)433 void Project::setProgress(int progress, ScheduleManager *sm)
434 {
435 m_progress = progress;
436 if (sm) {
437 sm->setProgress(progress);
438 }
439 emit sigProgress(progress);
440 }
441
setMaxProgress(int max,ScheduleManager * sm)442 void Project::setMaxProgress(int max, ScheduleManager *sm)
443 {
444 if (sm) {
445 sm->setMaxProgress(max);
446 }
447 emitMaxProgress(max);
448 }
449
incProgress()450 void Project::incProgress()
451 {
452 m_progress += 1;
453 emit sigProgress(m_progress);
454 }
455
emitMaxProgress(int value)456 void Project::emitMaxProgress(int value)
457 {
458 emit maxProgress(value);
459 }
460
calcCriticalPath(bool fromEnd)461 bool Project::calcCriticalPath(bool fromEnd)
462 {
463 //debugPlan;
464 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
465 if (cs == 0) {
466 return false;
467 }
468 if (fromEnd) {
469 QListIterator<Node*> startnodes = cs->startNodes();
470 while (startnodes.hasNext()) {
471 startnodes.next() ->calcCriticalPath(fromEnd);
472 }
473 } else {
474 QListIterator<Node*> endnodes = cs->endNodes();
475 while (endnodes.hasNext()) {
476 endnodes.next() ->calcCriticalPath(fromEnd);
477 }
478 }
479 calcCriticalPathList(cs);
480 return false;
481 }
482
calcCriticalPathList(MainSchedule * cs)483 void Project::calcCriticalPathList(MainSchedule *cs)
484 {
485 //debugPlan<<m_name<<", "<<cs->name();
486 cs->clearCriticalPathList();
487 foreach (Node *n, allNodes()) {
488 if (n->numDependParentNodes() == 0 && n->inCriticalPath(cs->id())) {
489 cs->addCriticalPath();
490 cs->addCriticalPathNode(n);
491 calcCriticalPathList(cs, n);
492 }
493 }
494 cs->criticalPathListCached = true;
495 //debugPlan<<*(criticalPathList(cs->id()));
496 }
497
calcCriticalPathList(MainSchedule * cs,Node * node)498 void Project::calcCriticalPathList(MainSchedule *cs, Node *node)
499 {
500 //debugPlan<<node->name()<<", "<<cs->id();
501 bool newPath = false;
502 QList<Node*> lst = *(cs->currentCriticalPath());
503 foreach (Relation *r, node->dependChildNodes()) {
504 if (r->child()->inCriticalPath(cs->id())) {
505 if (newPath) {
506 cs->addCriticalPath(&lst);
507 //debugPlan<<node->name()<<" new path";
508 }
509 cs->addCriticalPathNode(r->child());
510 calcCriticalPathList(cs, r->child());
511 newPath = true;
512 }
513 }
514 }
515
criticalPathList(long id)516 const QList< QList<Node*> > *Project::criticalPathList(long id)
517 {
518 Schedule *s = schedule(id);
519 if (s == 0) {
520 //debugPlan<<"No schedule with id="<<id;
521 return 0;
522 }
523 MainSchedule *ms = static_cast<MainSchedule*>(s);
524 if (! ms->criticalPathListCached) {
525 initiateCalculationLists(*ms);
526 calcCriticalPathList(ms);
527 }
528 return ms->criticalPathList();
529 }
530
criticalPath(long id,int index)531 QList<Node*> Project::criticalPath(long id, int index)
532 {
533 Schedule *s = schedule(id);
534 if (s == 0) {
535 //debugPlan<<"No schedule with id="<<id;
536 return QList<Node*>();
537 }
538 MainSchedule *ms = static_cast<MainSchedule*>(s);
539 if (! ms->criticalPathListCached) {
540 initiateCalculationLists(*ms);
541 calcCriticalPathList(ms);
542 }
543 return ms->criticalPath(index);
544 }
545
startTime(long id) const546 DateTime Project::startTime(long id) const
547 {
548 Schedule *s = schedule(id);
549 return s ? s->startTime : m_constraintStartTime;
550 }
551
endTime(long id) const552 DateTime Project::endTime(long id) const
553 {
554 Schedule *s = schedule(id);
555 return s ? s->endTime : m_constraintEndTime;
556 }
557
duration(long id) const558 Duration Project::duration(long id) const
559 {
560 Schedule *s = schedule(id);
561 return s ? s->duration : Duration::zeroDuration;
562 }
563
getRandomDuration()564 Duration *Project::getRandomDuration()
565 {
566 return 0L;
567 }
568
checkStartConstraints(const DateTime & dt) const569 DateTime Project::checkStartConstraints(const DateTime &dt) const
570 {
571 DateTime t = dt;
572 foreach (Node *n, nodeIdDict) {
573 if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) {
574 switch (n->constraint()) {
575 case Node::FixedInterval:
576 case Node::StartNotEarlier:
577 case Node::MustStartOn:
578 t = qMin(t, qMax(n->constraintStartTime(), m_constraintStartTime));
579 break;
580 default: break;
581 }
582 }
583 }
584 return t;
585 }
586
checkEndConstraints(const DateTime & dt) const587 DateTime Project::checkEndConstraints(const DateTime &dt) const
588 {
589 DateTime t = dt;
590 foreach (Node *n, nodeIdDict) {
591 if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) {
592 switch (n->constraint()) {
593 case Node::FixedInterval:
594 case Node::FinishNotLater:
595 case Node::MustFinishOn:
596 t = qMax(t, qMin(n->constraintEndTime(), m_constraintEndTime));
597 break;
598 default: break;
599 }
600 }
601 }
602 return t;
603 }
604
605 #ifndef PLAN_NLOGDEBUG
checkParent(Node * n,const QList<Node * > & list,QList<Relation * > & checked)606 bool Project::checkParent(Node *n, const QList<Node*> &list, QList<Relation*> &checked)
607 {
608 if (n->isStartNode()) {
609 debugPlan<<n<<"start node"<<list;
610 return true;
611 }
612 debugPlan<<"Check:"<<n<<":"<<checked.count()<<":"<<list;
613 if (list.contains(n)) {
614 debugPlan<<"Failed:"<<n<<":"<<list;
615 return false;
616 }
617 QList<Node*> lst = list;
618 lst << n;
619 foreach (Relation *r, n->dependParentNodes()) {
620 if (checked.contains(r)) {
621 debugPlan<<"Depend:"<<n<<":"<<r->parent()<<": checked";
622 continue;
623 }
624 checked << r;
625 if (! checkParent(r->parent(), lst, checked)) {
626 return false;
627 }
628 }
629 Task *t = static_cast<Task*>(n);
630 foreach (Relation *r, t->parentProxyRelations()) {
631 if (checked.contains(r)) {
632 debugPlan<<"Depend:"<<n<<":"<<r->parent()<<": checked";
633 continue;
634 }
635 checked << r;
636 debugPlan<<"Proxy:"<<n<<":"<<r->parent()<<":"<<lst;
637 if (! checkParent(r->parent(), lst, checked)) {
638 return false;
639 }
640 }
641 return true;
642 }
643
checkChildren(Node * n,const QList<Node * > & list,QList<Relation * > & checked)644 bool Project::checkChildren(Node *n, const QList<Node*> &list, QList<Relation*> &checked)
645 {
646 if (n->isEndNode()) {
647 debugPlan<<n<<"end node"<<list;
648 return true;
649 }
650 debugPlan<<"Check:"<<n<<":"<<checked.count()<<":"<<list;
651 if (list.contains(n)) {
652 debugPlan<<"Failed:"<<n<<":"<<list;
653 return false;
654 }
655 QList<Node*> lst = list;
656 lst << n;
657 foreach (Relation *r, n->dependChildNodes()) {
658 if (checked.contains(r)) {
659 debugPlan<<"Depend:"<<n<<":"<<r->parent()<<": checked";
660 continue;
661 }
662 checked << r;
663 if (! checkChildren(r->child(), lst, checked)) {
664 return false;
665 }
666 }
667 Task *t = static_cast<Task*>(n);
668 foreach (Relation *r, t->childProxyRelations()) {
669 if (checked.contains(r)) {
670 debugPlan<<"Depend:"<<n<<":"<<r->parent()<<": checked";
671 continue;
672 }
673 debugPlan<<"Proxy:"<<n<<":"<<r->parent()<<":"<<lst;
674 checked << r;
675 if (! checkChildren(r->child(), lst, checked)) {
676 return false;
677 }
678 }
679 return true;
680 }
681 #endif
tasksForward()682 void Project::tasksForward()
683 {
684 m_hardConstraints.clear();
685 m_softConstraints.clear();
686 m_terminalNodes.clear();
687 // Do these in reverse order to get tasks with same prio in wbs order
688 const QList<Task*> tasks = allTasks();
689 for (int i = tasks.count() -1; i >= 0; --i) {
690 Task *t = tasks.at(i);
691 switch (t->constraint()) {
692 case Node::MustStartOn:
693 case Node::MustFinishOn:
694 case Node::FixedInterval:
695 m_hardConstraints.prepend(t);
696 break;
697 case Node::StartNotEarlier:
698 case Node::FinishNotLater:
699 m_softConstraints.prepend(t);
700 break;
701 default:
702 if (t->isEndNode()) {
703 m_terminalNodes.insert(-t->priority(), t);
704 }
705 break;
706 }
707 }
708 #ifndef PLAN_NLOGDEBUG
709 debugPlan<<"End nodes:"<<m_terminalNodes;
710 foreach (Node* n, m_terminalNodes) {
711 QList<Node*> lst;
712 QList<Relation*> rel;
713 Q_ASSERT(checkParent(n, lst, rel)); Q_UNUSED(n);
714 }
715 #endif
716 }
717
tasksBackward()718 void Project::tasksBackward()
719 {
720 m_hardConstraints.clear();
721 m_softConstraints.clear();
722 m_terminalNodes.clear();
723 // Do these in reverse order to get tasks with same prio in wbs order
724 const QList<Task*> tasks = allTasks();
725 for (int i = tasks.count() -1; i >= 0; --i) {
726 Task *t = tasks.at(i);
727 switch (t->constraint()) {
728 case Node::MustStartOn:
729 case Node::MustFinishOn:
730 case Node::FixedInterval:
731 m_hardConstraints.prepend(t);
732 break;
733 case Node::StartNotEarlier:
734 case Node::FinishNotLater:
735 m_softConstraints.prepend(t);
736 break;
737 default:
738 if (t->isStartNode()) {
739 m_terminalNodes.insert(-t->priority(), t);
740 }
741 break;
742 }
743 }
744 #ifndef PLAN_NLOGDEBUG
745 debugPlan<<"Start nodes:"<<m_terminalNodes;
746 foreach (Node* n, m_terminalNodes) {
747 QList<Node*> lst;
748 QList<Relation*> rel;
749 Q_ASSERT(checkChildren(n, lst, rel)); Q_UNUSED(n);
750 }
751 #endif
752 }
753
calculateForward(int use)754 DateTime Project::calculateForward(int use)
755 {
756 //debugPlan<<m_name;
757 DateTime finish;
758 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
759 if (cs == 0) {
760 return finish;
761 }
762 if (type() == Node::Type_Project) {
763 QTime timer;
764 timer.start();
765 cs->logInfo(i18n("Start calculating forward"));
766 m_visitedForward = true;
767 if (! m_visitedBackward) {
768 // setup tasks
769 tasksForward();
770 // Do all hard constrained first
771 foreach (Node *n, m_hardConstraints) {
772 cs->logDebug("Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString());
773 DateTime time = n->calculateEarlyFinish(use); // do not do predeccessors
774 if (time > finish) {
775 finish = time;
776 }
777 }
778 // do the predeccessors
779 foreach (Node *n, m_hardConstraints) {
780 cs->logDebug("Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString());
781 DateTime time = n->calculateForward(use);
782 if (time > finish) {
783 finish = time;
784 }
785 }
786 // now try to schedule soft constrained *with* predeccessors
787 foreach (Node *n, m_softConstraints) {
788 cs->logDebug("Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString());
789 DateTime time = n->calculateForward(use);
790 if (time > finish) {
791 finish = time;
792 }
793 }
794 // and then the rest using the end nodes to calculate everything (remaining)
795 foreach (Task *n, m_terminalNodes) {
796 cs->logDebug("Calculate using end task:" + n->name() + " : " + n->constraintToString());
797 DateTime time = n->calculateForward(use);
798 if (time > finish) {
799 finish = time;
800 }
801 }
802 } else {
803 // tasks have been calculated backwards in this order
804 foreach (Node *n, cs->backwardNodes()) {
805 DateTime time = n->calculateForward(use);
806 if (time > finish) {
807 finish = time;
808 }
809 }
810 }
811 cs->logInfo(i18n("Finished calculating forward: %1 ms", timer.elapsed()));
812 } else {
813 //TODO: subproject
814 }
815 return finish;
816 }
817
calculateBackward(int use)818 DateTime Project::calculateBackward(int use)
819 {
820 //debugPlan<<m_name;
821 DateTime start;
822 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
823 if (cs == 0) {
824 return start;
825 }
826 if (type() == Node::Type_Project) {
827 QTime timer;
828 timer.start();
829 cs->logInfo(i18n("Start calculating backward"));
830 m_visitedBackward = true;
831 if (! m_visitedForward) {
832 // setup tasks
833 tasksBackward();
834 // Do all hard constrained first
835 foreach (Task *n, m_hardConstraints) {
836 cs->logDebug("Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString());
837 DateTime time = n->calculateLateStart(use); // do not do predeccessors
838 if (! start.isValid() || time < start) {
839 start = time;
840 }
841 }
842 // then do the predeccessors
843 foreach (Task *n, m_hardConstraints) {
844 cs->logDebug("Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString());
845 DateTime time = n->calculateBackward(use);
846 if (! start.isValid() || time < start) {
847 start = time;
848 }
849 }
850 // now try to schedule soft constrained *with* predeccessors
851 foreach (Task *n, m_softConstraints) {
852 cs->logDebug("Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString());
853 DateTime time = n->calculateBackward(use);
854 if (! start.isValid() || time < start) {
855 start = time;
856 }
857 }
858 // and then the rest using the start nodes to calculate everything (remaining)
859 foreach (Task *n, m_terminalNodes) {
860 cs->logDebug("Calculate using start task:" + n->name() + " : " + n->constraintToString());
861 DateTime time = n->calculateBackward(use);
862 if (! start.isValid() || time < start) {
863 start = time;
864 }
865 }
866 } else {
867 // tasks have been calculated forwards in this order
868 foreach (Node *n, cs->forwardNodes()) {
869 DateTime time = n->calculateBackward(use);
870 if (! start.isValid() || time < start) {
871 start = time;
872 }
873 }
874 }
875 cs->logInfo(i18n("Finished calculating backward: %1 ms", timer.elapsed()));
876 } else {
877 //TODO: subproject
878 }
879 return start;
880 }
881
scheduleForward(const DateTime & earliest,int use)882 DateTime Project::scheduleForward(const DateTime &earliest, int use)
883 {
884 DateTime end;
885 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
886 if (cs == 0 || stopcalculation) {
887 return DateTime();
888 }
889 QTime timer;
890 timer.start();
891 cs->logInfo(i18n("Start scheduling forward"));
892 resetVisited();
893 // Schedule in the same order as calculated forward
894 // Do all hard constrained first
895 foreach (Node *n, m_hardConstraints) {
896 cs->logDebug("Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString());
897 DateTime time = n->scheduleFromStartTime(use); // do not do predeccessors
898 if (time > end) {
899 end = time;
900 }
901 }
902 foreach (Node *n, cs->forwardNodes()) {
903 cs->logDebug("Schedule task:" + n->name() + " : " + n->constraintToString());
904 DateTime time = n->scheduleForward(earliest, use);
905 if (time > end) {
906 end = time;
907 }
908 }
909 // Fix summarytasks
910 adjustSummarytask();
911 cs->logInfo(i18n("Finished scheduling forward: %1 ms", timer.elapsed()));
912 foreach (Node *n, allNodes()) {
913 if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) {
914 Q_ASSERT(n->isScheduled());
915 }
916 }
917
918 return end;
919 }
920
scheduleBackward(const DateTime & latest,int use)921 DateTime Project::scheduleBackward(const DateTime &latest, int use)
922 {
923 DateTime start;
924 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
925 if (cs == 0 || stopcalculation) {
926 return start;
927 }
928 QTime timer;
929 timer.start();
930 cs->logInfo(i18n("Start scheduling backward"));
931 resetVisited();
932 // Schedule in the same order as calculated backward
933 // Do all hard constrained first
934 foreach (Node *n, m_hardConstraints) {
935 cs->logDebug("Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString());
936 DateTime time = n->scheduleFromEndTime(use); // do not do predeccessors
937 if (! start.isValid() || time < start) {
938 start = time;
939 }
940 }
941 foreach (Node *n, cs->backwardNodes()) {
942 cs->logDebug("Schedule task:" + n->name() + " : " + n->constraintToString());
943 DateTime time = n->scheduleBackward(latest, use);
944 if (! start.isValid() || time < start) {
945 start = time;
946 }
947 }
948 // Fix summarytasks
949 adjustSummarytask();
950 cs->logInfo(i18n("Finished scheduling backward: %1 ms", timer.elapsed()));
951 foreach (Node *n, allNodes()) {
952 if (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) {
953 Q_ASSERT(n->isScheduled());
954 }
955 }
956 return start;
957 }
958
adjustSummarytask()959 void Project::adjustSummarytask()
960 {
961 MainSchedule *cs = static_cast<MainSchedule*>(m_currentSchedule);
962 if (cs == 0 || stopcalculation) {
963 return;
964 }
965 QListIterator<Node*> it(cs->summaryTasks());
966 while (it.hasNext()) {
967 it.next() ->adjustSummarytask();
968 }
969 }
970
initiateCalculation(MainSchedule & sch)971 void Project::initiateCalculation(MainSchedule &sch)
972 {
973 //debugPlan<<m_name;
974 // clear all resource appointments
975 m_visitedForward = false;
976 m_visitedBackward = false;
977 QListIterator<ResourceGroup*> git(m_resourceGroups);
978 while (git.hasNext()) {
979 git.next() ->initiateCalculation(sch);
980 }
981 Node::initiateCalculation(sch);
982 }
983
initiateCalculationLists(MainSchedule & sch)984 void Project::initiateCalculationLists(MainSchedule &sch)
985 {
986 //debugPlan<<m_name;
987 sch.clearNodes();
988 if (type() == Node::Type_Project) {
989 QListIterator<Node*> it = childNodeIterator();
990 while (it.hasNext()) {
991 it.next() ->initiateCalculationLists(sch);
992 }
993 } else {
994 //TODO: subproject
995 }
996 }
997
load(KoXmlElement & element,XMLLoaderObject & status)998 bool Project::load(KoXmlElement &element, XMLLoaderObject &status)
999 {
1000 //debugPlan<<"--->";
1001 m_useSharedResources = false; // default should off in case old project
1002 // load locale first
1003 KoXmlNode n = element.firstChild();
1004 for (; ! n.isNull(); n = n.nextSibling()) {
1005 if (! n.isElement()) {
1006 continue;
1007 }
1008 KoXmlElement e = n.toElement();
1009 if (e.tagName() == "locale") {
1010 Locale *l = locale();
1011 l->setCurrencySymbol(e.attribute("currency-symbol", ""));
1012
1013 if (e.hasAttribute("currency-digits")) {
1014 l->setMonetaryDecimalPlaces(e.attribute("currency-digits").toInt());
1015 }
1016 QLocale::Language language = QLocale::AnyLanguage;
1017 QLocale::Country country = QLocale::AnyCountry;
1018 if (e.hasAttribute("language")) {
1019 language = static_cast<QLocale::Language>(e.attribute("language").toInt());
1020 }
1021 if (e.hasAttribute("country")) {
1022 country = static_cast<QLocale::Country>(e.attribute("country").toInt());
1023 }
1024 l->setCurrencyLocale(language, country);
1025 } else if (e.tagName() == "shared-resources") {
1026 m_useSharedResources = e.attribute("use", "0").toInt();
1027 m_sharedResourcesFile = e.attribute("file");
1028 m_sharedProjectsUrl = QUrl(e.attribute("projects-url"));
1029 m_loadProjectsAtStartup = (bool)e.attribute("projects-loadatstartup", "0").toInt();
1030 } else if (e.tagName() == QLatin1String("documents")) {
1031 m_documents.load(e, status);
1032 } else if (e.tagName() == QLatin1String("workpackageinfo")) {
1033 if (e.hasAttribute("check-for-workpackages")) {
1034 m_workPackageInfo.checkForWorkPackages = e.attribute("check-for-workpackages").toInt();
1035 }
1036 if (e.hasAttribute("retrieve-url")) {
1037 m_workPackageInfo.retrieveUrl = QUrl(e.attribute("retrieve-url"));
1038 }
1039 if (e.hasAttribute("delete-after-retrieval")) {
1040 m_workPackageInfo.deleteAfterRetrieval = e.attribute("delete-after-retrieval").toInt();
1041 }
1042 if (e.hasAttribute("archive-after-retrieval")) {
1043 m_workPackageInfo.archiveAfterRetrieval = e.attribute("archive-after-retrieval").toInt();
1044 }
1045 if (e.hasAttribute("archive-url")) {
1046 m_workPackageInfo.archiveUrl = QUrl(e.attribute("archive-url"));
1047 }
1048 if (e.hasAttribute("publish-url")) {
1049 m_workPackageInfo.publishUrl = QUrl(e.attribute("publish-url"));
1050 }
1051 } else if (e.tagName() == QLatin1String("task-modules")) {
1052 m_useLocalTaskModules = false;
1053 QList<QUrl> urls;
1054 for (KoXmlNode child = e.firstChild(); !child.isNull(); child = child.nextSibling()) {
1055 KoXmlElement path = child.toElement();
1056 if (path.isNull()) {
1057 continue;
1058 }
1059 QString s = path.attribute("url");
1060 if (!s.isEmpty()) {
1061 QUrl url = QUrl::fromUserInput(s);
1062 if (!urls.contains(url)) {
1063 urls << url;
1064 }
1065 }
1066 }
1067 m_taskModules = urls;
1068 // If set adds local path to taskModules()
1069 setUseLocalTaskModules((bool)e.attribute("use-local-task-modules").toInt());
1070 }
1071 }
1072 QList<Calendar*> cals;
1073 QString s;
1074 bool ok = false;
1075 setName(element.attribute("name"));
1076 removeId(m_id);
1077 m_id = element.attribute("id");
1078 registerNodeId(this);
1079 m_priority = element.attribute(QStringLiteral("priority"), "0").toInt();
1080 m_leader = element.attribute("leader");
1081 m_description = element.attribute("description");
1082 QTimeZone tz(element.attribute("timezone").toLatin1());
1083 if (tz.isValid()) {
1084 m_timeZone = tz;
1085 } else warnPlan<<"No timezone specified, using default (local)";
1086 status.setProjectTimeZone(m_timeZone);
1087
1088 // Allow for both numeric and text
1089 s = element.attribute("scheduling", "0");
1090 m_constraint = (Node::ConstraintType) s.toInt(&ok);
1091 if (!ok)
1092 setConstraint(s);
1093 if (m_constraint != Node::MustStartOn &&
1094 m_constraint != Node::MustFinishOn) {
1095 errorPlan << "Illegal constraint: " << constraintToString();
1096 setConstraint(Node::MustStartOn);
1097 }
1098 s = element.attribute("start-time");
1099 if (!s.isEmpty())
1100 m_constraintStartTime = DateTime::fromString(s, m_timeZone);
1101 s = element.attribute("end-time");
1102 if (!s.isEmpty())
1103 m_constraintEndTime = DateTime::fromString(s, m_timeZone);
1104
1105 status.setProgress(10);
1106
1107 // Load the project children
1108 // Do calendars first, they only reference other calendars
1109 //debugPlan<<"Calendars--->";
1110 n = element.firstChild();
1111 for (; ! n.isNull(); n = n.nextSibling()) {
1112 if (! n.isElement()) {
1113 continue;
1114 }
1115 KoXmlElement e = n.toElement();
1116 if (e.tagName() == "calendar") {
1117 // Load the calendar.
1118 // Referenced by resources
1119 Calendar * child = new Calendar();
1120 child->setProject(this);
1121 if (child->load(e, status)) {
1122 cals.append(child); // temporary, reorder later
1123 } else {
1124 // TODO: Complain about this
1125 errorPlan << "Failed to load calendar";
1126 delete child;
1127 }
1128 } else if (e.tagName() == "standard-worktime") {
1129 // Load standard worktime
1130 StandardWorktime * child = new StandardWorktime();
1131 if (child->load(e, status)) {
1132 setStandardWorktime(child);
1133 } else {
1134 errorPlan << "Failed to load standard worktime";
1135 delete child;
1136 }
1137 }
1138 }
1139 // calendars references calendars in arbitrary saved order
1140 bool added = false;
1141 do {
1142 added = false;
1143 QList<Calendar*> lst;
1144 while (!cals.isEmpty()) {
1145 Calendar *c = cals.takeFirst();
1146 c->m_blockversion = true;
1147 if (c->parentId().isEmpty()) {
1148 addCalendar(c, status.baseCalendar()); // handle pre 0.6 version
1149 added = true;
1150 //debugPlan<<"added to project:"<<c->name();
1151 } else {
1152 Calendar *par = calendar(c->parentId());
1153 if (par) {
1154 par->m_blockversion = true;
1155 addCalendar(c, par);
1156 added = true;
1157 //debugPlan<<"added:"<<c->name()<<" to parent:"<<par->name();
1158 par->m_blockversion = false;
1159 } else {
1160 lst.append(c); // treat later
1161 //debugPlan<<"treat later:"<<c->name();
1162 }
1163 }
1164 c->m_blockversion = false;
1165 }
1166 cals = lst;
1167 } while (added);
1168 if (! cals.isEmpty()) {
1169 errorPlan<<"All calendars not saved!";
1170 }
1171 //debugPlan<<"Calendars<---";
1172
1173 status.setProgress(15);
1174
1175 // Resource groups and resources, can reference calendars
1176 n = element.firstChild();
1177 for (; ! n.isNull(); n = n.nextSibling()) {
1178 if (! n.isElement()) {
1179 continue;
1180 }
1181 KoXmlElement e = n.toElement();
1182 if (e.tagName() == "resource-group") {
1183 // Load the resources
1184 // References calendars
1185 ResourceGroup * child = new ResourceGroup();
1186 if (child->load(e, status)) {
1187 addResourceGroup(child);
1188 } else {
1189 // TODO: Complain about this
1190 delete child;
1191 }
1192 }
1193 }
1194
1195 status.setProgress(20);
1196
1197 // The main stuff
1198 n = element.firstChild();
1199 for (; ! n.isNull(); n = n.nextSibling()) {
1200 if (! n.isElement()) {
1201 continue;
1202 }
1203 KoXmlElement e = n.toElement();
1204 if (e.tagName() == "project") {
1205 //debugPlan<<"Sub project--->";
1206 /* // Load the subproject
1207 Project * child = new Project(this);
1208 if (child->load(e)) {
1209 if (!addTask(child, this)) {
1210 delete child; // TODO: Complain about this
1211 }
1212 } else {
1213 // TODO: Complain about this
1214 delete child;
1215 }*/
1216 } else if (e.tagName() == "task") {
1217 //debugPlan<<"Task--->";
1218 // Load the task (and resourcerequests).
1219 // Depends on resources already loaded
1220 Task * child = new Task(this);
1221 if (child->load(e, status)) {
1222 if (!addTask(child, this)) {
1223 delete child; // TODO: Complain about this
1224 }
1225 } else {
1226 // TODO: Complain about this
1227 delete child;
1228 }
1229 }
1230 }
1231
1232 status.setProgress(70);
1233
1234 // These go last
1235 n = element.firstChild();
1236 for (; ! n.isNull(); n = n.nextSibling()) {
1237 //debugPlan<<n.isElement();
1238 if (! n.isElement()) {
1239 continue;
1240 }
1241 KoXmlElement e = n.toElement();
1242 if (e.tagName() == "accounts") {
1243 //debugPlan<<"Accounts--->";
1244 // Load accounts
1245 // References tasks
1246 if (!m_accounts.load(e, *this)) {
1247 errorPlan << "Failed to load accounts";
1248 }
1249 } else if (e.tagName() == "relation") {
1250 //debugPlan<<"Relation--->";
1251 // Load the relation
1252 // References tasks
1253 Relation * child = new Relation();
1254 if (!child->load(e, *this)) {
1255 // TODO: Complain about this
1256 errorPlan << "Failed to load relation";
1257 delete child;
1258 }
1259 //debugPlan<<"Relation<---";
1260 } else if (e.tagName() == "schedules") {
1261 //debugPlan<<"Project schedules & task appointments--->";
1262 // References tasks and resources
1263 KoXmlNode sn = e.firstChild();
1264 for (; ! sn.isNull(); sn = sn.nextSibling()) {
1265 if (! sn.isElement()) {
1266 continue;
1267 }
1268 KoXmlElement el = sn.toElement();
1269 //debugPlan<<el.tagName()<<" Version="<<status.version();
1270 ScheduleManager *sm = 0;
1271 bool add = false;
1272 if (status.version() <= "0.5") {
1273 if (el.tagName() == "schedule") {
1274 sm = findScheduleManagerByName(el.attribute("name"));
1275 if (sm == 0) {
1276 sm = new ScheduleManager(*this, el.attribute("name"));
1277 add = true;
1278 }
1279 }
1280 } else if (el.tagName() == "plan") {
1281 sm = new ScheduleManager(*this);
1282 add = true;
1283 }
1284 if (sm) {
1285 debugPlan<<"load schedule manager";
1286 if (sm->loadXML(el, status)) {
1287 if (add)
1288 addScheduleManager(sm);
1289 } else {
1290 errorPlan << "Failed to load schedule manager";
1291 delete sm;
1292 }
1293 } else {
1294 debugPlan<<"No schedule manager ?!";
1295 }
1296 }
1297 //debugPlan<<"Node schedules<---";
1298 } else if (e.tagName() == "resource-teams") {
1299 //debugPlan<<"Resource teams--->";
1300 // References other resources
1301 KoXmlNode tn = e.firstChild();
1302 for (; ! tn.isNull(); tn = tn.nextSibling()) {
1303 if (! tn.isElement()) {
1304 continue;
1305 }
1306 KoXmlElement el = tn.toElement();
1307 if (el.tagName() == "team") {
1308 Resource *r = findResource(el.attribute("team-id"));
1309 Resource *tm = findResource(el.attribute("member-id"));
1310 if (r == 0 || tm == 0) {
1311 errorPlan<<"resource-teams: cannot find resources";
1312 continue;
1313 }
1314 if (r == tm) {
1315 errorPlan<<"resource-teams: a team cannot be a member of itself";
1316 continue;
1317 }
1318 r->addTeamMemberId(tm->id());
1319 } else {
1320 errorPlan<<"resource-teams: unhandled tag"<<el.tagName();
1321 }
1322 }
1323 //debugPlan<<"Resource teams<---";
1324 } else if (e.tagName() == "wbs-definition") {
1325 m_wbsDefinition.loadXML(e, status);
1326 } else if (e.tagName() == "locale") {
1327 // handled earlier
1328 } else if (e.tagName() == "resource-group") {
1329 // handled earlier
1330 } else if (e.tagName() == "calendar") {
1331 // handled earlier
1332 } else if (e.tagName() == "standard-worktime") {
1333 // handled earlier
1334 } else if (e.tagName() == "project") {
1335 // handled earlier
1336 } else if (e.tagName() == "task") {
1337 // handled earlier
1338 } else if (e.tagName() == "shared-resources") {
1339 // handled earlier
1340 } else if (e.tagName() == "documents") {
1341 // handled earlier
1342 } else if (e.tagName() == "workpackageinfo") {
1343 // handled earlier
1344 } else if (e.tagName() == "task-modules") {
1345 // handled earlier
1346 } else {
1347 warnPlan<<"Unhandled tag:"<<e.tagName();
1348 }
1349 }
1350 //debugPlan<<"<---";
1351
1352 status.setProgress(90);
1353
1354 return true;
1355 }
1356
save(QDomElement & element,const XmlSaveContext & context) const1357 void Project::save(QDomElement &element, const XmlSaveContext &context) const
1358 {
1359 QDomElement me = element.ownerDocument().createElement("project");
1360 element.appendChild(me);
1361
1362 me.setAttribute("name", m_name);
1363 me.setAttribute("leader", m_leader);
1364 me.setAttribute("id", m_id);
1365 me.setAttribute("priority", QString::number(m_priority));
1366 me.setAttribute("description", m_description);
1367 me.setAttribute("timezone", m_timeZone.isValid() ? QString::fromLatin1(m_timeZone.id()) : QString());
1368
1369 me.setAttribute("scheduling", constraintToString());
1370 me.setAttribute("start-time", m_constraintStartTime.toString(Qt::ISODate));
1371 me.setAttribute("end-time", m_constraintEndTime.toString(Qt::ISODate));
1372
1373 m_wbsDefinition.saveXML(me);
1374
1375 QDomElement loc = me.ownerDocument().createElement("locale");
1376 me.appendChild(loc);
1377 const Locale *l = locale();
1378 loc.setAttribute("currency-symbol", l->currencySymbol());
1379 loc.setAttribute("currency-digits", l->monetaryDecimalPlaces());
1380 loc.setAttribute("language", l->currencyLanguage());
1381 loc.setAttribute("country", l->currencyCountry());
1382
1383 QDomElement share = me.ownerDocument().createElement("shared-resources");
1384 me.appendChild(share);
1385 share.setAttribute("use", m_useSharedResources);
1386 share.setAttribute("file", m_sharedResourcesFile);
1387 share.setAttribute("projects-url", QString(m_sharedProjectsUrl.toEncoded()));
1388 share.setAttribute("projects-loadatstartup", m_loadProjectsAtStartup);
1389
1390 QDomElement wpi = me.ownerDocument().createElement("workpackageinfo");
1391 me.appendChild(wpi);
1392 wpi.setAttribute("check-for-workpackages", m_workPackageInfo.checkForWorkPackages);
1393 wpi.setAttribute("retrieve-url", m_workPackageInfo.retrieveUrl.toString(QUrl::None));
1394 wpi.setAttribute("delete-after-retrieval", m_workPackageInfo.deleteAfterRetrieval);
1395 wpi.setAttribute("archive-after-retrieval", m_workPackageInfo.archiveAfterRetrieval);
1396 wpi.setAttribute("archive-url", m_workPackageInfo.archiveUrl.toString(QUrl::None));
1397 wpi.setAttribute("publish-url", m_workPackageInfo.publishUrl.toString(QUrl::None));
1398
1399 QDomElement tm = me.ownerDocument().createElement("task-modules");
1400 me.appendChild(tm);
1401 tm.setAttribute("use-local-task-modules", m_useLocalTaskModules);
1402 for (const QUrl &url : taskModules(false/*no local*/)) {
1403 QDomElement e = tm.ownerDocument().createElement("task-module");
1404 tm.appendChild(e);
1405 e.setAttribute("url", url.toString());
1406 }
1407
1408 m_documents.save(me);
1409
1410 if (context.saveAll(this)) {
1411 m_accounts.save(me);
1412
1413 // save calendars
1414 foreach (Calendar *c, calendarIdDict) {
1415 c->save(me);
1416 }
1417 // save standard worktime
1418 if (m_standardWorktime)
1419 m_standardWorktime->save(me);
1420
1421 // save project resources, must be after calendars
1422 QListIterator<ResourceGroup*> git(m_resourceGroups);
1423 while (git.hasNext()) {
1424 git.next() ->save(me);
1425 }
1426 }
1427 // Only save parent relations
1428 QListIterator<Relation*> it(m_dependParentNodes);
1429 while (it.hasNext()) {
1430 Relation *r = it.next();
1431 if (context.saveNode(r->parent()) && context.saveNode(r->child())) {
1432 r->save(me, context);
1433 }
1434 }
1435 if (context.saveAll(this)) {
1436 for (int i = 0; i < numChildren(); i++)
1437 // Save all children
1438 childNode(i)->save(me, context);
1439 }
1440 // Now we can save relations assuming no tasks have relations outside the project
1441 QListIterator<Node*> nodes(m_nodes);
1442 while (nodes.hasNext()) {
1443 nodes.next()->saveRelations(me, context);
1444 }
1445 if (context.saveAll(this)) {
1446 if (!m_managers.isEmpty()) {
1447 QDomElement el = me.ownerDocument().createElement("schedules");
1448 me.appendChild(el);
1449 foreach (ScheduleManager *sm, m_managers) {
1450 sm->saveXML(el);
1451 }
1452 }
1453 // save resource teams
1454 QDomElement el = me.ownerDocument().createElement("resource-teams");
1455 me.appendChild(el);
1456 foreach (Resource *r, resourceIdDict) {
1457 if (r->type() != Resource::Type_Team) {
1458 continue;
1459 }
1460 foreach (const QString &id, r->teamMemberIds()) {
1461 QDomElement e = el.ownerDocument().createElement("team");
1462 el.appendChild(e);
1463 e.setAttribute("team-id", r->id());
1464 e.setAttribute("member-id", id);
1465 }
1466 }
1467 }
1468 }
1469
saveWorkPackageXML(QDomElement & element,const Node * node,long id) const1470 void Project::saveWorkPackageXML(QDomElement &element, const Node *node, long id) const
1471 {
1472 QDomElement me = element.ownerDocument().createElement("project");
1473 element.appendChild(me);
1474
1475 me.setAttribute("name", m_name);
1476 me.setAttribute("leader", m_leader);
1477 me.setAttribute("id", m_id);
1478 me.setAttribute("description", m_description);
1479 me.setAttribute("timezone", m_timeZone.isValid() ? QString::fromLatin1(m_timeZone.id()) : QString());
1480
1481 me.setAttribute("scheduling", constraintToString());
1482 me.setAttribute("start-time", m_constraintStartTime.toString(Qt::ISODate));
1483 me.setAttribute("end-time", m_constraintEndTime.toString(Qt::ISODate));
1484
1485 QListIterator<ResourceGroup*> git(m_resourceGroups);
1486 while (git.hasNext()) {
1487 git.next() ->saveWorkPackageXML(me, node->assignedResources(id));
1488 }
1489
1490 if (node == 0) {
1491 return;
1492 }
1493 node->saveWorkPackageXML(me, id);
1494
1495 foreach (ScheduleManager *sm, m_managerIdMap) {
1496 if (sm->scheduleId() == id) {
1497 QDomElement el = me.ownerDocument().createElement("schedules");
1498 me.appendChild(el);
1499 sm->saveWorkPackageXML(el, *node);
1500 break;
1501 }
1502 }
1503 }
1504
setParentSchedule(Schedule * sch)1505 void Project::setParentSchedule(Schedule *sch)
1506 {
1507 QListIterator<Node*> it = m_nodes;
1508 while (it.hasNext()) {
1509 it.next() ->setParentSchedule(sch);
1510 }
1511 }
1512
addResourceGroup(ResourceGroup * group,int index)1513 void Project::addResourceGroup(ResourceGroup *group, int index)
1514 {
1515 int i = index == -1 ? m_resourceGroups.count() : index;
1516 emit resourceGroupToBeAdded(group, i);
1517 m_resourceGroups.insert(i, group);
1518 setResourceGroupId(group);
1519 group->setProject(this);
1520 foreach (Resource *r, group->resources()) {
1521 setResourceId(r);
1522 r->setProject(this);
1523 }
1524 emit resourceGroupAdded(group);
1525 emit projectChanged();
1526 }
1527
takeResourceGroup(ResourceGroup * group)1528 ResourceGroup *Project::takeResourceGroup(ResourceGroup *group)
1529 {
1530 int i = m_resourceGroups.indexOf(group);
1531 Q_ASSERT(i != -1);
1532 if (i == -1) {
1533 return 0;
1534 }
1535 emit resourceGroupToBeRemoved(group);
1536 ResourceGroup *g = m_resourceGroups.takeAt(i);
1537 Q_ASSERT(group == g);
1538 g->setProject(0);
1539 removeResourceGroupId(g->id());
1540 foreach (Resource *r, g->resources()) {
1541 r->setProject(0);
1542 removeResourceId(r->id());
1543 }
1544 emit resourceGroupRemoved(g);
1545 emit projectChanged();
1546 return g;
1547 }
1548
resourceGroups()1549 QList<ResourceGroup*> &Project::resourceGroups()
1550 {
1551 return m_resourceGroups;
1552 }
1553
addResource(ResourceGroup * group,Resource * resource,int index)1554 void Project::addResource(ResourceGroup *group, Resource *resource, int index)
1555 {
1556 int i = index == -1 ? group->numResources() : index;
1557 emit resourceToBeAdded(group, i);
1558 group->addResource(i, resource, 0);
1559 setResourceId(resource);
1560 emit resourceAdded(resource);
1561 emit projectChanged();
1562 }
1563
takeResource(ResourceGroup * group,Resource * resource)1564 Resource *Project::takeResource(ResourceGroup *group, Resource *resource)
1565 {
1566 emit resourceToBeRemoved(resource);
1567 bool result = removeResourceId(resource->id());
1568 Q_ASSERT(result == true);
1569 if (!result) {
1570 warnPlan << "Could not remove resource with id" << resource->id();
1571 }
1572 resource->removeRequests(); // not valid anymore
1573 Resource *r = group->takeResource(resource);
1574 Q_ASSERT(resource == r);
1575 if (resource != r) {
1576 warnPlan << "Could not take resource from group";
1577 }
1578 emit resourceRemoved(resource);
1579 emit projectChanged();
1580 return r;
1581 }
1582
moveResource(ResourceGroup * group,Resource * resource)1583 void Project::moveResource(ResourceGroup *group, Resource *resource)
1584 {
1585 if (group == resource->parentGroup()) {
1586 return;
1587 }
1588 takeResource(resource->parentGroup(), resource);
1589 addResource(group, resource);
1590 return;
1591 }
1592
externalProjects() const1593 QMap< QString, QString > Project::externalProjects() const
1594 {
1595 QMap< QString, QString > map;
1596 foreach (Resource *r, resourceList()) {
1597 for(QMapIterator<QString, QString> it(r->externalProjects()); it.hasNext();) {
1598 it.next();
1599 if (! map.contains(it.key())) {
1600 map[ it.key() ] = it.value();
1601 }
1602 }
1603 }
1604 return map;
1605 }
1606
addTask(Node * task,Node * position)1607 bool Project::addTask(Node* task, Node* position)
1608 {
1609 // we want to add a task at the given position. => the new node will
1610 // become next sibling right after position.
1611 if (0 == position) {
1612 return addSubTask(task, this);
1613 }
1614 //debugPlan<<"Add"<<task->name()<<" after"<<position->name();
1615 // in case we want to add to the main project, we make it child element
1616 // of the root element.
1617 if (Node::Type_Project == position->type()) {
1618 return addSubTask(task, position);
1619 }
1620 // find the position
1621 // we have to tell the parent that we want to delete one of its children
1622 Node* parentNode = position->parentNode();
1623 if (!parentNode) {
1624 debugPlan <<"parent node not found???";
1625 return false;
1626 }
1627 int index = parentNode->findChildNode(position);
1628 if (-1 == index) {
1629 // ok, it does not exist
1630 debugPlan <<"Task not found???";
1631 return false;
1632 }
1633 return addSubTask(task, index + 1, parentNode);
1634 }
1635
addSubTask(Node * task,Node * parent)1636 bool Project::addSubTask(Node* task, Node* parent)
1637 {
1638 // append task to parent
1639 return addSubTask(task, -1, parent);
1640 }
1641
addSubTask(Node * task,int index,Node * parent,bool emitSignal)1642 bool Project::addSubTask(Node* task, int index, Node* parent, bool emitSignal)
1643 {
1644 // we want to add a subtask to the node "parent" at the given index.
1645 // If parent is 0, add to this
1646 Node *p = parent;
1647 if (0 == p) {
1648 p = this;
1649 }
1650 if (!registerNodeId(task)) {
1651 errorPlan << "Failed to register node id, can not add subtask: " << task->name();
1652 return false;
1653 }
1654 int i = index == -1 ? p->numChildren() : index;
1655 if (emitSignal) emit nodeToBeAdded(p, i);
1656 p->insertChildNode(i, task);
1657 connect(this, &Project::standardWorktimeChanged, task, &Node::slotStandardWorktimeChanged);
1658 if (emitSignal) {
1659 emit nodeAdded(task);
1660 emit projectChanged();
1661 if (p != this && p->numChildren() == 1) {
1662 emit nodeChanged(p, TypeProperty);
1663 }
1664 }
1665 return true;
1666 }
1667
takeTask(Node * node,bool emitSignal)1668 void Project::takeTask(Node *node, bool emitSignal)
1669 {
1670 //debugPlan<<node->name();
1671 Node * parent = node->parentNode();
1672 if (parent == 0) {
1673 debugPlan <<"Node must have a parent!";
1674 return;
1675 }
1676 removeId(node->id());
1677 if (emitSignal) emit nodeToBeRemoved(node);
1678 disconnect(this, &Project::standardWorktimeChanged, node, &Node::slotStandardWorktimeChanged);
1679 parent->takeChildNode(node);
1680 if (emitSignal) {
1681 emit nodeRemoved(node);
1682 emit projectChanged();
1683 if (parent != this && parent->type() != Node::Type_Summarytask) {
1684 emit nodeChanged(parent, TypeProperty);
1685 }
1686 }
1687 }
1688
canMoveTask(Node * node,Node * newParent,bool checkBaselined)1689 bool Project::canMoveTask(Node* node, Node *newParent, bool checkBaselined)
1690 {
1691 //debugPlan<<node->name()<<" to"<<newParent->name();
1692 if (node == this) {
1693 return false;
1694 }
1695 if (checkBaselined) {
1696 if (node->isBaselined() || (newParent->type() != Node::Type_Summarytask && newParent->isBaselined())) {
1697 return false;
1698 }
1699 }
1700 Node *p = newParent;
1701 while (p && p != this) {
1702 if (! node->canMoveTo(p)) {
1703 return false;
1704 }
1705 p = p->parentNode();
1706 }
1707 return true;
1708 }
1709
moveTask(Node * node,Node * newParent,int newPos)1710 bool Project::moveTask(Node* node, Node *newParent, int newPos)
1711 {
1712 //debugPlan<<node->name()<<" to"<<newParent->name()<<","<<newPos;
1713 if (! canMoveTask(node, newParent)) {
1714 return false;
1715 }
1716 Node *oldParent = node->parentNode();
1717 int oldPos = oldParent->indexOf(node);
1718 int i = newPos < 0 ? newParent->numChildren() : newPos;
1719 if (oldParent == newParent && i == oldPos) {
1720 // no need to move to where it already is
1721 return false;
1722 }
1723 int newRow = i;
1724 if (oldParent == newParent && newPos > oldPos) {
1725 ++newRow; // itemmodels wants new row *before* node is removed from old position
1726 }
1727 debugPlan<<node->name()<<"at"<<oldParent->indexOf(node)<<"to"<<newParent->name()<<i<<newRow<<"("<<newPos<<")";
1728 emit nodeToBeMoved(node, oldPos, newParent, newRow);
1729 takeTask(node, false);
1730 addSubTask(node, i, newParent, false);
1731 emit nodeMoved(node);
1732 if (oldParent != this && oldParent->numChildren() == 0) {
1733 emit nodeChanged(oldParent, TypeProperty);
1734 }
1735 if (newParent != this && newParent->numChildren() == 1) {
1736 emit nodeChanged(newParent, TypeProperty);
1737 }
1738 return true;
1739 }
1740
canIndentTask(Node * node)1741 bool Project::canIndentTask(Node* node)
1742 {
1743 if (0 == node) {
1744 // should always be != 0. At least we would get the Project,
1745 // but you never know who might change that, so better be careful
1746 return false;
1747 }
1748 if (node->type() == Node::Type_Project) {
1749 //debugPlan<<"The root node cannot be indented";
1750 return false;
1751 }
1752 // we have to find the parent of task to manipulate its list of children
1753 Node* parentNode = node->parentNode();
1754 if (!parentNode) {
1755 return false;
1756 }
1757 if (parentNode->findChildNode(node) == -1) {
1758 errorPlan << "Tasknot found???";
1759 return false;
1760 }
1761 Node *sib = node->siblingBefore();
1762 if (!sib) {
1763 //debugPlan<<"new parent node not found";
1764 return false;
1765 }
1766 if (node->findParentRelation(sib) || node->findChildRelation(sib)) {
1767 //debugPlan<<"Cannot have relations to parent";
1768 return false;
1769 }
1770 return true;
1771 }
1772
indentTask(Node * node,int index)1773 bool Project::indentTask(Node* node, int index)
1774 {
1775 if (canIndentTask(node)) {
1776 Node * newParent = node->siblingBefore();
1777 int i = index == -1 ? newParent->numChildren() : index;
1778 moveTask(node, newParent, i);
1779 //debugPlan;
1780 return true;
1781 }
1782 return false;
1783 }
1784
canUnindentTask(Node * node)1785 bool Project::canUnindentTask(Node* node)
1786 {
1787 if (0 == node) {
1788 // is always != 0. At least we would get the Project, but you
1789 // never know who might change that, so better be careful
1790 return false;
1791 }
1792 if (Node::Type_Project == node->type()) {
1793 //debugPlan<<"The root node cannot be unindented";
1794 return false;
1795 }
1796 // we have to find the parent of task to manipulate its list of children
1797 // and we need the parent's parent too
1798 Node* parentNode = node->parentNode();
1799 if (!parentNode) {
1800 return false;
1801 }
1802 Node* grandParentNode = parentNode->parentNode();
1803 if (!grandParentNode) {
1804 //debugPlan<<"This node already is at the top level";
1805 return false;
1806 }
1807 int index = parentNode->findChildNode(node);
1808 if (-1 == index) {
1809 errorPlan << "Tasknot found???";
1810 return false;
1811 }
1812 return true;
1813 }
1814
unindentTask(Node * node)1815 bool Project::unindentTask(Node* node)
1816 {
1817 if (canUnindentTask(node)) {
1818 Node * parentNode = node->parentNode();
1819 Node *grandParentNode = parentNode->parentNode();
1820 int i = grandParentNode->indexOf(parentNode) + 1;
1821 if (i == 0) {
1822 i = grandParentNode->numChildren();
1823 }
1824 moveTask(node, grandParentNode, i);
1825 //debugPlan;
1826 return true;
1827 }
1828 return false;
1829 }
1830
canMoveTaskUp(Node * node)1831 bool Project::canMoveTaskUp(Node* node)
1832 {
1833 if (node == 0)
1834 return false; // safety
1835 // we have to find the parent of task to manipulate its list of children
1836 Node* parentNode = node->parentNode();
1837 if (!parentNode) {
1838 //debugPlan<<"No parent found";
1839 return false;
1840 }
1841 if (parentNode->findChildNode(node) == -1) {
1842 errorPlan << "Tasknot found???";
1843 return false;
1844 }
1845 if (node->siblingBefore()) {
1846 return true;
1847 }
1848 return false;
1849 }
1850
moveTaskUp(Node * node)1851 bool Project::moveTaskUp(Node* node)
1852 {
1853 if (canMoveTaskUp(node)) {
1854 moveTask(node, node->parentNode(), node->parentNode()->indexOf(node) - 1);
1855 return true;
1856 }
1857 return false;
1858 }
1859
canMoveTaskDown(Node * node)1860 bool Project::canMoveTaskDown(Node* node)
1861 {
1862 if (node == 0)
1863 return false; // safety
1864 // we have to find the parent of task to manipulate its list of children
1865 Node* parentNode = node->parentNode();
1866 if (!parentNode) {
1867 return false;
1868 }
1869 if (parentNode->findChildNode(node) == -1) {
1870 errorPlan << "Tasknot found???";
1871 return false;
1872 }
1873 if (node->siblingAfter()) {
1874 return true;
1875 }
1876 return false;
1877 }
1878
moveTaskDown(Node * node)1879 bool Project::moveTaskDown(Node* node)
1880 {
1881 if (canMoveTaskDown(node)) {
1882 moveTask(node, node->parentNode(), node->parentNode()->indexOf(node) + 1);
1883 return true;
1884 }
1885 return false;
1886 }
1887
createTask()1888 Task *Project::createTask()
1889 {
1890 Task * node = new Task();
1891 node->setId(uniqueNodeId());
1892 reserveId(node->id(), node);
1893 return node;
1894 }
1895
createTask(const Task & def)1896 Task *Project::createTask(const Task &def)
1897 {
1898 Task * node = new Task(def);
1899 node->setId(uniqueNodeId());
1900 reserveId(node->id(), node);
1901 return node;
1902 }
1903
findNode(const QString & id) const1904 Node *Project::findNode(const QString &id) const
1905 {
1906 if (m_parent == 0) {
1907 if (nodeIdDict.contains(id)) {
1908 return nodeIdDict[ id ];
1909 }
1910 return 0;
1911 }
1912 return m_parent->findNode(id);
1913 }
1914
nodeIdentExists(const QString & id) const1915 bool Project::nodeIdentExists(const QString &id) const
1916 {
1917 return nodeIdDict.contains(id) || nodeIdReserved.contains(id);
1918 }
1919
uniqueNodeId(int seed) const1920 QString Project::uniqueNodeId(int seed) const
1921 {
1922 Q_UNUSED(seed);
1923 QString ident = generateId();
1924 while (nodeIdentExists(ident)) {
1925 ident = generateId();
1926 }
1927 return ident;
1928 }
1929
uniqueNodeId(const QList<QString> & existingIds,int seed)1930 QString Project::uniqueNodeId(const QList<QString> &existingIds, int seed)
1931 {
1932 QString id = uniqueNodeId(seed);
1933 while (existingIds.contains(id)) {
1934 id = uniqueNodeId(seed);
1935 }
1936 return id;
1937 }
1938
removeId(const QString & id)1939 bool Project::removeId(const QString &id)
1940 {
1941 //debugPlan <<"id=" << id;
1942 if (m_parent) {
1943 return m_parent->removeId(id);
1944 }
1945 //debugPlan << "id=" << id<< nodeIdDict.contains(id);
1946 return nodeIdDict.remove(id);
1947 }
1948
reserveId(const QString & id,Node * node)1949 void Project::reserveId(const QString &id, Node *node)
1950 {
1951 //debugPlan <<"id=" << id << node->name();
1952 nodeIdReserved.insert(id, node);
1953 }
1954
registerNodeId(Node * node)1955 bool Project::registerNodeId(Node *node)
1956 {
1957 nodeIdReserved.remove(node->id());
1958 if (node->id().isEmpty()) {
1959 warnPlan << "Node id is empty, cannot register it";
1960 return false;
1961 }
1962 Node *rn = findNode(node->id());
1963 if (rn == 0) {
1964 //debugPlan <<"id=" << node->id() << node->name();
1965 nodeIdDict.insert(node->id(), node);
1966 return true;
1967 }
1968 if (rn != node) {
1969 errorPlan << "Id already exists for different task: " << node->id();
1970 return false;
1971 }
1972 //debugPlan<<"Already exists" <<"id=" << node->id() << node->name();
1973 return true;
1974 }
1975
allNodes(bool ordered,Node * parent) const1976 QList<Node*> Project::allNodes(bool ordered, Node* parent) const
1977 {
1978 QList<Node*> lst;
1979 if (ordered) {
1980 const Node *p = parent ? parent : this;
1981 foreach (Node *n, p->childNodeIterator()) {
1982 if (n->type() == Node::Type_Task || n->type() == Type_Milestone || n->type() == Node::Type_Summarytask) {
1983 lst << static_cast<Task*>(n);
1984 lst += allNodes(ordered, n);
1985 }
1986 }
1987 } else {
1988 lst = nodeIdDict.values();
1989 int me = lst.indexOf(const_cast<Project*>(this));
1990 if (me != -1) {
1991 lst.removeAt(me);
1992 }
1993 }
1994 return lst;
1995 }
1996
allTasks(const Node * parent) const1997 QList<Task*> Project::allTasks(const Node *parent) const
1998 {
1999 QList<Task*> lst;
2000 const Node *p = parent ? parent : this;
2001 foreach (Node *n, p->childNodeIterator()) {
2002 if (n->type() == Node::Type_Task || n->type() == Type_Milestone) {
2003 lst << static_cast<Task*>(n);
2004 }
2005 lst += allTasks(n);
2006 }
2007 return lst;
2008 }
2009
isStarted() const2010 bool Project::isStarted() const
2011 {
2012 const QList<Task*> tasks = allTasks();
2013 for (const Task *t : tasks) {
2014 if (t->isStarted()) {
2015 return true;
2016 }
2017 }
2018 return false;
2019 }
2020
setResourceGroupId(ResourceGroup * group)2021 bool Project::setResourceGroupId(ResourceGroup *group)
2022 {
2023 if (group == 0) {
2024 return false;
2025 }
2026 if (! group->id().isEmpty()) {
2027 ResourceGroup *g = findResourceGroup(group->id());
2028 if (group == g) {
2029 return true;
2030 } else if (g == 0) {
2031 insertResourceGroupId(group->id(), group);
2032 return true;
2033 }
2034 }
2035 QString id = uniqueResourceGroupId();
2036 group->setId(id);
2037 if (id.isEmpty()) {
2038 return false;
2039 }
2040 insertResourceGroupId(id, group);
2041 return true;
2042 }
2043
uniqueResourceGroupId() const2044 QString Project::uniqueResourceGroupId() const {
2045 QString id = generateId();
2046 while (resourceGroupIdDict.contains(id)) {
2047 id = generateId();
2048 }
2049 return id;
2050 }
2051
group(const QString & id)2052 ResourceGroup *Project::group(const QString& id)
2053 {
2054 return findResourceGroup(id);
2055 }
2056
groupByName(const QString & name) const2057 ResourceGroup *Project::groupByName(const QString& name) const
2058 {
2059 foreach (ResourceGroup *g, resourceGroupIdDict) {
2060 if (g->name() == name) {
2061 return g;
2062 }
2063 }
2064 return 0;
2065 }
2066
autoAllocateResources() const2067 QList<Resource*> Project::autoAllocateResources() const
2068 {
2069 QList<Resource*> lst;
2070 foreach (Resource *r, resourceIdDict) {
2071 if (r->autoAllocate()) {
2072 lst << r;
2073 }
2074 }
2075 return lst;
2076 }
2077
insertResourceId(const QString & id,Resource * resource)2078 void Project::insertResourceId(const QString &id, Resource *resource)
2079 {
2080 resourceIdDict.insert(id, resource);
2081 }
2082
removeResourceId(const QString & id)2083 bool Project::removeResourceId(const QString &id)
2084 {
2085 return resourceIdDict.remove(id);
2086 }
2087
setResourceId(Resource * resource)2088 bool Project::setResourceId(Resource *resource)
2089 {
2090 if (resource == 0) {
2091 return false;
2092 }
2093 if (! resource->id().isEmpty()) {
2094 Resource *r = findResource(resource->id());
2095 if (resource == r) {
2096 return true;
2097 } else if (r == 0) {
2098 insertResourceId(resource->id(), resource);
2099 return true;
2100 }
2101 }
2102 QString id = uniqueResourceId();
2103 resource->setId(id);
2104 if (id.isEmpty()) {
2105 return false;
2106 }
2107 insertResourceId(id, resource);
2108 return true;
2109 }
2110
uniqueResourceId() const2111 QString Project::uniqueResourceId() const {
2112 QString id = generateId();
2113 while (resourceIdDict.contains(id)) {
2114 id = generateId();
2115 }
2116 return id;
2117 }
2118
resource(const QString & id)2119 Resource *Project::resource(const QString& id)
2120 {
2121 return findResource(id);
2122 }
2123
resourceByName(const QString & name) const2124 Resource *Project::resourceByName(const QString& name) const
2125 {
2126 QHash<QString, Resource*>::const_iterator it;
2127 for (it = resourceIdDict.constBegin(); it != resourceIdDict.constEnd(); ++it) {
2128 Resource *r = it.value();
2129 if (r->name() == name) {
2130 Q_ASSERT(it.key() == r->id());
2131 return r;
2132 }
2133 }
2134 return 0;
2135 }
2136
resourceNameList() const2137 QStringList Project::resourceNameList() const
2138 {
2139 QStringList lst;
2140 foreach (Resource *r, resourceIdDict) {
2141 lst << r->name();
2142 }
2143 return lst;
2144 }
2145
plannedEffortCostPrDay(QDate start,QDate end,long id,EffortCostCalculationType typ) const2146 EffortCostMap Project::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const
2147 {
2148 //debugPlan<<start<<end<<id;
2149 Schedule *s = schedule(id);
2150 if (s == 0) {
2151 return EffortCostMap();
2152 }
2153 EffortCostMap ec;
2154 QListIterator<Node*> it(childNodeIterator());
2155 while (it.hasNext()) {
2156 ec += it.next() ->plannedEffortCostPrDay(start, end, id, typ);
2157 }
2158 return ec;
2159 }
2160
plannedEffortCostPrDay(const Resource * resource,QDate start,QDate end,long id,EffortCostCalculationType typ) const2161 EffortCostMap Project::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const
2162 {
2163 //debugPlan<<start<<end<<id;
2164 EffortCostMap ec;
2165 QListIterator<Node*> it(childNodeIterator());
2166 while (it.hasNext()) {
2167 ec += it.next() ->plannedEffortCostPrDay(resource, start, end, id, typ);
2168 }
2169 return ec;
2170 }
2171
actualEffortCostPrDay(QDate start,QDate end,long id,EffortCostCalculationType typ) const2172 EffortCostMap Project::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ) const
2173 {
2174 //debugPlan<<start<<end<<id;
2175 EffortCostMap ec;
2176 QListIterator<Node*> it(childNodeIterator());
2177 while (it.hasNext()) {
2178 ec += it.next() ->actualEffortCostPrDay(start, end, id, typ);
2179 }
2180 return ec;
2181 }
2182
actualEffortCostPrDay(const Resource * resource,QDate start,QDate end,long id,EffortCostCalculationType typ) const2183 EffortCostMap Project::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ) const
2184 {
2185 //debugPlan<<start<<end<<id;
2186 EffortCostMap ec;
2187 QListIterator<Node*> it(childNodeIterator());
2188 while (it.hasNext()) {
2189 ec += it.next() ->actualEffortCostPrDay(resource, start, end, id, typ);
2190 }
2191 return ec;
2192 }
2193
2194 // Returns the total planned effort for this project (or subproject)
plannedEffort(long id,EffortCostCalculationType typ) const2195 Duration Project::plannedEffort(long id, EffortCostCalculationType typ) const
2196 {
2197 //debugPlan;
2198 Duration eff;
2199 QListIterator<Node*> it(childNodeIterator());
2200 while (it.hasNext()) {
2201 eff += it.next() ->plannedEffort(id, typ);
2202 }
2203 return eff;
2204 }
2205
2206 // Returns the total planned effort for this project (or subproject) on date
plannedEffort(QDate date,long id,EffortCostCalculationType typ) const2207 Duration Project::plannedEffort(QDate date, long id, EffortCostCalculationType typ) const
2208 {
2209 //debugPlan;
2210 Duration eff;
2211 QListIterator<Node*> it(childNodeIterator());
2212 while (it.hasNext()) {
2213 eff += it.next() ->plannedEffort(date, id, typ);
2214 }
2215 return eff;
2216 }
2217
2218 // Returns the total planned effort for this project (or subproject) upto and including date
plannedEffortTo(QDate date,long id,EffortCostCalculationType typ) const2219 Duration Project::plannedEffortTo(QDate date, long id, EffortCostCalculationType typ) const
2220 {
2221 //debugPlan;
2222 Duration eff;
2223 QListIterator<Node*> it(childNodeIterator());
2224 while (it.hasNext()) {
2225 eff += it.next() ->plannedEffortTo(date, id, typ);
2226 }
2227 return eff;
2228 }
2229
2230 // Returns the total actual effort for this project (or subproject) upto and including date
actualEffortTo(QDate date) const2231 Duration Project::actualEffortTo(QDate date) const
2232 {
2233 //debugPlan;
2234 Duration eff;
2235 QListIterator
2236 <Node*> it(childNodeIterator());
2237 while (it.hasNext()) {
2238 eff += it.next() ->actualEffortTo(date);
2239 }
2240 return eff;
2241 }
2242
2243 // Returns the total planned effort for this project (or subproject) upto and including date
plannedCostTo(QDate date,long id,EffortCostCalculationType typ) const2244 double Project::plannedCostTo(QDate date, long id, EffortCostCalculationType typ) const
2245 {
2246 //debugPlan;
2247 double c = 0;
2248 QListIterator
2249 <Node*> it(childNodeIterator());
2250 while (it.hasNext()) {
2251 c += it.next() ->plannedCostTo(date, id, typ);
2252 }
2253 return c;
2254 }
2255
2256 // Returns the total actual cost for this project (or subproject) upto and including date
actualCostTo(long int id,QDate date) const2257 EffortCost Project::actualCostTo(long int id, QDate date) const
2258 {
2259 //debugPlan;
2260 EffortCost c;
2261 QListIterator<Node*> it(childNodeIterator());
2262 while (it.hasNext()) {
2263 c += it.next() ->actualCostTo(id, date);
2264 }
2265 return c;
2266 }
2267
budgetedWorkPerformed(QDate date,long id) const2268 Duration Project::budgetedWorkPerformed(QDate date, long id) const
2269 {
2270 //debugPlan;
2271 Duration e;
2272 foreach (Node *n, childNodeIterator()) {
2273 e += n->budgetedWorkPerformed(date, id);
2274 }
2275 return e;
2276 }
2277
budgetedCostPerformed(QDate date,long id) const2278 double Project::budgetedCostPerformed(QDate date, long id) const
2279 {
2280 //debugPlan;
2281 double c = 0.0;
2282 foreach (Node *n, childNodeIterator()) {
2283 c += n->budgetedCostPerformed(date, id);
2284 }
2285 return c;
2286 }
2287
effortPerformanceIndex(QDate date,long id) const2288 double Project::effortPerformanceIndex(QDate date, long id) const
2289 {
2290 //debugPlan;
2291 debugPlan<<date<<id;
2292 Duration b = budgetedWorkPerformed(date, id);
2293 if (b == Duration::zeroDuration) {
2294 return 1.0;
2295 }
2296 Duration a = actualEffortTo(date);
2297 if (b == Duration::zeroDuration) {
2298 return 1.0;
2299 }
2300 return b.toDouble() / a.toDouble();
2301 }
2302
schedulePerformanceIndex(QDate date,long id) const2303 double Project::schedulePerformanceIndex(QDate date, long id) const
2304 {
2305 //debugPlan;
2306 double r = 1.0;
2307 double s = bcws(date, id);
2308 double p = bcwp(date, id);
2309 if (s > 0.0) {
2310 r = p / s;
2311 }
2312 debugPlan<<s<<p<<r;
2313 return r;
2314 }
2315
bcws(QDate date,long id) const2316 double Project::bcws(QDate date, long id) const
2317 {
2318 //debugPlan;
2319 double c = plannedCostTo(date, id, ECCT_EffortWork);
2320 debugPlan<<c;
2321 return c;
2322 }
2323
bcwp(long id) const2324 double Project::bcwp(long id) const
2325 {
2326 QDate date = QDate::currentDate();
2327 return bcwp(date, id);
2328 }
2329
bcwp(QDate date,long id) const2330 double Project::bcwp(QDate date, long id) const
2331 {
2332 debugPlan<<date<<id;
2333 QDate start = startTime(id).date();
2334 QDate end = endTime(id).date();
2335 EffortCostMap plan = plannedEffortCostPrDay(start, end, id, ECCT_EffortWork);
2336 EffortCostMap actual = actualEffortCostPrDay(start, (end > date ? end : date), id);
2337
2338 double budgetAtCompletion;
2339 double plannedCompleted;
2340 double budgetedCompleted;
2341 bool useEffort = false; //FIXME
2342 if (useEffort) {
2343 budgetAtCompletion = plan.totalEffort().toDouble(Duration::Unit_h);
2344 plannedCompleted = plan.effortTo(date).toDouble(Duration::Unit_h);
2345 //actualCompleted = actual.effortTo(date).toDouble(Duration::Unit_h);
2346 budgetedCompleted = budgetedWorkPerformed(date, id).toDouble(Duration::Unit_h);
2347 } else {
2348 budgetAtCompletion = plan.totalCost();
2349 plannedCompleted = plan.costTo(date);
2350 budgetedCompleted = budgetedCostPerformed(date, id);
2351 }
2352 double c = 0.0;
2353 if (budgetAtCompletion > 0.0) {
2354 double percentageCompletion = budgetedCompleted / budgetAtCompletion;
2355 c = budgetAtCompletion * percentageCompletion; //??
2356 debugPlan<<percentageCompletion<<budgetAtCompletion<<budgetedCompleted<<plannedCompleted;
2357 }
2358 return c;
2359 }
2360
addCalendar(Calendar * calendar,Calendar * parent,int index)2361 void Project::addCalendar(Calendar *calendar, Calendar *parent, int index)
2362 {
2363 Q_ASSERT(calendar != 0);
2364 //debugPlan<<calendar->name()<<","<<(parent?parent->name():"No parent");
2365 int row = parent == 0 ? m_calendars.count() : parent->calendars().count();
2366 if (index >= 0 && index < row) {
2367 row = index;
2368 }
2369 emit calendarToBeAdded(parent, row);
2370 calendar->setProject(this);
2371 if (parent == 0) {
2372 calendar->setParentCal(0); // in case
2373 m_calendars.insert(row, calendar);
2374 } else {
2375 calendar->setParentCal(parent, row);
2376 }
2377 if (calendar->isDefault()) {
2378 setDefaultCalendar(calendar);
2379 }
2380 setCalendarId(calendar);
2381 emit calendarAdded(calendar);
2382 emit projectChanged();
2383 }
2384
takeCalendar(Calendar * calendar)2385 void Project::takeCalendar(Calendar *calendar)
2386 {
2387 emit calendarToBeRemoved(calendar);
2388 removeCalendarId(calendar->id());
2389 if (calendar == m_defaultCalendar) {
2390 m_defaultCalendar = 0;
2391 }
2392 if (calendar->parentCal() == 0) {
2393 int i = indexOf(calendar);
2394 if (i != -1) {
2395 m_calendars.removeAt(i);
2396 }
2397 } else {
2398 calendar->setParentCal(0);
2399 }
2400 emit calendarRemoved(calendar);
2401 calendar->setProject(0);
2402 emit projectChanged();
2403 }
2404
indexOf(const Calendar * calendar) const2405 int Project::indexOf(const Calendar *calendar) const
2406 {
2407 return m_calendars.indexOf(const_cast<Calendar*>(calendar));
2408 }
2409
calendar(const QString & id) const2410 Calendar *Project::calendar(const QString& id) const
2411 {
2412 return findCalendar(id);
2413 }
2414
calendarByName(const QString & name) const2415 Calendar *Project::calendarByName(const QString& name) const
2416 {
2417 foreach(Calendar *c, calendarIdDict) {
2418 if (c->name() == name) {
2419 return c;
2420 }
2421 }
2422 return 0;
2423 }
2424
calendars() const2425 const QList<Calendar*> &Project::calendars() const
2426 {
2427 return m_calendars;
2428 }
2429
allCalendars() const2430 QList<Calendar*> Project::allCalendars() const
2431 {
2432 return calendarIdDict.values();
2433 }
2434
calendarNames() const2435 QStringList Project::calendarNames() const
2436 {
2437 QStringList lst;
2438 foreach(Calendar *c, calendarIdDict) {
2439 lst << c->name();
2440 }
2441 return lst;
2442 }
2443
setCalendarId(Calendar * calendar)2444 bool Project::setCalendarId(Calendar *calendar)
2445 {
2446 if (calendar == 0) {
2447 return false;
2448 }
2449 if (! calendar->id().isEmpty()) {
2450 Calendar *c = findCalendar(calendar->id());
2451 if (calendar == c) {
2452 return true;
2453 } else if (c == 0) {
2454 insertCalendarId(calendar->id(), calendar);
2455 return true;
2456 }
2457 }
2458 QString id = uniqueCalendarId();
2459 calendar->setId(id);
2460 if (id.isEmpty()) {
2461 return false;
2462 }
2463 insertCalendarId(id, calendar);
2464 return true;
2465 }
2466
uniqueCalendarId() const2467 QString Project::uniqueCalendarId() const {
2468 QString id = generateId();
2469 while (calendarIdDict.contains(id)) {
2470 id = generateId();
2471 }
2472 return id;
2473 }
2474
setDefaultCalendar(Calendar * cal)2475 void Project::setDefaultCalendar(Calendar *cal)
2476 {
2477 if (m_defaultCalendar) {
2478 m_defaultCalendar->setDefault(false);
2479 }
2480 m_defaultCalendar = cal;
2481 if (cal) {
2482 cal->setDefault(true);
2483 }
2484 emit defaultCalendarChanged(cal);
2485 emit projectChanged();
2486 }
2487
setStandardWorktime(StandardWorktime * worktime)2488 void Project::setStandardWorktime(StandardWorktime * worktime)
2489 {
2490 if (m_standardWorktime != worktime) {
2491 delete m_standardWorktime;
2492 m_standardWorktime = worktime;
2493 m_standardWorktime->setProject(this);
2494 emit standardWorktimeChanged(worktime);
2495 }
2496 }
2497
emitDocumentAdded(Node * node,Document * doc,int index)2498 void Project::emitDocumentAdded(Node *node , Document *doc , int index)
2499 {
2500 emit documentAdded(node, doc, index);
2501 }
2502
emitDocumentRemoved(Node * node,Document * doc,int index)2503 void Project::emitDocumentRemoved(Node *node , Document *doc , int index)
2504 {
2505 emit documentRemoved(node, doc, index);
2506 }
2507
emitDocumentChanged(Node * node,Document * doc,int index)2508 void Project::emitDocumentChanged(Node *node , Document *doc , int index)
2509 {
2510 emit documentChanged(node, doc, index);
2511 }
2512
linkExists(const Node * par,const Node * child) const2513 bool Project::linkExists(const Node *par, const Node *child) const
2514 {
2515 if (par == 0 || child == 0 || par == child || par->isDependChildOf(child)) {
2516 return false;
2517 }
2518 foreach (Relation *r, par->dependChildNodes()) {
2519 if (r->child() == child) {
2520 return true;
2521 }
2522 }
2523 return false;
2524 }
2525
legalToLink(const Node * par,const Node * child) const2526 bool Project::legalToLink(const Node *par, const Node *child) const
2527 {
2528 //debugPlan<<par.name()<<" ("<<par.numDependParentNodes()<<" parents)"<<child.name()<<" ("<<child.numDependChildNodes()<<" children)";
2529
2530 if (par == 0 || child == 0 || par == child || par->isDependChildOf(child)) {
2531 return false;
2532 }
2533 if (linkExists(par, child)) {
2534 return false;
2535 }
2536 bool legal = true;
2537 // see if par/child is related
2538 if (legal && (par->isParentOf(child) || child->isParentOf(par))) {
2539 legal = false;
2540 }
2541 if (legal)
2542 legal = legalChildren(par, child);
2543 if (legal)
2544 legal = legalParents(par, child);
2545
2546 if (legal) {
2547 foreach (Node *p, par->childNodeIterator()) {
2548 if (! legalToLink(p, child)) {
2549 return false;
2550 }
2551 }
2552 }
2553 return legal;
2554 }
2555
legalParents(const Node * par,const Node * child) const2556 bool Project::legalParents(const Node *par, const Node *child) const
2557 {
2558 bool legal = true;
2559 //debugPlan<<par->name()<<" ("<<par->numDependParentNodes()<<" parents)"<<child->name()<<" ("<<child->numDependChildNodes()<<" children)";
2560 for (int i = 0; i < par->numDependParentNodes() && legal; ++i) {
2561 Node *pNode = par->getDependParentNode(i) ->parent();
2562 if (child->isParentOf(pNode) || pNode->isParentOf(child)) {
2563 //debugPlan<<"Found:"<<pNode->name()<<" is related to"<<child->name();
2564 legal = false;
2565 } else {
2566 legal = legalChildren(pNode, child);
2567 }
2568 if (legal)
2569 legal = legalParents(pNode, child);
2570 }
2571 return legal;
2572 }
2573
legalChildren(const Node * par,const Node * child) const2574 bool Project::legalChildren(const Node *par, const Node *child) const
2575 {
2576 bool legal = true;
2577 //debugPlan<<par->name()<<" ("<<par->numDependParentNodes()<<" parents)"<<child->name()<<" ("<<child->numDependChildNodes()<<" children)";
2578 for (int j = 0; j < child->numDependChildNodes() && legal; ++j) {
2579 Node *cNode = child->getDependChildNode(j) ->child();
2580 if (par->isParentOf(cNode) || cNode->isParentOf(par)) {
2581 //debugPlan<<"Found:"<<par->name()<<" is related to"<<cNode->name();
2582 legal = false;
2583 } else {
2584 legal = legalChildren(par, cNode);
2585 }
2586 }
2587 return legal;
2588 }
2589
wbsDefinition()2590 WBSDefinition &Project::wbsDefinition()
2591 {
2592 return m_wbsDefinition;
2593 }
2594
setWbsDefinition(const WBSDefinition & def)2595 void Project::setWbsDefinition(const WBSDefinition &def)
2596 {
2597 //debugPlan;
2598 m_wbsDefinition = def;
2599 emit wbsDefinitionChanged();
2600 emit projectChanged();
2601 }
2602
generateWBSCode(QList<int> & indexes,bool sortable) const2603 QString Project::generateWBSCode(QList<int> &indexes, bool sortable) const
2604 {
2605 QString code = m_wbsDefinition.projectCode();
2606 if (sortable) {
2607 int fw = (nodeIdDict.count() / 10) + 1;
2608 QLatin1Char fc('0');
2609 foreach (int index, indexes) {
2610 code += ".%1";
2611 code = code.arg(QString::number(index), fw, fc);
2612 }
2613 //debugPlan<<code<<"------------------";
2614 } else {
2615 if (! code.isEmpty() && ! indexes.isEmpty()) {
2616 code += m_wbsDefinition.projectSeparator();
2617 }
2618 int level = 1;
2619 foreach (int index, indexes) {
2620 code += m_wbsDefinition.code(index + 1, level);
2621 if (level < indexes.count()) {
2622 // not last level, add separator also
2623 code += m_wbsDefinition.separator(level);
2624 }
2625 ++level;
2626 }
2627 }
2628 //debugPlan<<code;
2629 return code;
2630 }
2631
setCurrentSchedule(long id)2632 void Project::setCurrentSchedule(long id)
2633 {
2634 //debugPlan;
2635 setCurrentSchedulePtr(findSchedule(id));
2636 Node::setCurrentSchedule(id);
2637 QHash<QString, Resource*> hash = resourceIdDict;
2638 foreach (Resource * r, hash) {
2639 r->setCurrentSchedule(id);
2640 }
2641 emit currentScheduleChanged();
2642 emit projectChanged();
2643 }
2644
scheduleManager(long id) const2645 ScheduleManager *Project::scheduleManager(long id) const
2646 {
2647 foreach (ScheduleManager *sm, m_managers) {
2648 if (sm->scheduleId() == id) {
2649 return sm;
2650 }
2651 }
2652 return 0;
2653 }
2654
scheduleManager(const QString & id) const2655 ScheduleManager *Project::scheduleManager(const QString &id) const
2656 {
2657 return m_managerIdMap.value(id);
2658 }
2659
findScheduleManagerByName(const QString & name) const2660 ScheduleManager *Project::findScheduleManagerByName(const QString &name) const
2661 {
2662 //debugPlan;
2663 ScheduleManager *m = 0;
2664 foreach(ScheduleManager *sm, m_managers) {
2665 m = sm->findManager(name);
2666 if (m) {
2667 break;
2668 }
2669 }
2670 return m;
2671 }
2672
allScheduleManagers() const2673 QList<ScheduleManager*> Project::allScheduleManagers() const
2674 {
2675 QList<ScheduleManager*> lst;
2676 foreach (ScheduleManager *sm, m_managers) {
2677 lst << sm;
2678 lst << sm->allChildren();
2679 }
2680 return lst;
2681 }
2682
uniqueScheduleName() const2683 QString Project::uniqueScheduleName() const {
2684 //debugPlan;
2685 QString n = i18n("Plan");
2686 bool unique = findScheduleManagerByName(n) == 0;
2687 if (unique) {
2688 return n;
2689 }
2690 n += " %1";
2691 int i = 1;
2692 for (; true; ++i) {
2693 unique = findScheduleManagerByName(n.arg(i)) == 0;
2694 if (unique) {
2695 break;
2696 }
2697 }
2698 return n.arg(i);
2699 }
2700
addScheduleManager(ScheduleManager * sm,ScheduleManager * parent,int index)2701 void Project::addScheduleManager(ScheduleManager *sm, ScheduleManager *parent, int index)
2702 {
2703 int row = parent == 0 ? m_managers.count() : parent->childCount();
2704 if (index >= 0 && index < row) {
2705 row = index;
2706 }
2707 if (parent == 0) {
2708 emit scheduleManagerToBeAdded(parent, row);
2709 m_managers.insert(row, sm);
2710 } else {
2711 emit scheduleManagerToBeAdded(parent, row);
2712 sm->setParentManager(parent, row);
2713 }
2714 if (sm->managerId().isEmpty()) {
2715 sm->setManagerId(uniqueScheduleManagerId());
2716 }
2717 Q_ASSERT(! m_managerIdMap.contains(sm->managerId()));
2718 m_managerIdMap.insert(sm->managerId(), sm);
2719
2720 emit scheduleManagerAdded(sm);
2721 emit projectChanged();
2722 //debugPlan<<"Added:"<<sm->name()<<", now"<<m_managers.count();
2723 }
2724
takeScheduleManager(ScheduleManager * sm)2725 int Project::takeScheduleManager(ScheduleManager *sm)
2726 {
2727 foreach (ScheduleManager *s, sm->children()) {
2728 takeScheduleManager(s);
2729 }
2730 if (sm->scheduling()) {
2731 sm->stopCalculation();
2732 }
2733 int index = -1;
2734 if (sm->parentManager()) {
2735 int index = sm->parentManager()->indexOf(sm);
2736 if (index >= 0) {
2737 emit scheduleManagerToBeRemoved(sm);
2738 sm->setParentManager(0);
2739 m_managerIdMap.remove(sm->managerId());
2740 emit scheduleManagerRemoved(sm);
2741 emit projectChanged();
2742 }
2743 } else {
2744 index = indexOf(sm);
2745 if (index >= 0) {
2746 emit scheduleManagerToBeRemoved(sm);
2747 m_managers.removeAt(indexOf(sm));
2748 m_managerIdMap.remove(sm->managerId());
2749 emit scheduleManagerRemoved(sm);
2750 emit projectChanged();
2751 }
2752 }
2753 return index;
2754 }
2755
swapScheduleManagers(ScheduleManager * from,ScheduleManager * to)2756 void Project::swapScheduleManagers(ScheduleManager *from, ScheduleManager *to)
2757 {
2758 emit scheduleManagersSwapped(from, to);
2759 }
2760
moveScheduleManager(ScheduleManager * sm,ScheduleManager * newparent,int newindex)2761 void Project::moveScheduleManager(ScheduleManager *sm, ScheduleManager *newparent, int newindex)
2762 {
2763 //debugPlan<<sm->name()<<newparent<<newindex;
2764 emit scheduleManagerToBeMoved(sm);
2765 if (! sm->parentManager()) {
2766 m_managers.removeAt(indexOf(sm));
2767 }
2768 sm->setParentManager(newparent, newindex);
2769 if (! newparent) {
2770 m_managers.insert(newindex, sm);
2771 }
2772 emit scheduleManagerMoved(sm, newindex);
2773 }
2774
isScheduleManager(void * ptr) const2775 bool Project::isScheduleManager(void *ptr) const
2776 {
2777 const ScheduleManager *sm = static_cast<ScheduleManager*>(ptr);
2778 if (indexOf(sm) >= 0) {
2779 return true;
2780 }
2781 foreach (ScheduleManager *p, m_managers) {
2782 if (p->isParentOf(sm)) {
2783 return true;
2784 }
2785 }
2786 return false;
2787 }
2788
createScheduleManager(const QString & name)2789 ScheduleManager *Project::createScheduleManager(const QString &name)
2790 {
2791 //debugPlan<<name;
2792 ScheduleManager *sm = new ScheduleManager(*this, name);
2793 return sm;
2794 }
2795
createScheduleManager()2796 ScheduleManager *Project::createScheduleManager()
2797 {
2798 //debugPlan;
2799 return createScheduleManager(uniqueScheduleName());
2800 }
2801
uniqueScheduleManagerId() const2802 QString Project::uniqueScheduleManagerId() const
2803 {
2804 QString ident = KRandom::randomString(10);
2805 while (m_managerIdMap.contains(ident)) {
2806 ident = KRandom::randomString(10);
2807 }
2808 return ident;
2809 }
2810
isBaselined(long id) const2811 bool Project::isBaselined(long id) const
2812 {
2813 if (id == ANYSCHEDULED) {
2814 foreach (ScheduleManager *sm, allScheduleManagers()) {
2815 if (sm->isBaselined()) {
2816 return true;
2817 }
2818 }
2819 return false;
2820 }
2821 Schedule *s = schedule(id);
2822 return s == 0 ? false : s->isBaselined();
2823 }
2824
createSchedule(const QString & name,Schedule::Type type)2825 MainSchedule *Project::createSchedule(const QString& name, Schedule::Type type)
2826 {
2827 //debugPlan<<"No of schedules:"<<m_schedules.count();
2828 MainSchedule *sch = new MainSchedule();
2829 sch->setName(name);
2830 sch->setType(type);
2831 addMainSchedule(sch);
2832 return sch;
2833 }
2834
addMainSchedule(MainSchedule * sch)2835 void Project::addMainSchedule(MainSchedule *sch)
2836 {
2837 if (sch == 0) {
2838 return;
2839 }
2840 //debugPlan<<"No of schedules:"<<m_schedules.count();
2841 long i = 1; // keep this positive (negative values are special...)
2842 while (m_schedules.contains(i)) {
2843 ++i;
2844 }
2845 sch->setId(i);
2846 sch->setNode(this);
2847 addSchedule(sch);
2848 }
2849
removeCalendarId(const QString & id)2850 bool Project::removeCalendarId(const QString &id)
2851 {
2852 //debugPlan <<"id=" << id;
2853 return calendarIdDict.remove(id);
2854 }
2855
insertCalendarId(const QString & id,Calendar * calendar)2856 void Project::insertCalendarId(const QString &id, Calendar *calendar)
2857 {
2858 //debugPlan <<"id=" << id <<":" << calendar->name();
2859 calendarIdDict.insert(id, calendar);
2860 }
2861
changed(Node * node,int property)2862 void Project::changed(Node *node, int property)
2863 {
2864 if (m_parent == 0) {
2865 Node::changed(node, property); // reset cache
2866 if (property != Node::TypeProperty) {
2867 // add/remove node is handled elsewhere
2868 emit nodeChanged(node, property);
2869 emit projectChanged();
2870 }
2871 return;
2872 }
2873 Node::changed(node, property);
2874 }
2875
changed(ResourceGroup * group)2876 void Project::changed(ResourceGroup *group)
2877 {
2878 //debugPlan;
2879 emit resourceGroupChanged(group);
2880 emit projectChanged();
2881 }
2882
changed(ScheduleManager * sm,int property)2883 void Project::changed(ScheduleManager *sm, int property)
2884 {
2885 emit scheduleManagerChanged(sm, property);
2886 emit projectChanged();
2887 }
2888
changed(MainSchedule * sch)2889 void Project::changed(MainSchedule *sch)
2890 {
2891 //debugPlan<<sch->id();
2892 emit scheduleChanged(sch);
2893 emit projectChanged();
2894 }
2895
sendScheduleToBeAdded(const ScheduleManager * sm,int row)2896 void Project::sendScheduleToBeAdded(const ScheduleManager *sm, int row)
2897 {
2898 emit scheduleToBeAdded(sm, row);
2899 }
2900
sendScheduleAdded(const MainSchedule * sch)2901 void Project::sendScheduleAdded(const MainSchedule *sch)
2902 {
2903 //debugPlan<<sch->id();
2904 emit scheduleAdded(sch);
2905 emit projectChanged();
2906 }
2907
sendScheduleToBeRemoved(const MainSchedule * sch)2908 void Project::sendScheduleToBeRemoved(const MainSchedule *sch)
2909 {
2910 //debugPlan<<sch->id();
2911 emit scheduleToBeRemoved(sch);
2912 }
2913
sendScheduleRemoved(const MainSchedule * sch)2914 void Project::sendScheduleRemoved(const MainSchedule *sch)
2915 {
2916 //debugPlan<<sch->id();
2917 emit scheduleRemoved(sch);
2918 emit projectChanged();
2919 }
2920
changed(Resource * resource)2921 void Project::changed(Resource *resource)
2922 {
2923 emit resourceChanged(resource);
2924 emit projectChanged();
2925 }
2926
changed(Calendar * cal)2927 void Project::changed(Calendar *cal)
2928 {
2929 emit calendarChanged(cal);
2930 emit projectChanged();
2931 }
2932
changed(StandardWorktime * w)2933 void Project::changed(StandardWorktime *w)
2934 {
2935 emit standardWorktimeChanged(w);
2936 emit projectChanged();
2937 }
2938
addRelation(Relation * rel,bool check)2939 bool Project::addRelation(Relation *rel, bool check)
2940 {
2941 if (rel->parent() == 0 || rel->child() == 0) {
2942 return false;
2943 }
2944 if (check && !legalToLink(rel->parent(), rel->child())) {
2945 return false;
2946 }
2947 emit relationToBeAdded(rel, rel->parent()->numDependChildNodes(), rel->child()->numDependParentNodes());
2948 rel->parent()->addDependChildNode(rel);
2949 rel->child()->addDependParentNode(rel);
2950 emit relationAdded(rel);
2951 emit projectChanged();
2952 return true;
2953 }
2954
takeRelation(Relation * rel)2955 void Project::takeRelation(Relation *rel)
2956 {
2957 emit relationToBeRemoved(rel);
2958 rel->parent() ->takeDependChildNode(rel);
2959 rel->child() ->takeDependParentNode(rel);
2960 emit relationRemoved(rel);
2961 emit projectChanged();
2962 }
2963
setRelationType(Relation * rel,Relation::Type type)2964 void Project::setRelationType(Relation *rel, Relation::Type type)
2965 {
2966 emit relationToBeModified(rel);
2967 rel->setType(type);
2968 emit relationModified(rel);
2969 emit projectChanged();
2970 }
2971
setRelationLag(Relation * rel,const Duration & lag)2972 void Project::setRelationLag(Relation *rel, const Duration &lag)
2973 {
2974 emit relationToBeModified(rel);
2975 rel->setLag(lag);
2976 emit relationModified(rel);
2977 emit projectChanged();
2978 }
2979
flatNodeList(Node * parent)2980 QList<Node*> Project::flatNodeList(Node *parent)
2981 {
2982 QList<Node*> lst;
2983 Node *p = parent == 0 ? this : parent;
2984 //debugPlan<<p->name()<<lst.count();
2985 foreach (Node *n, p->childNodeIterator()) {
2986 lst.append(n);
2987 if (n->numChildren() > 0) {
2988 lst += flatNodeList(n);
2989 }
2990 }
2991 return lst;
2992 }
2993
setSchedulerPlugins(const QMap<QString,SchedulerPlugin * > & plugins)2994 void Project::setSchedulerPlugins(const QMap<QString, SchedulerPlugin*> &plugins)
2995 {
2996 m_schedulerPlugins = plugins;
2997 debugPlan<<m_schedulerPlugins;
2998 }
2999
emitLocaleChanged()3000 void Project::emitLocaleChanged()
3001 {
3002 emit localeChanged();
3003 }
3004
useSharedResources() const3005 bool Project::useSharedResources() const
3006 {
3007 return m_useSharedResources;
3008 }
3009
setUseSharedResources(bool on)3010 void Project::setUseSharedResources(bool on)
3011 {
3012 m_useSharedResources = on;
3013 }
3014
isSharedResourcesLoaded() const3015 bool Project::isSharedResourcesLoaded() const
3016 {
3017 return m_sharedResourcesLoaded;
3018 }
3019
setSharedResourcesLoaded(bool on)3020 void Project::setSharedResourcesLoaded(bool on)
3021 {
3022 m_sharedResourcesLoaded = on;
3023 }
3024
setSharedResourcesFile(const QString & file)3025 void Project::setSharedResourcesFile(const QString &file)
3026 {
3027 m_sharedResourcesFile = file;
3028 }
3029
sharedResourcesFile() const3030 QString Project::sharedResourcesFile() const
3031 {
3032 return m_sharedResourcesFile;
3033 }
3034
setSharedProjectsUrl(const QUrl & url)3035 void Project::setSharedProjectsUrl(const QUrl &url)
3036 {
3037 m_sharedProjectsUrl = url;
3038 }
3039
sharedProjectsUrl() const3040 QUrl Project::sharedProjectsUrl() const
3041 {
3042 return m_sharedProjectsUrl;
3043 }
3044
setLoadProjectsAtStartup(bool value)3045 void Project::setLoadProjectsAtStartup(bool value)
3046 {
3047 m_loadProjectsAtStartup = value;
3048 }
3049
loadProjectsAtStartup() const3050 bool Project::loadProjectsAtStartup() const
3051 {
3052 return m_loadProjectsAtStartup;
3053 }
3054
taskModules(bool includeLocal) const3055 QList<QUrl> Project::taskModules(bool includeLocal) const
3056 {
3057 if (!includeLocal && m_useLocalTaskModules) {
3058 QList<QUrl> lst = m_taskModules;
3059 lst.removeAll(m_localTaskModulesPath);
3060 return lst;
3061 }
3062 return m_taskModules;
3063 }
3064
setTaskModules(const QList<QUrl> modules,bool useLocalTaskModules)3065 void Project::setTaskModules(const QList<QUrl> modules, bool useLocalTaskModules)
3066 {
3067 m_taskModules = modules;
3068 m_useLocalTaskModules = useLocalTaskModules;
3069 if (m_useLocalTaskModules && m_localTaskModulesPath.isValid()) {
3070 m_taskModules.prepend(m_localTaskModulesPath);
3071 }
3072 emit taskModulesChanged(m_taskModules);
3073 }
3074
useLocalTaskModules() const3075 bool Project::useLocalTaskModules() const
3076 {
3077 return m_useLocalTaskModules;
3078 }
3079
setUseLocalTaskModules(bool value,bool emitChanged)3080 void Project::setUseLocalTaskModules(bool value, bool emitChanged)
3081 {
3082 if (m_useLocalTaskModules) {
3083 m_taskModules.removeAll(m_localTaskModulesPath);
3084 }
3085 m_useLocalTaskModules = value;
3086 if (m_useLocalTaskModules && m_localTaskModulesPath.isValid()) {
3087 m_taskModules.prepend(m_localTaskModulesPath);
3088 }
3089 if (emitChanged) {
3090 emit taskModulesChanged(m_taskModules);
3091 }
3092 }
3093
setLocalTaskModulesPath(const QUrl & url)3094 void Project::setLocalTaskModulesPath(const QUrl &url)
3095 {
3096 m_taskModules.removeAll(m_localTaskModulesPath);
3097 m_localTaskModulesPath = url;
3098 if (m_useLocalTaskModules && url.isValid()) {
3099 m_taskModules.prepend(url);
3100 }
3101 emit taskModulesChanged(m_taskModules);
3102 }
3103
3104 } //KPlato namespace
3105