1 /* This file is part of the KDE project
2    Copyright (C) 2001 Thomas zander <zander@kde.org>
3    Copyright (C) 2002 - 2010, 2012 Dag Andersen <danders@get2net.dk>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19 */
20 // clazy:excludeall=qstring-arg
21 #include "kptnode.h"
22 
23 #include "kptappointment.h"
24 #include "kptaccount.h"
25 #include "kptwbsdefinition.h"
26 #include "kptresource.h"
27 #include "kptschedule.h"
28 #include "kptxmlloaderobject.h"
29 #include "XmlSaveContext.h"
30 #include "kptdebug.h"
31 
32 #include <KoXmlReader.h>
33 
34 #include <KLocalizedString>
35 
36 #include <QListIterator>
37 
38 
39 namespace KPlato
40 {
41 
Node(Node * parent)42 Node::Node(Node *parent)
43     : QObject(0), // We don't use qobjects parent
44       m_nodes(), m_dependChildNodes(), m_dependParentNodes(),
45       m_estimate(0),
46       m_blockChanged(false)
47 {
48     //debugPlan<<"("<<this<<")";
49     m_parent = parent;
50     init();
51     m_id.clear(); // Not mapped
52 }
53 
Node(const Node & node,Node * parent)54 Node::Node(const Node &node, Node *parent)
55     : QObject(0), // Don't set parent, we handle parent/child ourselves
56       m_nodes(),
57       m_dependChildNodes(),
58       m_dependParentNodes(),
59       m_estimate(0),
60       m_blockChanged(false)
61 {
62     //debugPlan<<"("<<this<<")";
63     m_parent = parent;
64     init();
65     m_name = node.name();
66     m_leader = node.leader();
67     m_description = node.description();
68     m_constraint = (ConstraintType) node.constraint();
69     m_constraintStartTime = node.constraintStartTime();
70     m_constraintEndTime = node.constraintEndTime();
71 
72     m_runningAccount = node.runningAccount();
73     m_startupAccount = node.startupAccount();
74     m_shutdownAccount = node.shutdownAccount();
75 
76     m_startupCost = node.startupCost();
77     m_shutdownCost = node.shutdownCost();
78 
79     setObjectName(node.objectName());
80 }
81 
~Node()82 Node::~Node() {
83     //debugPlan<<"("<<this<<")"<<m_name;
84     delete m_estimate;
85     while (!m_nodes.isEmpty())
86         delete m_nodes.takeFirst();
87 
88     if (findNode() == this) {
89         removeId(); // only remove myself (I may be just a working copy)
90     }
91     while (!m_dependParentNodes.isEmpty()) {
92         delete m_dependParentNodes.value(0);
93     }
94     while (!m_dependChildNodes.isEmpty()) {
95         delete m_dependChildNodes.value(0);
96     }
97     if (m_runningAccount)
98         m_runningAccount->removeRunning(*this);
99     if (m_startupAccount)
100         m_startupAccount->removeStartup(*this);
101     if (m_shutdownAccount)
102         m_shutdownAccount->removeShutdown(*this);
103 
104     foreach (Schedule *s, m_schedules) {
105         delete s;
106     }
107     m_schedules.clear();
108     m_parent = 0; //safety
109 }
110 
init()111 void Node::init() {
112     m_priority = 0;
113     m_documents.node = this;
114     m_currentSchedule = 0;
115     m_name="";
116     m_constraint = Node::ASAP;
117     m_estimate = 0;
118     m_visitedForward = false;
119     m_visitedBackward = false;
120 
121     m_runningAccount = 0;
122     m_startupAccount = 0;
123     m_shutdownAccount = 0;
124     m_startupCost = 0.0;
125     m_shutdownCost = 0.0;
126 }
127 
setPriority(int priority)128 void Node::setPriority(int priority)
129 {
130     m_priority = priority;
131     changed(PriorityProperty);
132 }
133 
priority() const134 int Node::priority() const
135 {
136     return m_priority;
137 }
138 
typeToString(bool trans) const139 QString Node::typeToString(bool trans) const
140 {
141     return typeToString((Node::NodeTypes)type(), trans);
142 }
143 
144 // static
typeToString(Node::NodeTypes typ,bool trans)145 QString Node::typeToString(Node::NodeTypes typ, bool trans)
146 {
147     return typeToStringList(trans).at(typ);
148 }
149 
150 // static
typeToStringList(bool trans)151 QStringList Node::typeToStringList(bool trans)
152 {
153     return QStringList()
154             << (trans ? i18n("None") : QString("None"))
155             << (trans ? i18n("Project") : QString("Project"))
156             << (trans ? i18n("Sub-Project") : QString("Sub-Project"))
157             << (trans ? i18n("Task") : QString("Task"))
158             << (trans ? i18n("Milestone") : QString("Milestone"))
159             << (trans ? i18n("Periodic") : QString("Periodic"))
160             << (trans ? i18n("Summary") : QString("Summary-Task"));
161 }
162 
setName(const QString & n)163 void Node::setName(const QString &n)
164 {
165 #ifndef NDEBUG
166     setObjectName(n);
167 #endif
168      m_name = n;
169      changed(this, NameProperty);
170 }
171 
setLeader(const QString & l)172 void Node::setLeader(const QString &l)
173 {
174      m_leader = l;
175      changed(this, LeaderProperty);
176 }
177 
setDescription(const QString & d)178 void Node::setDescription(const QString &d)
179 {
180      m_description = d;
181      changed(this, DescriptionProperty);
182 }
183 
projectNode()184 Node *Node::projectNode() {
185     if ((type() == Type_Project) || (type() == Type_Subproject)) {
186         return this;
187     }
188     if (m_parent)
189         return m_parent->projectNode();
190 
191     // This happens for default tasks
192     return 0;
193 }
194 
projectNode() const195 const Node *Node::projectNode() const {
196     if ((type() == Type_Project) || (type() == Type_Subproject)) {
197         return this;
198     }
199     if (m_parent)
200         return m_parent->projectNode();
201 
202     // This happens for default tasks
203     return 0;
204 }
205 
takeChildNode(Node * node)206 void Node::takeChildNode(Node *node) {
207     //debugPlan<<"find="<<m_nodes.indexOf(node);
208     int t = type();
209     int i = m_nodes.indexOf(node);
210     if (i != -1) {
211         m_nodes.removeAt(i);
212     }
213     node->setParentNode(0);
214     if (t != type()) {
215         changed(TypeProperty);
216     }
217 }
218 
takeChildNode(int number)219 void Node::takeChildNode(int number) {
220     int t = type();
221     if (number >= 0 && number < m_nodes.size()) {
222         Node *n = m_nodes.takeAt(number);
223         //debugPlan<<(n?n->id():"null")<<" :"<<(n?n->name():"");
224         if (n) {
225             n->setParentNode(0);
226         }
227     }
228     if (t != type()) {
229         changed(TypeProperty);
230     }
231 }
232 
insertChildNode(int index,Node * node)233 void Node::insertChildNode(int index, Node *node) {
234     int t = type();
235     if (index == -1)
236         m_nodes.append(node);
237     else
238         m_nodes.insert(index,node);
239     node->setParentNode(this);
240     if (t != type()) {
241         changed(TypeProperty);
242     }
243 }
244 
addChildNode(Node * node,Node * after)245 void Node::addChildNode(Node *node, Node *after) {
246     int t = type();
247     int index = m_nodes.indexOf(after);
248     if (index == -1) {
249         m_nodes.append(node);
250         node->setParentNode(this);
251         if (t != type()) {
252             changed(TypeProperty);
253         }
254         return;
255     }
256     m_nodes.insert(index+1, node);
257     node->setParentNode(this);
258     if (t != type()) {
259         changed(TypeProperty);
260     }
261 }
262 
findChildNode(const Node * node) const263 int Node::findChildNode(const Node* node) const
264 {
265     return m_nodes.indexOf(const_cast<Node*>(node));
266 }
267 
isChildOf(const Node * node) const268 bool Node::isChildOf(const Node* node) const
269 {
270     if (node == 0 || m_parent == 0) {
271         return false;
272     }
273     if (node == m_parent) {
274         return true;
275     }
276     return m_parent->isChildOf(node);
277 }
278 
279 
childNode(int number)280 Node* Node::childNode(int number)
281 {
282     //debugPlan<<number;
283     return m_nodes.value(number);
284 }
285 
childNode(int number) const286 const Node* Node::childNode(int number) const
287 {
288     if (number < 0 || number >= m_nodes.count()) {
289         return 0;
290     }
291     return m_nodes.at(number);
292 }
293 
indexOf(const Node * node) const294 int Node::indexOf(const Node *node) const
295 {
296     return m_nodes.indexOf(const_cast<Node*>(node));
297 }
298 
299 
getDelay()300 Duration *Node::getDelay() {
301     /* TODO
302        Calculate the delay of this node. Use the calculated startTime and the set startTime.
303     */
304     return 0L;
305 }
306 
addDependChildNode(Node * node,Relation::Type p)307 void Node::addDependChildNode(Node *node, Relation::Type p) {
308     addDependChildNode(node,p,Duration());
309 }
310 
addDependChildNode(Node * node,Relation::Type p,Duration lag)311 void Node::addDependChildNode(Node *node, Relation::Type p, Duration lag) {
312     Relation *relation = new Relation(this, node, p, lag);
313     if (node->addDependParentNode(relation))
314         m_dependChildNodes.append(relation);
315     else
316         delete relation;
317 }
318 
insertDependChildNode(unsigned int index,Node * node,Relation::Type p)319 void Node::insertDependChildNode(unsigned int index, Node *node, Relation::Type p) {
320     Relation *relation = new Relation(this, node, p, Duration());
321     if (node->addDependParentNode(relation))
322         m_dependChildNodes.insert(index, relation);
323     else
324         delete relation;
325 }
326 
addDependChildNode(Relation * relation)327 bool Node::addDependChildNode(Relation *relation) {
328     if(m_dependChildNodes.indexOf(relation) != -1)
329         return false;
330     m_dependChildNodes.append(relation);
331     return true;
332 }
333 
takeDependChildNode(Relation * rel)334 void Node::takeDependChildNode(Relation *rel) {
335     int i = m_dependChildNodes.indexOf(rel);
336     if (i != -1) {
337         //debugPlan<<m_name<<": ("<<rel<<")";
338         m_dependChildNodes.removeAt(i);
339     }
340 }
341 
addDependParentNode(Node * node,Relation::Type p)342 void Node::addDependParentNode(Node *node, Relation::Type p) {
343     addDependParentNode(node,p,Duration());
344 }
345 
addDependParentNode(Node * node,Relation::Type p,Duration lag)346 void Node::addDependParentNode(Node *node, Relation::Type p, Duration lag) {
347     Relation *relation = new Relation(node, this, p, lag);
348     if (node->addDependChildNode(relation))
349         m_dependParentNodes.append(relation);
350     else
351         delete relation;
352 }
353 
insertDependParentNode(unsigned int index,Node * node,Relation::Type p)354 void Node::insertDependParentNode(unsigned int index, Node *node, Relation::Type p) {
355     Relation *relation = new Relation(this, node, p, Duration());
356     if (node->addDependChildNode(relation))
357         m_dependParentNodes.insert(index,relation);
358     else
359         delete relation;
360 }
361 
addDependParentNode(Relation * relation)362 bool Node::addDependParentNode(Relation *relation) {
363     if(m_dependParentNodes.indexOf(relation) != -1)
364         return false;
365     m_dependParentNodes.append(relation);
366     return true;
367 }
368 
takeDependParentNode(Relation * rel)369 void Node::takeDependParentNode(Relation *rel) {
370     int i = m_dependParentNodes.indexOf(rel);
371     if (i != -1) {
372         //debugPlan<<m_name<<": ("<<rel<<")";
373         m_dependParentNodes.removeAt(i);
374     }
375 }
376 
isParentOf(const Node * node) const377 bool Node::isParentOf(const Node *node) const
378 {
379     if (m_nodes.indexOf(const_cast<Node*>(node)) != -1)
380         return true;
381 
382     QListIterator<Node*> nit(childNodeIterator());
383     while (nit.hasNext()) {
384         if (nit.next()->isParentOf(node))
385             return true;
386     }
387     return false;
388 }
389 
findParentRelation(const Node * node) const390 Relation *Node::findParentRelation(const Node *node) const
391 {
392     for (int i=0; i<numDependParentNodes(); i++) {
393         Relation *rel = getDependParentNode(i);
394         if (rel->parent() == node)
395             return rel;
396     }
397     return (Relation *)0;
398 }
399 
findChildRelation(const Node * node) const400 Relation *Node::findChildRelation(const Node *node) const
401 {
402     for (int i=0; i<numDependChildNodes(); i++) {
403         Relation *rel = getDependChildNode(i);
404         if (rel->child() == node)
405             return rel;
406     }
407     return (Relation *)0;
408 }
409 
findRelation(const Node * node) const410 Relation *Node::findRelation(const Node *node) const
411 {
412     Relation *rel = findParentRelation(node);
413     if (!rel)
414         rel = findChildRelation(node);
415     return rel;
416 }
417 
isDependChildOf(const Node * node) const418 bool Node::isDependChildOf(const Node *node) const
419 {
420     //debugPlan<<" '"<<m_name<<"' checking against '"<<node->name()<<"'";
421     for (int i=0; i<numDependParentNodes(); i++) {
422         Relation *rel = getDependParentNode(i);
423         if (rel->parent() == node)
424             return true;
425         if (rel->parent()->isDependChildOf(node))
426             return true;
427     }
428     return false;
429 }
430 
getParentNodes()431 QList<Node*> Node::getParentNodes()
432 {
433     this->m_parentNodes.clear();
434     foreach(Relation * currentRelation, this->dependParentNodes())
435     {
436         if (!this->m_parentNodes.contains(currentRelation->parent()))
437         {
438             this->m_parentNodes.append(currentRelation->parent());
439         }
440     }
441     return this->m_parentNodes;
442 }
443 
canMoveTo(const Node * newParent) const444 bool Node::canMoveTo(const Node *newParent) const
445 {
446     if (m_parent == newParent) {
447         return true;
448     }
449     if (newParent->isChildOf(this)) {
450         return false;
451     }
452     if (isDependChildOf(newParent) || newParent->isDependChildOf(this)) {
453         debugPlan<<"Can't move, node is dependent on new parent";
454         return false;
455     }
456     foreach (Node *n, m_nodes) {
457         if (!n->canMoveTo(newParent)) {
458             return false;
459         }
460     }
461     return true;
462 }
463 
makeAppointments()464 void Node::makeAppointments() {
465     QListIterator<Node*> nit(m_nodes);
466     while (nit.hasNext()) {
467         nit.next()->makeAppointments();
468     }
469 }
470 
calcResourceOverbooked()471 void Node::calcResourceOverbooked() {
472     QListIterator<Node*> nit(m_nodes);
473     while (nit.hasNext()) {
474         nit.next()->calcResourceOverbooked();
475     }
476 }
477 
478 // Returns the (previously) calculated duration
duration(long id) const479 Duration Node::duration(long id) const
480 {
481     Schedule *s = schedule(id);
482     return s ? s->duration : Duration::zeroDuration;
483 }
484 
variance(long id,Duration::Unit unit) const485 double Node::variance(long id, Duration::Unit unit) const
486 {
487     double d = deviation(id, unit);
488     return d * d;
489 }
490 
deviation(long id,Duration::Unit unit) const491 double Node::deviation(long id, Duration::Unit unit) const
492 {
493     Schedule *s = schedule(id);
494     double d = 0.0;
495     if (s && m_estimate) {
496         d = s->duration.toDouble(unit);
497         double o = (d *  (100 + m_estimate->optimisticRatio())) / 100;
498         double p = (d * (100 + m_estimate->pessimisticRatio())) / 100;
499         d =  (p - o) / 6;
500     }
501     return d;
502 }
503 
startTime(long id) const504 DateTime Node::startTime(long id) const
505 {
506     Schedule *s = schedule(id);
507     return s ? s->startTime : DateTime();
508 }
endTime(long id) const509 DateTime Node::endTime(long id) const
510 {
511     Schedule *s = schedule(id);
512     return s ? s->endTime : DateTime();
513 }
514 
appointmentStartTime(long id) const515 DateTime Node::appointmentStartTime(long id) const
516 {
517     Schedule *s = schedule(id);
518     return s ? s->appointmentStartTime() : DateTime();
519 }
appointmentEndTime(long id) const520 DateTime Node::appointmentEndTime(long id) const
521 {
522     Schedule *s = schedule(id);
523     return s ? s->appointmentEndTime() : DateTime();
524 }
525 
setDuration(const Duration & duration,long id)526 void Node::setDuration(const Duration &duration, long id)
527 {
528     Schedule *s = schedule(id);
529     if (s) {
530         s->duration = duration;
531     }
532 }
533 
setEarlyStart(const DateTime & dt,long id)534 void Node::setEarlyStart(const DateTime &dt, long id)
535 {
536     Schedule *s = schedule(id);
537     if (s) s->earlyStart = dt;
538 }
539 
earlyStart(long id) const540 DateTime Node::earlyStart(long id) const
541 {
542     Schedule *s = schedule(id);
543     return s ? s->earlyStart : DateTime();
544 }
545 
setLateStart(const DateTime & dt,long id)546 void Node::setLateStart(const DateTime &dt, long id)
547 {
548     Schedule *s = schedule(id);
549     if (s) s->lateStart = dt;
550 }
551 
lateStart(long id) const552 DateTime Node::lateStart(long id) const
553 {
554     Schedule *s = schedule(id);
555     return s ? s->lateStart : DateTime();
556 }
557 
setEarlyFinish(const DateTime & dt,long id)558 void Node::setEarlyFinish(const DateTime &dt, long id)
559 {
560     Schedule *s = schedule(id);
561     if (s) s->earlyFinish = dt;
562 }
563 
earlyFinish(long id) const564 DateTime Node::earlyFinish(long id) const
565 {
566     Schedule *s = schedule(id);
567     return s ? s->earlyFinish : DateTime();
568 }
569 
setLateFinish(const DateTime & dt,long id)570 void Node::setLateFinish(const DateTime &dt, long id)
571 {
572     Schedule *s = schedule(id);
573     if (s) s->lateFinish = dt;
574 }
575 
lateFinish(long id) const576 DateTime Node::lateFinish(long id) const
577 {
578     Schedule *s = schedule(id);
579     return s ? s->lateFinish : DateTime();
580 }
581 
workStartTime(long id) const582 DateTime Node::workStartTime(long id) const
583 {
584     Schedule *s = schedule(id);
585     return s ? s->workStartTime : DateTime();
586 }
587 
setWorkStartTime(const DateTime & dt,long id)588 void Node::setWorkStartTime(const DateTime &dt, long id)
589 {
590     Schedule *s = schedule(id);
591     if (s) s->workStartTime = dt;
592 }
593 
workEndTime(long id) const594 DateTime Node::workEndTime(long id) const
595 {
596     Schedule *s = schedule(id);
597     return s ? s->workEndTime : DateTime();
598 }
599 
setWorkEndTime(const DateTime & dt,long id)600 void Node::setWorkEndTime(const DateTime &dt, long id)
601 {
602     Schedule *s = schedule(id);
603     if (s) s->workEndTime = dt;
604 }
605 
inCriticalPath(long id) const606 bool Node::inCriticalPath(long id) const
607 {
608     Schedule *s = schedule(id);
609     return s ? s->inCriticalPath : false;
610 }
611 
schedulingStatus(long id,bool trans) const612 QStringList Node::schedulingStatus(long id, bool trans) const
613 {
614     Schedule *s = schedule(id);
615     QStringList lst;
616     if (s) {
617         lst = s->state();
618     }
619     if (lst.isEmpty()) {
620         lst.append(trans ? i18n("Not scheduled") : QString("Not scheduled"));
621     }
622     return lst;
623 }
624 
resourceError(long id) const625 bool Node::resourceError(long id) const
626 {
627     Schedule *s = schedule(id);
628     return s ? s->resourceError : false;
629 }
630 
resourceOverbooked(long id) const631 bool Node::resourceOverbooked(long id) const
632 {
633     Schedule *s = schedule(id);
634     return s ? s->resourceOverbooked : false;
635 }
636 
resourceNotAvailable(long id) const637 bool Node::resourceNotAvailable(long id) const
638 {
639     Schedule *s = schedule(id);
640     return s ? s->resourceNotAvailable : false;
641 }
642 
constraintError(long id) const643 bool Node::constraintError(long id) const
644 {
645     Schedule *s = schedule(id);
646     return s ? s->constraintError : false;
647 }
648 
schedulingError(long id) const649 bool Node::schedulingError(long id) const
650 {
651     Schedule *s = schedule(id);
652     return s ? s->schedulingError : false;
653 }
654 
notScheduled(long id) const655 bool Node::notScheduled(long id) const
656 {
657     if (type() == Type_Summarytask) {
658         // i am scheduled if al least on child is scheduled
659         foreach (Node *n, m_nodes) {
660             if (! n->notScheduled(id)) {
661                 return false;
662             }
663         }
664         return true;
665     }
666     Schedule *s = schedule(id);
667     return s == 0 || s->isDeleted() || s->notScheduled;
668 }
669 
overbookedResources(long id) const670 QStringList Node::overbookedResources(long id) const
671 {
672     Schedule *s = schedule(id);
673     return s ? s->overbookedResources() : QStringList();
674 }
675 
saveWorkPackageXML(QDomElement &,long) const676 void Node::saveWorkPackageXML(QDomElement &, long) const
677 {
678     return;
679 }
680 
saveRelations(QDomElement & element,const XmlSaveContext & context) const681 void Node::saveRelations(QDomElement &element, const XmlSaveContext &context) const
682 {
683     QListIterator<Relation*> it(m_dependChildNodes);
684     while (it.hasNext()) {
685         Relation *r = it.next();
686         if (context.saveNode(r->child())) {
687             r->save(element, context);
688         }
689     }
690     QListIterator<Node*> nodes(m_nodes);
691     while (nodes.hasNext()) {
692         nodes.next()->saveRelations(element, context);
693     }
694 }
695 
setConstraint(Node::ConstraintType type)696 void Node::setConstraint(Node::ConstraintType type)
697 {
698     m_constraint = type;
699     changed(this, ConstraintTypeProperty);
700 }
701 
setConstraint(const QString & type)702 void Node::setConstraint(const QString &type) {
703     // Do not i18n these, they are used in load()
704     if (type == "ASAP")
705         setConstraint(ASAP);
706     else if (type == "ALAP")
707         setConstraint(ALAP);
708     else if (type == "MustStartOn")
709         setConstraint(MustStartOn);
710     else if (type == "MustFinishOn")
711         setConstraint(MustFinishOn);
712     else if (type == "StartNotEarlier")
713         setConstraint(StartNotEarlier);
714     else if (type == "FinishNotLater")
715         setConstraint(FinishNotLater);
716     else if (type == "FixedInterval")
717         setConstraint(FixedInterval);
718     else
719         setConstraint(ASAP);  // default
720 }
721 
constraintToString(bool trans) const722 QString Node::constraintToString(bool trans) const {
723     return constraintList(trans).at(m_constraint);
724 }
725 
constraintList(bool trans)726 QStringList Node::constraintList(bool trans) {
727     // keep theses in the same order as the enum!
728     return QStringList()
729             << (trans ? i18n("As Soon As Possible") : QString("ASAP"))
730             << (trans ? i18n("As Late As Possible") : QString("ALAP"))
731             << (trans ? i18n("Must Start On") : QString("MustStartOn"))
732             << (trans ? i18n("Must Finish On") : QString("MustFinishOn"))
733             << (trans ? i18n("Start Not Earlier") : QString("StartNotEarlier"))
734             << (trans ? i18n("Finish Not Later") : QString("FinishNotLater"))
735             << (trans ? i18n("Fixed Interval") : QString("FixedInterval"));
736 }
737 
propagateEarliestStart(DateTime & time)738 void Node::propagateEarliestStart(DateTime &time) {
739     if (m_currentSchedule == 0) {
740         return;
741     }
742     if (type() != Type_Project) {
743         m_currentSchedule->earlyStart = time;
744         if (m_currentSchedule->lateStart.isValid() && m_currentSchedule->lateStart < time) {
745             m_currentSchedule->lateStart = time;
746         }
747         //m_currentSchedule->logDebug("propagateEarliestStart: " + time.toString());
748         switch (m_constraint) {
749             case FinishNotLater:
750             case MustFinishOn:
751                 if (m_constraintEndTime < time) {
752                     m_currentSchedule->logWarning("Task constraint outside project constraint");
753 #ifndef PLAN_NLOGDEBUG
754                     m_currentSchedule->logDebug(QString("%1: end constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
755 #endif
756                 }
757                 break;
758             case MustStartOn:
759             case FixedInterval:
760                 if (m_constraintStartTime < time) {
761                     m_currentSchedule->logWarning("Task constraint outside project constraint");
762 #ifndef PLAN_NLOGDEBUG
763                     m_currentSchedule->logDebug(QString("%1: start constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
764 #endif
765                 }
766                 break;
767             default:
768                 break;
769         }
770     }
771     //debugPlan<<m_name<<":"<<m_currentSchedule->earlyStart;
772     QListIterator<Node*> it = m_nodes;
773     while (it.hasNext()) {
774         it.next()->propagateEarliestStart(time);
775     }
776 }
777 
propagateLatestFinish(DateTime & time)778 void Node::propagateLatestFinish(DateTime &time) {
779     if (m_currentSchedule == 0) {
780         return;
781     }
782     if (type() != Type_Project) {
783         m_currentSchedule->lateFinish = time;
784         if (m_currentSchedule->earlyFinish.isValid() && m_currentSchedule->earlyFinish > time) {
785             m_currentSchedule->earlyFinish = time;
786         }
787         switch (m_constraint) {
788             case StartNotEarlier:
789             case MustStartOn:
790                 if (m_constraintStartTime > time) {
791                     m_currentSchedule->logWarning("Task constraint outside project constraint");
792 #ifndef PLAN_NLOGDEBUG
793                     m_currentSchedule->logDebug(QString("%1: start constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
794 #endif
795                 }
796                 break;
797             case MustFinishOn:
798             case FixedInterval:
799                 if (m_constraintEndTime > time) {
800                     m_currentSchedule->logWarning("Task constraint outside project constraint");
801 #ifndef PLAN_NLOGDEBUG
802                     m_currentSchedule->logDebug(QString("%1: end constraint %2 > %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString()));
803 #endif
804                 }
805                 break;
806             default:
807                 break;
808         }
809     }
810     //debugPlan<<m_name<<":"<<m_currentSchedule->lateFinish;
811     QListIterator<Node*> it = m_nodes;
812     while (it.hasNext()) {
813         it.next()->propagateLatestFinish(time);
814     }
815 }
816 
moveEarliestStart(DateTime & time)817 void Node::moveEarliestStart(DateTime &time) {
818     if (m_currentSchedule == 0)
819         return;
820     if (m_currentSchedule->earlyStart < time) {
821         //m_currentSchedule->logDebug("moveEarliestStart: " + m_currentSchedule->earlyStart.toString() + " -> " + time.toString());
822         m_currentSchedule->earlyStart = time;
823     }
824     QListIterator<Node*> it = m_nodes;
825     while (it.hasNext()) {
826         it.next()->moveEarliestStart(time);
827     }
828 }
829 
moveLatestFinish(DateTime & time)830 void Node::moveLatestFinish(DateTime &time) {
831     if (m_currentSchedule == 0)
832         return;
833     if (m_currentSchedule->lateFinish > time)
834         m_currentSchedule->lateFinish = time;
835     QListIterator<Node*> it = m_nodes;
836     while (it.hasNext()) {
837         it.next()->moveLatestFinish(time);
838     }
839 }
840 
initiateCalculation(MainSchedule & sch)841 void Node::initiateCalculation(MainSchedule &sch) {
842     m_visitedForward = false;
843     m_visitedBackward = false;
844     m_durationForward = Duration::zeroDuration;
845     m_durationBackward = Duration::zeroDuration;
846     m_earlyStart = DateTime();
847     m_earlyFinish = DateTime();
848     m_lateFinish = DateTime();
849 
850     QListIterator<Node*> it = m_nodes;
851     while (it.hasNext()) {
852         it.next()->initiateCalculation(sch);
853     }
854 }
855 
resetVisited()856 void Node::resetVisited() {
857     m_visitedForward = false;
858     m_visitedBackward = false;
859     QListIterator<Node*> it = m_nodes;
860     while (it.hasNext()) {
861         it.next()->resetVisited();
862     }
863 }
864 
siblingBefore()865 Node *Node::siblingBefore() {
866     //debugPlan;
867     if (parentNode())
868         return parentNode()->childBefore(this);
869     return 0;
870 }
871 
childBefore(Node * node)872 Node *Node::childBefore(Node *node) {
873     //debugPlan;
874     int index = m_nodes.indexOf(node);
875     if (index > 0){
876         return m_nodes.at(index-1);
877     }
878     return 0;
879 }
880 
siblingAfter()881 Node *Node::siblingAfter() {
882     //debugPlan;
883     if (parentNode())
884         return parentNode()->childAfter(this);
885     return 0;
886 }
887 
childAfter(Node * node)888 Node *Node::childAfter(Node *node)
889 {
890     //debugPlan;
891     Q_ASSERT(m_nodes.contains(node));
892     int index = m_nodes.indexOf(node);
893     if (index < m_nodes.count()-1) {
894         return m_nodes.at(index+1);
895     }
896     return 0;
897 }
898 
moveChildUp(Node * node)899 bool Node::moveChildUp(Node* node)
900 {
901     if (findChildNode(node) == -1)
902         return false; // not my node!
903     Node *sib = node->siblingBefore();
904     if (!sib)
905         return false;
906     sib = sib->siblingBefore();
907     takeChildNode(node);
908     if (sib) {
909         addChildNode(node, sib);
910     } else {
911         insertChildNode(0, node);
912     }
913     return true;
914 }
915 
moveChildDown(Node * node)916 bool Node::moveChildDown(Node* node)
917 {
918     if (findChildNode(node) == -1)
919         return false; // not my node!
920     Node *sib = node->siblingAfter();
921     if (!sib)
922         return false;
923     takeChildNode(node);
924     addChildNode(node, sib);
925     return true;
926 }
927 
legalToLink(const Node * node) const928 bool Node::legalToLink(const Node *node) const
929 {
930     Node *p = const_cast<Node*>(this)->projectNode();
931     if (p)
932         return p->legalToLink(this, node);
933     return false;
934 }
935 
isEndNode() const936 bool Node::isEndNode() const {
937     return m_dependChildNodes.isEmpty();
938 }
isStartNode() const939 bool Node::isStartNode() const {
940     return m_dependParentNodes.isEmpty();
941 }
942 
setId(const QString & id)943 void Node::setId(const QString& id) {
944     //debugPlan<<id;
945     m_id = id;
946 }
947 
setStartTime(const DateTime & startTime,long id)948 void Node::setStartTime(const DateTime &startTime, long id)
949 {
950     Schedule *s = schedule(id);
951     if (s)
952         s->startTime = startTime;
953 }
954 
setEndTime(const DateTime & endTime,long id)955 void Node::setEndTime(const DateTime &endTime, long id)
956 {
957     Schedule *s = schedule(id);
958     if (s)
959         s->endTime = endTime;
960 }
961 
saveAppointments(QDomElement & element,long id) const962 void Node::saveAppointments(QDomElement &element, long id) const {
963     //debugPlan<<m_name<<" id="<<id;
964     QListIterator<Node*> it(m_nodes);
965     while (it.hasNext()) {
966         it.next()->saveAppointments(element, id);
967     }
968 }
969 
appointments(long id)970 QList<Appointment*> Node::appointments(long id)
971 {
972     Schedule *s = schedule(id);
973     QList<Appointment*> lst;
974     if (s)
975         lst = s->appointments();
976     return lst;
977 }
978 
assignedResources(long id) const979 QList<Resource*> Node::assignedResources(long id) const {
980     Schedule *s = schedule(id);
981     QList<Resource*> res;
982     if (s) {
983         foreach (Appointment *a, s->appointments()) {
984             res << a->resource()->resource();
985         }
986     }
987     return res;
988 }
989 
990 
991 // Appointment *Node::findAppointment(Resource *resource) {
992 //     if (m_currentSchedule)
993 //         return m_currentSchedule->findAppointment(resource);
994 //     return 0;
995 // }
996 // bool Node::addAppointment(Appointment *appointment) {
997 //     if (m_currentSchedule)
998 //         return m_currentSchedule->add(appointment);
999 //     return false;
1000 // }
1001 //
1002 // called from resource side when resource adds appointment
addAppointment(Appointment * appointment,Schedule & main)1003 bool Node::addAppointment(Appointment *appointment, Schedule &main) {
1004     Schedule *s = findSchedule(main.id());
1005     if (s == 0) {
1006         s = createSchedule(&main);
1007     }
1008     appointment->setNode(s);
1009     //debugPlan<<this<<":"<<appointment<<","<<s<<","<<s->id()<<","<<main.id();
1010     return s->add(appointment);
1011 }
1012 
addAppointment(ResourceSchedule * resource,const DateTime & start,const DateTime & end,double load)1013 void Node::addAppointment(ResourceSchedule *resource, const DateTime &start, const DateTime &end, double load) {
1014     Schedule *node = findSchedule(resource->id());
1015     if (node == 0) {
1016         node = createSchedule(resource->parent());
1017     }
1018     node->setCalculationMode(resource->calculationMode());
1019     node->addAppointment(resource, start, end, load);
1020 }
1021 
isBaselined(long id) const1022 bool Node::isBaselined(long id) const
1023 {
1024     Schedule *s = schedule(id);
1025     return s ? s->isBaselined() : false;
1026 }
1027 
takeSchedule(const Schedule * schedule)1028 void Node::takeSchedule(const Schedule *schedule) {
1029     if (schedule == 0)
1030         return;
1031     if (m_currentSchedule == schedule)
1032         m_currentSchedule = 0;
1033     m_schedules.take(schedule->id());
1034 }
1035 
addSchedule(Schedule * schedule)1036 void Node::addSchedule(Schedule *schedule) {
1037     if (schedule == 0)
1038         return;
1039     m_schedules.insert(schedule->id(), schedule);
1040 }
1041 
createSchedule(const QString & name,Schedule::Type type,long id)1042 Schedule *Node::createSchedule(const QString& name, Schedule::Type type, long id) {
1043     //debugPlan<<name<<" type="<<type<<" id="<<(int)id;
1044     NodeSchedule *sch = new NodeSchedule(this, name, type, id);
1045     addSchedule(sch);
1046     return sch;
1047 }
1048 
createSchedule(Schedule * parent)1049 Schedule *Node::createSchedule(Schedule *parent) {
1050     //debugPlan<<name<<" type="<<type<<" id="<<(int)id;
1051     NodeSchedule *sch = new NodeSchedule(parent, this);
1052     addSchedule(sch);
1053     return sch;
1054 }
1055 
schedule(long id) const1056 Schedule *Node::schedule(long id) const
1057 {
1058     switch (id) {
1059         case ANYSCHEDULED: {
1060             foreach (Schedule *s, m_schedules) {
1061                 if (s->isScheduled()) {
1062                     return s;
1063                 }
1064             }
1065             return 0;
1066         }
1067         case CURRENTSCHEDULE:
1068             return m_currentSchedule;
1069         case NOTSCHEDULED:
1070             return 0;
1071         case BASELINESCHEDULE: {
1072             foreach (Schedule *s, m_schedules) {
1073                 if (s->isBaselined()) {
1074                     return s;
1075                 }
1076             }
1077             return 0;
1078         }
1079         default:
1080             break;
1081     }
1082     return findSchedule(id);
1083 }
1084 
findSchedule(long id) const1085 Schedule *Node::findSchedule(long id) const
1086 {
1087     return m_schedules.value(id);
1088 }
1089 
findSchedule(const QString & name,const Schedule::Type type)1090 Schedule *Node::findSchedule(const QString &name, const Schedule::Type type) {
1091     QHash<long, Schedule*> it;
1092     foreach (Schedule *sch, it) {
1093         if (!sch->isDeleted() &&
1094             sch->name() == name && sch->type() == type)
1095             return sch;
1096     }
1097     return 0;
1098 }
1099 
findSchedule(const QString & name)1100 Schedule *Node::findSchedule(const QString &name) {
1101     foreach (Schedule *sch, m_schedules) {
1102         if (!sch->isDeleted() && sch->name() == name)
1103             return sch;
1104     }
1105     return 0;
1106 }
1107 
1108 
findSchedule(const Schedule::Type type)1109 Schedule *Node::findSchedule(const Schedule::Type type) {
1110     //debugPlan<<m_name<<" find type="<<type<<" nr="<<m_schedules.count();
1111     QHash<long, Schedule*> hash;
1112     foreach (Schedule *sch, hash) {
1113         if (!sch->isDeleted() && sch->type() == type) {
1114             return sch;
1115         }
1116     }
1117     return 0;
1118 }
1119 
setScheduleDeleted(long id,bool on)1120 void Node::setScheduleDeleted(long id, bool on) {
1121     Schedule *ns = findSchedule(id);
1122     if (ns == 0) {
1123         errorPlan<<m_name<<" Could not find schedule with id="<<id;
1124     } else {
1125         ns->setDeleted(on);
1126     }
1127 }
1128 
setParentSchedule(Schedule * sch)1129 void Node::setParentSchedule(Schedule *sch) {
1130     Schedule *s = findSchedule(sch->id());
1131     if (s) {
1132         s->setParent(sch);
1133     }
1134     QListIterator<Node*> it = m_nodes;
1135     while (it.hasNext()) {
1136         it.next()->setParentSchedule(sch);
1137     }
1138 }
1139 
calcCriticalPath(bool fromEnd)1140 bool Node::calcCriticalPath(bool fromEnd) {
1141     if (m_currentSchedule == 0)
1142         return false;
1143     //debugPlan<<m_name;
1144     if (!isCritical()) {
1145         return false;
1146     }
1147     if (!fromEnd && isStartNode()) {
1148         m_currentSchedule->inCriticalPath = true;
1149         return true;
1150     }
1151     if (fromEnd && isEndNode()) {
1152         m_currentSchedule->inCriticalPath = true;
1153         return true;
1154     }
1155     QListIterator<Relation*> pit(m_dependParentNodes);
1156     while (pit.hasNext()) {
1157         if (pit.next()->parent()->calcCriticalPath(fromEnd)) {
1158             m_currentSchedule->inCriticalPath = true;
1159         }
1160     }
1161     return m_currentSchedule->inCriticalPath;
1162 }
1163 
calcFreeFloat()1164 void Node::calcFreeFloat() {
1165     foreach (Node *n, m_nodes) {
1166         n->calcFreeFloat();
1167     }
1168     return;
1169 }
1170 
level() const1171 int Node::level() const {
1172     const Node *n = parentNode();
1173     return n ? n->level() + 1 : 0;
1174 }
1175 
generateWBSCode(QList<int> & indexes,bool sortable) const1176 QString Node::generateWBSCode(QList<int> &indexes, bool sortable) const {
1177     //debugPlan<<m_name<<indexes;
1178     if (m_parent == 0) {
1179         return QString();
1180     }
1181     indexes.insert(0, m_parent->indexOf(this));
1182     return m_parent->generateWBSCode(indexes, sortable);
1183 }
1184 
wbsCode(bool sortable) const1185 QString Node::wbsCode(bool sortable) const {
1186     //debugPlan<<m_name;
1187     QList<int> indexes;
1188     return generateWBSCode(indexes, sortable);
1189 }
1190 
isScheduled(long id) const1191 bool Node::isScheduled(long id) const
1192 {
1193     Schedule *s = schedule(id);
1194     return s != 0 && s->isScheduled();
1195 }
1196 
setCurrentSchedule(long id)1197 void Node::setCurrentSchedule(long id) {
1198     QListIterator<Node*> it = m_nodes;
1199     while (it.hasNext()) {
1200         it.next()->setCurrentSchedule(id);
1201     }
1202     //debugPlan<<m_name<<" id:"<<id<<"="<<m_currentSchedule;
1203 }
1204 
setStartupCost(double cost)1205 void Node::setStartupCost(double cost)
1206 {
1207     m_startupCost = cost;
1208     changed(StartupCostProperty);
1209 }
1210 
setStartupAccount(Account * acc)1211 void Node::setStartupAccount(Account *acc)
1212 {
1213     //debugPlan<<m_name<<"="<<acc;
1214     if (m_startupAccount) {
1215         m_startupAccount->removeStartup(*this);
1216     }
1217     m_startupAccount = acc;
1218     changed(StartupAccountProperty);
1219 }
1220 
setShutdownCost(double cost)1221 void Node::setShutdownCost(double cost)
1222 {
1223     m_shutdownCost = cost;
1224     changed(ShutdownCostProperty);
1225 }
1226 
setShutdownAccount(Account * acc)1227 void Node::setShutdownAccount(Account *acc)
1228 {
1229     //debugPlan<<m_name<<"="<<acc;
1230     if (m_shutdownAccount) {
1231         m_shutdownAccount->removeShutdown(*this);
1232     }
1233     m_shutdownAccount = acc;
1234     changed(ShutdownAccountProperty);
1235 }
1236 
setRunningAccount(Account * acc)1237 void Node::setRunningAccount(Account *acc)
1238 {
1239     //debugPlan<<m_name<<"="<<acc;
1240     if (m_runningAccount) {
1241         m_runningAccount->removeRunning(*this);
1242     }
1243     m_runningAccount = acc;
1244     changed(RunningAccountProperty);
1245 }
1246 
blockChanged(bool on)1247 void Node::blockChanged(bool on)
1248 {
1249     m_blockChanged = on;
1250 }
1251 
changed(Node * node,int property)1252 void Node::changed(Node *node, int property) {
1253     if (m_blockChanged) {
1254         return;
1255     }
1256     switch (property) {
1257         case TypeProperty:
1258         case StartupCostProperty:
1259         case ShutdownCostProperty:
1260         case CompletionEntryProperty:
1261         case CompletionStartedProperty:
1262         case CompletionFinishedProperty:
1263         case CompletionStartTimeProperty:
1264         case CompletionFinishTimeProperty:
1265         case CompletionPercentageProperty:
1266         case CompletionRemainingEffortProperty:
1267         case CompletionActualEffortProperty:
1268         case CompletionUsedEffortProperty:
1269             foreach (Schedule *s, m_schedules) {
1270                 s->clearPerformanceCache();
1271             }
1272         break;
1273         default: break;
1274     }
1275     if (m_parent) {
1276         m_parent->changed(node, property);
1277     }
1278 }
1279 
plannedEffort(const Resource * resource,long id,EffortCostCalculationType type) const1280 Duration Node::plannedEffort(const Resource *resource, long id, EffortCostCalculationType type) const
1281 {
1282     Duration e;
1283     foreach (Node *n, m_nodes) {
1284         e += n->plannedEffort(resource, id, type);
1285     }
1286     return e;
1287 }
1288 
plannedEffort(const Resource * resource,QDate date,long id,EffortCostCalculationType type) const1289 Duration Node::plannedEffort(const Resource *resource, QDate date, long id, EffortCostCalculationType type) const
1290 {
1291     Duration e;
1292     foreach (Node *n, m_nodes) {
1293         e += n->plannedEffort(resource, date, id, type);
1294     }
1295     return e;
1296 }
1297 
plannedEffortTo(const Resource * resource,QDate date,long id,EffortCostCalculationType type) const1298 Duration Node::plannedEffortTo(const Resource *resource, QDate date, long id, EffortCostCalculationType type) const
1299 {
1300     Duration e;
1301     foreach (Node *n, m_nodes) {
1302         e += n->plannedEffortTo(resource, date, id, type);
1303     }
1304     return e;
1305 }
1306 
plannedCost(long id,EffortCostCalculationType type) const1307 EffortCost Node::plannedCost(long id, EffortCostCalculationType type) const
1308 {
1309     EffortCost ec;
1310     foreach (Node *n, m_nodes) {
1311         ec += n->plannedCost(id, type);
1312     }
1313     return ec;
1314 }
1315 
bcwsPrDay(long int id,EffortCostCalculationType type) const1316 EffortCostMap Node::bcwsPrDay(long int id, EffortCostCalculationType type) const
1317 {
1318     return const_cast<Node*>(this)->bcwsPrDay(id, type);
1319 }
1320 
bcwsPrDay(long int id,EffortCostCalculationType type)1321 EffortCostMap Node::bcwsPrDay(long int id, EffortCostCalculationType type)
1322 {
1323     Schedule *s = schedule(id);
1324     if (s == 0) {
1325         return EffortCostMap();
1326     }
1327     EffortCostCache &ec = s->bcwsPrDayCache(type);
1328     if (! ec.cached) {
1329         ec.effortcostmap = EffortCostMap();
1330         foreach (Node *n, m_nodes) {
1331             ec.effortcostmap += n->bcwsPrDay(id, type);
1332         }
1333         ec.cached = true;
1334     }
1335     return ec.effortcostmap;
1336 }
1337 
bcwpPrDay(long int id,EffortCostCalculationType type) const1338 EffortCostMap Node::bcwpPrDay(long int id, EffortCostCalculationType type) const
1339 {
1340     return const_cast<Node*>(this)->bcwpPrDay(id, type);
1341 }
1342 
bcwpPrDay(long int id,EffortCostCalculationType type)1343 EffortCostMap Node::bcwpPrDay(long int id, EffortCostCalculationType type)
1344 {
1345     Schedule *s = schedule(id);
1346     if (s == 0) {
1347         return EffortCostMap();
1348     }
1349     EffortCostCache &ec = s->bcwpPrDayCache(type);
1350     if (! ec.cached) {
1351         ec.effortcostmap = EffortCostMap();
1352         foreach (Node *n, m_nodes) {
1353             ec.effortcostmap += n->bcwpPrDay(id, type);
1354         }
1355         ec.cached = true;
1356     }
1357     return ec.effortcostmap;
1358 }
1359 
acwp(long id,EffortCostCalculationType type) const1360 EffortCostMap Node::acwp(long id, EffortCostCalculationType type) const
1361 {
1362     return const_cast<Node*>(this)->acwp(id, type);
1363 }
1364 
acwp(long id,EffortCostCalculationType type)1365 EffortCostMap Node::acwp(long id, EffortCostCalculationType type)
1366 {
1367     Schedule *s = schedule(id);
1368     if (s == 0) {
1369         return EffortCostMap();
1370     }
1371     EffortCostCache &ec = s->acwpCache(type);
1372     if (! ec.cached) {
1373         ec.effortcostmap = EffortCostMap();
1374         foreach (Node *n, m_nodes) {
1375             ec.effortcostmap += n->acwp(id, type);
1376         }
1377         ec.cached = true;
1378     }
1379     return ec.effortcostmap;
1380 }
1381 
acwp(QDate date,long id) const1382 EffortCost Node::acwp(QDate date, long id) const
1383 {
1384     EffortCost ec;
1385     foreach (Node *n, m_nodes) {
1386         ec += n->acwp(date, id);
1387     }
1388     return ec;
1389 }
1390 
slotStandardWorktimeChanged(KPlato::StandardWorktime *)1391 void Node::slotStandardWorktimeChanged(KPlato::StandardWorktime*)
1392 {
1393     //debugPlan<<m_estimate;
1394     if (m_estimate) {
1395         m_estimate->m_expectedCached = false;
1396         m_estimate->m_optimisticCached = false;
1397         m_estimate->m_pessimisticCached = false;
1398     }
1399 }
1400 
emitDocumentAdded(Node * node,Document * doc,int idx)1401 void Node::emitDocumentAdded(Node *node, Document *doc, int idx)
1402 {
1403     if (m_parent) {
1404         m_parent->emitDocumentAdded(node, doc, idx);
1405     }
1406 }
1407 
emitDocumentRemoved(Node * node,Document * doc,int idx)1408 void Node::emitDocumentRemoved(Node *node, Document *doc, int idx)
1409 {
1410     if (m_parent) {
1411         m_parent->emitDocumentRemoved(node, doc, idx);
1412     }
1413 }
1414 
emitDocumentChanged(Node * node,Document * doc,int idx)1415 void Node::emitDocumentChanged(Node *node, Document *doc, int idx)
1416 {
1417     if (m_parent) {
1418         m_parent->emitDocumentChanged(node, doc, idx);
1419     }
1420 }
1421 
1422 //////////////////////////   Estimate   /////////////////////////////////
1423 
Estimate(Node * parent)1424 Estimate::Estimate(Node *parent)
1425     : m_parent(parent)
1426 {
1427     m_pertCached = false;
1428 
1429     setUnit(Duration::Unit_h);
1430     setExpectedEstimate(8.0);
1431     setPessimisticEstimate(8.0);
1432     setOptimisticEstimate(8.0);
1433 
1434     m_type = Type_Effort;
1435     m_calendar = 0;
1436     m_risktype = Risk_None;
1437 }
1438 
Estimate(const Estimate & estimate,Node * parent)1439 Estimate::Estimate(const Estimate &estimate, Node *parent)
1440     : m_parent(parent)
1441 {
1442     copy(estimate);
1443 }
1444 
~Estimate()1445 Estimate::~Estimate()
1446 {
1447 }
1448 
clear()1449 void Estimate::clear()
1450 {
1451     m_pertCached = false;
1452 
1453     setExpectedEstimate(0.0);
1454     setPessimisticEstimate(0.0);
1455     setOptimisticEstimate(0.0);
1456 
1457     m_type = Type_Effort;
1458     m_calendar = 0;
1459     m_risktype = Risk_None;
1460     m_unit = Duration::Unit_h;
1461     changed(Node::EstimateProperty);
1462 }
1463 
operator =(const Estimate & estimate)1464 Estimate &Estimate::operator=(const Estimate &estimate)
1465 {
1466     copy(estimate);
1467     return *this;
1468 }
1469 
copy(const Estimate & estimate)1470 void Estimate::copy(const Estimate &estimate)
1471 {
1472     //m_parent = 0; // don't touch
1473     m_expectedEstimate = estimate.m_expectedEstimate;
1474     m_optimisticEstimate = estimate.m_optimisticEstimate;
1475     m_pessimisticEstimate = estimate.m_pessimisticEstimate;
1476 
1477     m_expectedValue = estimate.m_expectedValue;
1478     m_optimisticValue = estimate.m_optimisticValue;
1479     m_pessimisticValue = estimate.m_pessimisticValue;
1480 
1481     m_expectedCached = estimate.m_expectedCached;
1482     m_optimisticCached = estimate.m_optimisticCached;
1483     m_pessimisticCached = estimate.m_pessimisticCached;
1484 
1485     m_pertExpected = estimate.m_pertExpected;
1486     m_pertCached = estimate.m_pertCached;
1487 
1488     m_type = estimate.m_type;
1489     m_calendar = estimate.m_calendar;
1490     m_risktype = estimate.m_risktype;
1491     m_unit = estimate.m_unit;
1492     changed(Node::EstimateProperty);
1493 }
1494 
variance() const1495 double Estimate::variance() const
1496 {
1497     double d = deviation();
1498     return d * d;
1499 }
1500 
variance(Duration::Unit unit) const1501 double Estimate::variance(Duration::Unit unit) const
1502 {
1503     double d = deviation(unit);
1504     return d * d;
1505 }
1506 
deviation() const1507 double Estimate::deviation() const
1508 {
1509     return (m_pessimisticEstimate - m_optimisticEstimate) / 6;
1510 }
1511 
deviation(Duration::Unit unit) const1512 double Estimate::deviation(Duration::Unit unit) const
1513 {
1514     if (unit == m_unit) {
1515         return deviation();
1516     }
1517     double p = pessimisticValue().toDouble(unit);
1518     double o = optimisticValue().toDouble(unit);
1519     double v = (p - o) / 6;
1520     return v;
1521 }
1522 
pertExpected() const1523 Duration Estimate::pertExpected() const {
1524     if (m_risktype == Risk_Low) {
1525         if (! m_pertCached) {
1526             m_pertExpected = (optimisticValue() + pessimisticValue() + (expectedValue()*4))/6;
1527             m_pertCached = true;
1528         }
1529         return m_pertExpected;
1530     } else if (m_risktype == Risk_High) {
1531         if (! m_pertCached) {
1532             m_pertExpected = (optimisticValue() + (pessimisticValue()*2) + (expectedValue()*4))/7;
1533             m_pertCached = true;
1534         }
1535         return m_pertExpected;
1536     }
1537     return expectedValue(); // risk==none
1538 }
1539 
pertOptimistic() const1540 Duration Estimate::pertOptimistic() const {
1541     if (m_risktype != Risk_None) {
1542         return pertExpected() - Duration(variance(Duration::Unit_ms));
1543     }
1544     return optimisticValue();
1545 }
1546 
pertPessimistic() const1547 Duration Estimate::pertPessimistic() const {
1548     if (m_risktype != Risk_None) {
1549         return pertExpected() + Duration(variance(Duration::Unit_ms));
1550     }
1551     return pessimisticValue();
1552 }
1553 
value(int valueType,bool pert) const1554 Duration Estimate::value(int valueType, bool pert) const {
1555     if (valueType == Estimate::Use_Expected) {
1556         return pert ? pertExpected() : expectedValue();
1557     } else if (valueType == Estimate::Use_Optimistic) {
1558         return pert ? pertOptimistic() : optimisticValue();
1559     } else if (valueType == Estimate::Use_Pessimistic) {
1560         return pert ? pertPessimistic() : pessimisticValue();
1561     }
1562     return expectedValue();
1563 }
1564 
setUnit(Duration::Unit unit)1565 void Estimate::setUnit(Duration::Unit unit)
1566 {
1567     m_unit = unit;
1568     m_expectedCached = false;
1569     m_optimisticCached = false;
1570     m_pessimisticCached = false;
1571     m_pertCached = false;
1572     changed(Node::EstimateProperty);
1573 }
1574 
load(KoXmlElement & element,XMLLoaderObject & status)1575 bool Estimate::load(KoXmlElement &element, XMLLoaderObject &status) {
1576     setType(element.attribute("type"));
1577     setRisktype(element.attribute("risk"));
1578     if (status.version() <= "0.6") {
1579         m_unit = (Duration::Unit)(element.attribute("display-unit", QString().number(Duration::Unit_h)).toInt());
1580         QList<qint64> s = status.project().standardWorktime()->scales();
1581         m_expectedEstimate = scale(Duration::fromString(element.attribute("expected")), m_unit, s);
1582         m_optimisticEstimate = scale(Duration::fromString(element.attribute("optimistic")), m_unit, s);
1583         m_pessimisticEstimate = scale(Duration::fromString(element.attribute("pessimistic")), m_unit, s);
1584     } else {
1585         if (status.version() <= "0.6.2") {
1586             // 0 pos in unit is now Unit_Y, so add 3 to get the correct new unit
1587             m_unit = (Duration::Unit)(element.attribute("unit", QString().number(Duration::Unit_ms - 3)).toInt() + 3);
1588         } else {
1589             m_unit = Duration::unitFromString(element.attribute("unit"));
1590         }
1591         m_expectedEstimate = element.attribute("expected", "0.0").toDouble();
1592         m_optimisticEstimate = element.attribute("optimistic", "0.0").toDouble();
1593         m_pessimisticEstimate = element.attribute("pessimistic", "0.0").toDouble();
1594 
1595         m_calendar = status.project().findCalendar(element.attribute("calendar-id"));
1596     }
1597     return true;
1598 }
1599 
save(QDomElement & element) const1600 void Estimate::save(QDomElement &element) const {
1601     QDomElement me = element.ownerDocument().createElement("estimate");
1602     element.appendChild(me);
1603     me.setAttribute("expected", QString::number(m_expectedEstimate));
1604     me.setAttribute("optimistic", QString::number(m_optimisticEstimate));
1605     me.setAttribute("pessimistic", QString::number(m_pessimisticEstimate));
1606     me.setAttribute("type", typeToString());
1607     if (m_calendar) {
1608         me.setAttribute("calendar-id", m_calendar->id());
1609     }
1610     me.setAttribute("risk", risktypeToString());
1611     me.setAttribute("unit", Duration::unitToString(m_unit));
1612 }
1613 
typeToString(bool trans) const1614 QString Estimate::typeToString(bool trans) const {
1615     return typeToStringList(trans).at(m_type);
1616 }
1617 
typeToString(Estimate::Type typ,bool trans)1618 QString Estimate::typeToString(Estimate::Type typ, bool trans)
1619 {
1620     return typeToStringList(trans).value(typ);
1621 }
1622 
typeToStringList(bool trans)1623 QStringList Estimate::typeToStringList(bool trans) {
1624     return QStringList()
1625             << (trans ? i18n("Effort") : QString("Effort"))
1626             << (trans ? i18n("Duration") : QString("Duration"));
1627 }
1628 
setType(Type type)1629 void Estimate::setType(Type type)
1630 {
1631     m_type = type;
1632     m_expectedCached = false;
1633     m_optimisticCached = false;
1634     m_pessimisticCached = false;
1635     m_pertCached = false;
1636     changed(Node::EstimateProperty);
1637 }
1638 
setType(const QString & type)1639 void Estimate::setType(const QString& type) {
1640     if (type == "Effort")
1641         setType(Type_Effort);
1642     else if (type == "Duration" || /*old format*/ type == "FixedDuration")
1643         setType(Type_Duration);
1644     else if (/*old format*/type == "Length")
1645         setType(Type_Duration);
1646     else if (type == "Type_FixedDuration") // Typo, keep old xml files working
1647         setType(Type_Duration);
1648     else
1649         setType(Type_Effort); // default
1650 }
1651 
risktypeToString(bool trans) const1652 QString Estimate::risktypeToString(bool trans) const {
1653     return risktypeToStringList(trans).at(m_risktype);
1654 }
1655 
risktypeToStringList(bool trans)1656 QStringList Estimate::risktypeToStringList(bool trans) {
1657     return QStringList()
1658             << (trans ? i18n("None") : QString("None"))
1659             << (trans ? i18n("Low") : QString("Low"))
1660             << (trans ? i18n("High") : QString("High"));
1661 }
1662 
setRisktype(const QString & type)1663 void Estimate::setRisktype(const QString& type) {
1664     if (type == "High")
1665         setRisktype(Risk_High);
1666     else if (type == "Low")
1667         setRisktype(Risk_Low);
1668     else
1669         setRisktype(Risk_None); // default
1670 }
1671 
setRisktype(Risktype type)1672 void Estimate::setRisktype(Risktype type)
1673 {
1674     m_pertCached = false;
1675     m_risktype = type;
1676     changed(Node::EstimateRiskProperty);
1677 }
1678 
setCalendar(Calendar * calendar)1679 void Estimate::setCalendar(Calendar *calendar)
1680 {
1681     m_calendar = calendar;
1682     m_expectedCached = false;
1683     m_optimisticCached = false;
1684     m_pessimisticCached = false;
1685     m_pertCached = false;
1686     changed(Node::EstimateProperty);
1687 }
1688 
setExpectedEstimate(double value)1689 void Estimate::setExpectedEstimate(double value)
1690 {
1691     m_expectedEstimate = value;
1692     m_expectedCached = false;
1693     m_pertCached = false;
1694     changed(Node::EstimateProperty);
1695 }
1696 
setOptimisticEstimate(double value)1697 void Estimate::setOptimisticEstimate(double value)
1698 {
1699     m_optimisticEstimate = value;
1700     m_optimisticCached = false;
1701     m_pertCached = false;
1702     changed(Node::EstimateOptimisticProperty);
1703 }
1704 
setPessimisticEstimate(double value)1705 void Estimate::setPessimisticEstimate(double value)
1706 {
1707     m_pessimisticEstimate = value;
1708     m_pessimisticCached = false;
1709     m_pertCached = false;
1710     changed(Node::EstimatePessimisticProperty);
1711 }
1712 
setOptimisticRatio(int percent)1713 void Estimate::setOptimisticRatio(int percent)
1714 {
1715     int p = percent>0 ? -percent : percent;
1716     m_optimisticValue = expectedValue()*(100+p)/100;
1717     m_optimisticEstimate = scale(m_optimisticValue, m_unit, scales());
1718     m_optimisticCached = true;
1719     m_pertCached = false;
1720     changed(Node::EstimateOptimisticProperty);
1721 }
1722 
optimisticRatio() const1723 int Estimate::optimisticRatio() const {
1724     if (m_expectedEstimate == 0.0)
1725         return 0;
1726     return (int)((optimisticValue()*100)/expectedValue())-100;
1727 }
1728 
setPessimisticRatio(int percent)1729 void Estimate::setPessimisticRatio(int percent)
1730 {
1731     int p = percent<0 ? -percent : percent;
1732     m_pessimisticValue = expectedValue()*(100+p)/100;
1733     m_pessimisticEstimate = scale(m_pessimisticValue, m_unit, scales());
1734     m_pessimisticCached = true;
1735     m_pertCached = false;
1736     changed(Node::EstimatePessimisticProperty);
1737 }
1738 
pessimisticRatio() const1739 int Estimate::pessimisticRatio() const {
1740     if (m_expectedEstimate == 0.0)
1741         return 0;
1742     return (int)((pessimisticValue()*100)/expectedValue())-100;
1743 }
1744 
1745 // internal
setOptimisticValue()1746 void Estimate::setOptimisticValue()
1747 {
1748     m_optimisticValue = scale(m_optimisticEstimate, m_unit, scales());
1749     m_optimisticCached = true;
1750     m_pertCached = false;
1751 }
1752 
1753 // internal
setExpectedValue()1754 void Estimate::setExpectedValue()
1755 {
1756     m_expectedValue = scale(m_expectedEstimate, m_unit, scales());
1757     m_expectedCached = true;
1758     m_pertCached = false;
1759 }
1760 
1761 // internal
setPessimisticValue()1762 void Estimate::setPessimisticValue()
1763 {
1764     m_pessimisticValue = scale(m_pessimisticEstimate, m_unit, scales());
1765     m_pessimisticCached = true;
1766     m_pertCached = false;
1767 }
1768 
optimisticValue() const1769 Duration Estimate::optimisticValue() const
1770 {
1771     if (! m_optimisticCached) {
1772         const_cast<Estimate*>(this)->setOptimisticValue();
1773     }
1774     return m_optimisticValue;
1775 }
1776 
pessimisticValue() const1777 Duration Estimate::pessimisticValue() const
1778 {
1779     if (! m_pessimisticCached) {
1780         const_cast<Estimate*>(this)->setPessimisticValue();
1781     }
1782     return m_pessimisticValue;
1783 }
1784 
expectedValue() const1785 Duration Estimate::expectedValue() const
1786 {
1787     if (! m_expectedCached) {
1788         const_cast<Estimate*>(this)->setExpectedValue();
1789     }
1790     return m_expectedValue;
1791 }
1792 
scale(const Duration & value,Duration::Unit unit,const QList<qint64> & scales)1793 double Estimate::scale(const Duration &value, Duration::Unit unit, const QList<qint64> &scales)
1794 {
1795     //debugPlan<<value.toDouble(unit)<<","<<unit<<scales;
1796     QList<qint64> lst = scales;
1797     switch (lst.count()) {
1798         case Duration::Unit_Y:
1799             lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year
1800             Q_FALLTHROUGH();
1801         case Duration::Unit_M:
1802             lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month
1803             Q_FALLTHROUGH();
1804         case Duration::Unit_w:
1805             lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week
1806             Q_FALLTHROUGH();
1807         case Duration::Unit_d:
1808             lst << 24 * 60 * 60 * 1000; // add milliseconds in day
1809             Q_FALLTHROUGH();
1810         case Duration::Unit_h:
1811             lst << 60 * 60 * 1000; // add milliseconds in hour
1812             Q_FALLTHROUGH();
1813         case Duration::Unit_m:
1814             lst << 60 * 1000; // add milliseconds in minute
1815             Q_FALLTHROUGH();
1816         case Duration::Unit_s:
1817             lst << 1000; // add milliseconds in second
1818             Q_FALLTHROUGH();
1819         case Duration::Unit_ms:
1820             lst << 1; // add milliseconds in a millisecond
1821             Q_FALLTHROUGH();
1822         default:
1823             break;
1824     }
1825     double v = (double)(value.milliseconds());
1826     v /= lst[ unit ];
1827     //debugPlan<<value.toString()<<","<<unit<<"="<<v;
1828     return v;
1829 }
1830 
scale(double value,Duration::Unit unit,const QList<qint64> & scales)1831 Duration Estimate::scale(double value, Duration::Unit unit, const QList<qint64> &scales)
1832 {
1833     //debugPlan<<value<<","<<unit<<scales;
1834     QList<qint64> lst = scales;
1835     switch (lst.count()) {
1836         case Duration::Unit_Y:
1837             lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year
1838             Q_FALLTHROUGH();
1839         case Duration::Unit_M:
1840             lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month
1841             Q_FALLTHROUGH();
1842         case Duration::Unit_w:
1843             lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week
1844             Q_FALLTHROUGH();
1845         case Duration::Unit_d:
1846             lst << 24 * 60 * 60 * 1000; // add milliseconds in day
1847             Q_FALLTHROUGH();
1848         case Duration::Unit_h:
1849             lst << 60 * 60 * 1000; // add milliseconds in hour
1850             Q_FALLTHROUGH();
1851         case Duration::Unit_m:
1852             lst << 60 * 1000; // add milliseconds in minute
1853             Q_FALLTHROUGH();
1854         case Duration::Unit_s:
1855             lst << 1000; // add milliseconds in second
1856             Q_FALLTHROUGH();
1857         case Duration::Unit_ms:
1858             lst << 1; // add milliseconds in a millisecond
1859             Q_FALLTHROUGH();
1860         default:
1861             break;
1862     }
1863     qint64 v = (qint64)(value * lst[ unit ]);
1864     //debugPlan<<value<<","<<unit<<"="<<v;
1865     return Duration(v, Duration::Unit_ms);
1866 }
1867 
1868 //static
defaultScales()1869 QList<qint64> Estimate::defaultScales()
1870 {
1871     QList<qint64> lst;
1872     lst << (qint64)(365 * 24) * 60 * 60 * 1000  // add milliseconds in a year
1873         << (qint64)(30 * 24) * 60 * 60 * 1000   // add milliseconds in a month
1874         << (qint64)(7 * 24) * 60 * 60 * 1000    // add milliseconds in a week
1875         << 24 * 60 * 60 * 1000                  // add milliseconds in day
1876         << 60 * 60 * 1000                       // add milliseconds in hour
1877         << 60 * 1000                            // add milliseconds in minute
1878         << 1000                                 // add milliseconds in second
1879         << 1;                                   // add milliseconds in a millisecond
1880     return lst;
1881 }
1882 
scales() const1883 QList<qint64> Estimate::scales() const
1884 {
1885     QList<qint64> s;
1886     if (m_type == Type_Duration && m_calendar == 0) {
1887         return s; // Use default scaling (24h a day...)
1888     }
1889     if (m_parent == 0) {
1890         return s;
1891     }
1892     Project *p = static_cast<Project*>(m_parent->projectNode());
1893     if (p == 0) {
1894         return s;
1895     }
1896     s << p->standardWorktime()->scales();
1897     return s;
1898 }
1899 
1900 
1901 }  //KPlato namespace
1902