kplato

kptproject.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2004 - 2006 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
21#include "kptproject.h"
22#include "kptappointment.h"
23#include "kpttask.h"
24#include "kptprojectdialog.h"
25#include "kptdatetime.h"
26#include "kptpart.h"
27#include "kptconfig.h"
28#include "kpteffortcostmap.h"
29#include "kptschedule.h"
30
31#include <tqdom.h>
32#include <tqstring.h>
33#include <tqdatetime.h>
34#include <tqbrush.h>
35#include <tqcanvas.h>
36#include <tqptrlist.h>
37
38#include <kdatetimewidget.h>
39#include <kdebug.h>
40
41namespace KPlato
42{
43
44
45Project::Project(Node *parent)
46 : Node(parent),
47 m_accounts(*this),
48 m_baselined(false) {
49 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
50 m_constraint = Node::MustStartOn;
51 m_standardWorktime = new StandardWorktime();
52 m_schedules.setAutoDelete(true);
53 init();
54}
55
56void Project::init() {
57 if (m_parent == 0) {
58 // set sensible defaults for a project wo parent
59 m_constraintStartTime = TQDateTime(TQDate::currentDate(), TQTime());
60 m_constraintEndTime = TQDateTime(m_constraintStartTime.addDays(1));
61 }
62 m_calendars.setAutoDelete(true);
63}
64
65
66Project::~Project() {
67 m_resourceGroups.setAutoDelete(true);
68 m_resourceGroups.clear();
69 delete m_standardWorktime;
70}
71
72int Project::type() const { return Node::Type_Project; }
73
74void Project::calculate(Schedule *schedule) {
75 if (schedule == 0) {
76 kdError()<<k_funcinfo<<"Schedule == 0, cannot calculate"<<endl;
77 return;
78 }
79 m_currentSchedule = schedule;
80 calculate();
81}
82
83void Project::calculate(Effort::Use estType) {
84 m_currentSchedule = findSchedule((Schedule::Type)estType);
85 if (m_currentSchedule == 0) {
86 m_currentSchedule = createSchedule(i18n("Standard"), (Schedule::Type)estType);
87 }
88 calculate();
89}
90
91void Project::calculate() {
92 if (m_currentSchedule == 0) {
93 kdError()<<k_funcinfo<<"No current schedule to calculate"<<endl;
94 return;
95 }
96 Effort::Use estType = (Effort::Use)m_currentSchedule->type();
97 if (type() == Type_Project) {
98 initiateCalculation(*m_currentSchedule);
99 if (m_constraint == Node::MustStartOn) {
100 //kdDebug()<<k_funcinfo<<"Node="<<m_name<<" Start="<<m_constraintStartTime.toString()<<endl;
101 m_currentSchedule->startTime = m_constraintStartTime;
102 m_currentSchedule->earliestStart = m_constraintStartTime;
103 // Calculate from start time
104 propagateEarliestStart(m_currentSchedule->earliestStart);
105 m_currentSchedule->latestFinish = calculateForward(estType);
106 propagateLatestFinish(m_currentSchedule->latestFinish);
107 calculateBackward(estType);
108 m_currentSchedule->endTime = scheduleForward(m_currentSchedule->startTime, estType);
109 calcCriticalPath(false);
110 } else {
111 //kdDebug()<<k_funcinfo<<"Node="<<m_name<<" End="<<m_constraintEndTime.toString()<<endl;
112 m_currentSchedule->endTime = m_constraintEndTime;
113 m_currentSchedule->latestFinish = m_constraintEndTime;
114 // Calculate from end time
115 propagateLatestFinish(m_currentSchedule->latestFinish);
116 m_currentSchedule->earliestStart = calculateBackward(estType);
117 propagateEarliestStart(m_currentSchedule->earliestStart);
118 calculateForward(estType);
119 m_currentSchedule->startTime = scheduleBackward(m_currentSchedule->endTime, estType);
120 calcCriticalPath(true);
121 }
122 makeAppointments();
123 calcResourceOverbooked();
124 m_currentSchedule->notScheduled = false;
125 } else if (type() == Type_Subproject) {
126 kdWarning()<<k_funcinfo<<"Subprojects not implemented"<<endl;
127 } else {
128 kdError()<<k_funcinfo<<"Illegal project type: "<<type()<<endl;
129 }
130}
131
132bool Project::calcCriticalPath(bool fromEnd) {
133 //kdDebug()<<k_funcinfo<<endl;
134 if (fromEnd) {
135 TQPtrListIterator<Node> startnodes = m_startNodes;
136 for (; startnodes.current(); ++startnodes) {
137 startnodes.current()->calcCriticalPath(fromEnd);
138 }
139 } else {
140 TQPtrListIterator<Node> endnodes = m_endNodes;
141 for (; endnodes.current(); ++endnodes) {
142 endnodes.current()->calcCriticalPath(fromEnd);
143 }
144 }
145 return false;
146}
147
148DateTime Project::startTime() const {
149 //kdDebug()<<k_funcinfo<<(m_currentSchedule?m_currentSchedule->id():-1)<<" "<<(m_currentSchedule?m_currentSchedule->typeToString():"")<<endl;
150 if (m_currentSchedule)
151 return m_currentSchedule->startTime;
152
153 return m_constraintStartTime;
154}
155
156DateTime Project::endTime() const {
157 //kdDebug()<<k_funcinfo<<(m_currentSchedule?m_currentSchedule->id():-1)<<" "<<(m_currentSchedule?m_currentSchedule->typeToString():"")<<endl;
158 if (m_currentSchedule)
159 return m_currentSchedule->endTime;
160
161 return m_constraintEndTime;
162}
163
164Duration *Project::getExpectedDuration() {
165 //kdDebug()<<k_funcinfo<<endl;
166 return new Duration(getLatestFinish() - getEarliestStart());
167}
168
169Duration *Project::getRandomDuration() {
170 return 0L;
171}
172
173DateTime Project::calculateForward(int use) {
174 //kdDebug()<<k_funcinfo<<m_name<<endl;
175 if (type() == Node::Type_Project) {
176 // Follow *parent* relations back and
177 // calculate forwards following the child relations
178 DateTime finish;
179 DateTime time;
180 TQPtrListIterator<Node> endnodes = m_endNodes;
181 for (; endnodes.current(); ++endnodes) {
182 time = endnodes.current()->calculateForward(use);
183 if (!finish.isValid() || time > finish)
184 finish = time;
185 }
186 //kdDebug()<<k_funcinfo<<m_name<<" finish="<<finish.toString()<<endl;
187 return finish;
188 } else {
189 //TODO: subproject
190 }
191 return DateTime();
192}
193
194DateTime Project::calculateBackward(int use) {
195 //kdDebug()<<k_funcinfo<<m_name<<endl;
196 if (type() == Node::Type_Project) {
197 // Follow *child* relations back and
198 // calculate backwards following parent relation
199 DateTime start;
200 DateTime time;
201 TQPtrListIterator<Node> startnodes = m_startNodes;
202 for (; startnodes.current(); ++startnodes) {
203 time = startnodes.current()->calculateBackward(use);
204 if (!start.isValid() || time < start)
205 start = time;
206 }
207 //kdDebug()<<k_funcinfo<<m_name<<" start="<<start.toString()<<endl;
208 return start;
209 } else {
210 //TODO: subproject
211 }
212 return DateTime();
213}
214
215DateTime Project::scheduleForward(const DateTime &earliest, int use) {
216 resetVisited();
217 DateTime end = earliest;
218 DateTime time;
219 TQPtrListIterator<Node> it(m_endNodes);
220 for (; it.current(); ++it) {
221 time = it.current()->scheduleForward(earliest, use);
222 if (time > end)
223 end = time;
224 }
225 // Fix summarytasks
226 adjustSummarytask();
227 return end;
228}
229
230DateTime Project::scheduleBackward(const DateTime &latest, int use) {
231 resetVisited();
232 DateTime start = latest;
233 DateTime time;
234 TQPtrListIterator<Node> it(m_startNodes);
235 for (; it.current(); ++it) {
236 time = it.current()->scheduleBackward(latest, use);
237 if (time < start)
238 start = time;
239 }
240 // Fix summarytasks
241 adjustSummarytask();
242 return start;
243}
244
245void Project::adjustSummarytask() {
246 TQPtrListIterator<Node> it(m_summarytasks);
247 for (; it.current(); ++it) {
248 it.current()->adjustSummarytask();
249 }
250}
251
252void Project::initiateCalculation(Schedule &sch) {
253 //kdDebug()<<k_funcinfo<<m_name<<endl;
254 // clear all resource appointments
255 m_visitedForward = false;
256 m_visitedBackward = false;
257 TQPtrListIterator<ResourceGroup> git(m_resourceGroups);
258 for ( ; git.current(); ++git ) {
259 git.current()->initiateCalculation(sch);
260 }
261 Node::initiateCalculation(sch);
262 m_startNodes.clear();
263 m_endNodes.clear();
264 m_summarytasks.clear();
265 initiateCalculationLists(m_startNodes, m_endNodes, m_summarytasks);
266}
267
268void Project::initiateCalculationLists(TQPtrList<Node> &startnodes, TQPtrList<Node> &endnodes, TQPtrList<Node> &summarytasks) {
269 //kdDebug()<<k_funcinfo<<m_name<<endl;
270 if (type() == Node::Type_Project) {
271 TQPtrListIterator<Node> it = childNodeIterator();
272 for (; it.current(); ++it) {
273 it.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
274 }
275 } else {
276 //TODO: subproject
277 }
278}
279
280bool Project::load(TQDomElement &element) {
281 //kdDebug()<<k_funcinfo<<"--->"<<endl;
282 TQString s;
283 bool ok = false;
284 TQString id = element.attribute("id");
285 if (!setId(id)) {
286 kdWarning()<<k_funcinfo<<"Id must be unique: "<<id<<endl;
287 }
288 m_name = element.attribute("name");
289 m_leader = element.attribute("leader");
290 m_description = element.attribute("description");
291
292 //m_baselined = (bool)element.attribute("baselined","0").toInt(&ok);FIXME: Removed for this release
293
294 // Allow for both numeric and text
295 s = element.attribute("scheduling","0");
296 m_constraint = (Node::ConstraintType)s.toInt(&ok);
297 if (!ok)
298 setConstraint(s);
299 if (m_constraint != Node::MustStartOn &&
300 m_constraint != Node::MustFinishOn) {
301 kdError()<<k_funcinfo<<"Illegal constraint: "<<constraintToString()<<endl;
302 setConstraint(Node::MustStartOn);
303 }
304 s = element.attribute("start-time");
305 if (!s.isEmpty())
306 m_constraintStartTime = DateTime::fromString(s);
307 s = element.attribute("end-time");
308 if (!s.isEmpty())
309 m_constraintEndTime = DateTime::fromString(s);
310
311 // Load the project children
312 // Must do these first
313 TQDomNodeList list = element.childNodes();
314 for (unsigned int i=0; i<list.count(); ++i) {
315 if (list.item(i).isElement()) {
316 TQDomElement e = list.item(i).toElement();
317 if (e.tagName() == "calendar") {
318 // Load the calendar.
319 // References by resources
320 Calendar *child = new Calendar();
321 child->setProject(this);
322 if (child->load(e)) {
323 addCalendar(child);
324 } else {
325 // TODO: Complain about this
326 kdError()<<k_funcinfo<<"Failed to load calendar"<<endl;
327 delete child;
328 }
329 } else if (e.tagName() == "standard-worktime") {
330 // Load standard worktime
331 StandardWorktime *child = new StandardWorktime();
332 if (child->load(e)) {
333 setStandardWorktime(child);
334 } else {
335 kdError()<<k_funcinfo<<"Failed to load standard worktime"<<endl;
336 delete child;
337 }
338 }
339 }
340 }
341 for (unsigned int i=0; i<list.count(); ++i) {
342 if (list.item(i).isElement()) {
343 TQDomElement e = list.item(i).toElement();
344
345 if (e.tagName() == "resource-group") {
346 // Load the resources
347 // References calendars
348 ResourceGroup *child = new ResourceGroup(this);
349 if (child->load(e)) {
350 addResourceGroup(child);
351 } else {
352 // TODO: Complain about this
353 delete child;
354 }
355 }
356 }
357 }
358 for (unsigned int i=0; i<list.count(); ++i) {
359 if (list.item(i).isElement()) {
360 TQDomElement e = list.item(i).toElement();
361
362 if (e.tagName() == "project") {
363 //kdDebug()<<k_funcinfo<<"Sub project--->"<<endl;
364 // Load the subproject
365 Project *child = new Project(this);
366 if (child->load(e)) {
367 if (!addTask(child, this)) {
368 delete child; // TODO: Complain about this
369 }
370 } else {
371 // TODO: Complain about this
372 delete child;
373 }
374 } else if (e.tagName() == "task") {
375 //kdDebug()<<k_funcinfo<<"Task--->"<<endl;
376 // Load the task (and resourcerequests).
377 // Depends on resources already loaded
378 Task *child = new Task(this);
379 if (child->load(e, *this)) {
380 if (!addTask(child, this)) {
381 delete child; // TODO: Complain about this
382 }
383 } else {
384 // TODO: Complain about this
385 delete child;
386 }
387 }
388 }
389 }
390 // These go last
391 for (unsigned int i=0; i<list.count(); ++i) {
392 if (list.item(i).isElement()) {
393 TQDomElement e = list.item(i).toElement();
394 if (e.tagName() == "accounts") {
395 //kdDebug()<<k_funcinfo<<"Accounts--->"<<endl;
396 // Load accounts
397 // References tasks
398 if (!m_accounts.load(e, *this)) {
399 kdError()<<k_funcinfo<<"Failed to load accounts"<<endl;
400 }
401 } else if (e.tagName() == "relation") {
402 //kdDebug()<<k_funcinfo<<"Relation--->"<<endl;
403 // Load the relation
404 // References tasks
405 Relation *child = new Relation();
406 if (!child->load(e, *this)) {
407 // TODO: Complain about this
408 kdError()<<k_funcinfo<<"Failed to load relation"<<endl;
409 delete child;
410 }
411 //kdDebug()<<k_funcinfo<<"Relation<---"<<endl;
412 } else if (e.tagName() == "schedules") {
413 //kdDebug()<<k_funcinfo<<"Project schedules & task appointments--->"<<endl;
414 // Prepare for multiple schedules
415 // References tasks and resources
416 TQDomNodeList lst = e.childNodes();
417 for (unsigned int i=0; i<lst.count(); ++i) {
418 if (lst.item(i).isElement()) {
419 TQDomElement el = lst.item(i).toElement();
420 if (el.tagName() == "schedule") {
421 MainSchedule *sch = new MainSchedule();
422 if (sch->loadXML(el, *this)) {
423 addSchedule(sch);
424 sch->setNode(this);
425 setParentSchedule(sch);
426 // If it's here, it's scheduled!
427 sch->setScheduled(true);
428 } else {
429 kdError()<<k_funcinfo<<"Failed to load schedule"<<endl;
430 delete sch;
431 }
432 }
433 }
434 }
435 //kdDebug()<<k_funcinfo<<"Node schedules<---"<<endl;
436 }
437 }
438 }
439 //kdDebug()<<k_funcinfo<<"Calendars--->"<<endl;
440 // calendars references calendars in arbritary saved order
441 TQPtrListIterator<Calendar> calit(m_calendars);
442 for (; calit.current(); ++calit) {
443 if (calit.current()->id() == calit.current()->parentId()) {
444 kdError()<<k_funcinfo<<"Calendar want itself as parent"<<endl;
445 continue;
446 }
447 calit.current()->setParent(calendar(calit.current()->parentId()));
448 }
449 //kdDebug()<<k_funcinfo<<"Project schedules--->"<<endl;
450 TQIntDictIterator<Schedule> it = m_schedules;
451 if (it.current()) {
452 if (m_constraint == Node::MustFinishOn)
453 m_constraintEndTime = it.current()->endTime;
454 else
455 m_constraintStartTime = it.current()->startTime;
456 }
457 //kdDebug()<<k_funcinfo<<"Project schedules<---"<<endl;
458 //kdDebug()<<k_funcinfo<<"<---"<<endl;
459 return true;
460}
461
462void Project::save(TQDomElement &element) const {
463 TQDomElement me = element.ownerDocument().createElement("project");
464 element.appendChild(me);
465
466 me.setAttribute("name", m_name);
467 me.setAttribute("leader", m_leader);
468 me.setAttribute("id", m_id);
469 me.setAttribute("description", m_description);
470
471 //me.setAttribute("baselined",(int)m_baselined); FIXME: Removed for this release
472
473 me.setAttribute("scheduling",constraintToString());
474 me.setAttribute("start-time", m_constraintStartTime.toString(TQt::ISODate));
475 me.setAttribute("end-time", m_constraintEndTime.toString(TQt::ISODate));
476
477 m_accounts.save(me);
478
479 // save calendars
480 TQPtrListIterator<Calendar> calit(m_calendars);
481 for (; calit.current(); ++calit) {
482 calit.current()->save(me);
483 }
484 // save standard worktime
485 if (m_standardWorktime)
486 m_standardWorktime->save(me);
487
488 // save project resources, must be after calendars
489 TQPtrListIterator<ResourceGroup> git(m_resourceGroups);
490 for ( ; git.current(); ++git ) {
491 git.current()->save(me);
492 }
493
494 // Only save parent relations
495 TQPtrListIterator<Relation> it(m_dependParentNodes);
496 for ( ; it.current(); ++it ) {
497 it.current()->save(me);
498 }
499
500 for (int i=0; i<numChildren(); i++)
501 // Save all children
502 getChildNode(i)->save(me);
503
504 // Now we can save relations assuming no tasks have relations outside the project
505 TQPtrListIterator<Node> nodes(m_nodes);
506 for ( ; nodes.current(); ++nodes ) {
507 nodes.current()->saveRelations(me);
508 }
509
510 if (!m_schedules.isEmpty()) {
511 TQDomElement el = me.ownerDocument().createElement("schedules");
512 me.appendChild(el);
513 TQIntDictIterator<Schedule> it = m_schedules;
514 for (; it.current(); ++it) {
515 if (!it.current()->isDeleted() && it.current()->isScheduled()) {
516 TQDomElement schs = el.ownerDocument().createElement("schedule");
517 el.appendChild(schs);
518 it.current()->saveXML(schs);
519 //kdDebug()<<k_funcinfo<<m_name<<" id="<<it.current()->id()<<(it.current()->isDeleted()?" Deleted":"")<<endl;
520 Node::saveAppointments(schs, it.current()->id());
521 }
522 }
523 }
524}
525
526void Project::setParentSchedule(Schedule *sch) {
527 TQPtrListIterator<Node> it = m_nodes;
528 for (; it.current(); ++it) {
529 it.current()->setParentSchedule(sch);
530 }
531}
532
533void Project::addResourceGroup(ResourceGroup * group) {
534 m_resourceGroups.append(group);
535}
536
537
538void Project::removeResourceGroup(ResourceGroup * group){
539 m_resourceGroups.remove(group);
540}
541
542
543void Project::removeResourceGroup(int /* number */){
544 // always auto remove
545}
546
547
548void Project::insertResourceGroup( unsigned int /* index */,
549 ResourceGroup * /* resource */) {
550}
551
552TQPtrList<ResourceGroup> &Project::resourceGroups() {
553 return m_resourceGroups;
554}
555
556bool Project::addTask( Node* task, Node* position )
557{
558 // we want to add a task at the given position. => the new node will
559 // become next sibling right after position.
560 if ( 0 == position ) {
561 kdError()<<k_funcinfo<<"position=0, could not add task: "<<task->name()<<endl;
562 return false;
563 }
564 //kdDebug()<<k_funcinfo<<"Add "<<task->name()<<" after "<<position->name()<<endl;
565 // in case we want to add to the main project, we make it child element
566 // of the root element.
567 if ( Node::Type_Project == position->type() ) {
568 return addSubTask(task, position);
569 }
570 // find the position
571 // we have to tell the parent that we want to delete one of its children
572 Node* parentNode = position->getParent();
573 if ( !parentNode ) {
574 kdDebug()<<k_funcinfo<<"parent node not found???"<<endl;
575 return false;
576 }
577 int index = parentNode->findChildNode( position );
578 if ( -1 == index ) {
579 // ok, it does not exist
580 kdDebug()<<k_funcinfo<<"Task not found???"<<endl;
581 return false;
582 }
583 return addSubTask(task, index+1, parentNode);
584}
585
586bool Project::addSubTask( Node* task, Node* position )
587{
588 // we want to add a subtask to the node "position". It will become
589 // position's last child.
590 if ( 0 == position ) {
591 kdError()<<k_funcinfo<<"No parent, can not add subtask: "<<task->name()<<endl;
592 return false;
593 }
594 if (!registerNodeId(task)) {
595 kdError()<<k_funcinfo<<"Failed to register node id, can not add subtask: "<<task->name()<<endl;
596 return false;
597 }
598 position->addChildNode(task);
599 return true;
600}
601
602bool Project::addSubTask( Node* task, int index, Node* parent )
603{
604 // we want to add a subtask to the node "parent" at the given index.
605 if ( 0 == parent ) {
606 kdError()<<k_funcinfo<<"No parent, can not add subtask: "<<task->name()<<endl;
607 return false;
608 }
609 if (!registerNodeId(task)) {
610 kdError()<<k_funcinfo<<"Failed to register node id, can not add subtask: "<<task->name()<<endl;
611 return false;
612 }
613 parent->insertChildNode(index, task);
614 return true;
615}
616
617void Project::delTask(Node *node)
618{
619 Node *parent = node->getParent();
620 if (parent == 0) {
621 kdDebug()<<k_funcinfo<<"Node must have a parent!"<<endl;
622 return;
623 }
624 removeId(node->id());
625 parent->delChildNode(node, false/*take*/);
626}
627
628bool Project::canIndentTask(Node* node)
629{
630 if (0 == node) {
631 // should always be != 0. At least we would get the Project,
632 // but you never know who might change that, so better be careful
633 return false;
634 }
635 if (node->type() == Node::Type_Project) {
636 //kdDebug()<<k_funcinfo<<"The root node cannot be indented"<<endl;
637 return false;
638 }
639 // we have to find the parent of task to manipulate its list of children
640 Node* parentNode = node->getParent();
641 if ( !parentNode ) {
642 return false;
643 }
644 if (parentNode->findChildNode(node) == -1) {
645 kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
646 return false;
647 }
648 Node *sib = node->siblingBefore();
649 if (!sib) {
650 //kdDebug()<<k_funcinfo<<"new parent node not found"<<endl;
651 return false;
652 }
653 if (node->findParentRelation(sib) || node->findChildRelation(sib)) {
654 //kdDebug()<<k_funcinfo<<"Cannot have relations to parent"<<endl;
655 return false;
656 }
657 return true;
658}
659
660bool Project::indentTask( Node* node )
661{
662 if (canIndentTask(node)) {
663 Node *newParent = node->siblingBefore();
664 node->getParent()->delChildNode(node, false/*do not delete objekt*/);
665 newParent->addChildNode(node);
666 return true;
667 }
668 return false;
669}
670
671bool Project::canUnindentTask( Node* node )
672{
673 if ( 0 == node ) {
674 // is always != 0. At least we would get the Project, but you
675 // never know who might change that, so better be careful
676 return false;
677 }
678 if ( Node::Type_Project == node->type() ) {
679 //kdDebug()<<k_funcinfo<<"The root node cannot be unindented"<<endl;
680 return false;
681 }
682 // we have to find the parent of task to manipulate its list of children
683 // and we need the parent's parent too
684 Node* parentNode = node->getParent();
685 if ( !parentNode ) {
686 return false;
687 }
688 Node* grandParentNode = parentNode->getParent();
689 if ( !grandParentNode ) {
690 //kdDebug()<<k_funcinfo<<"This node already is at the top level"<<endl;
691 return false;
692 }
693 int index = parentNode->findChildNode( node );
694 if ( -1 == index ) {
695 kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
696 return false;
697 }
698 return true;
699}
700
701bool Project::unindentTask( Node* node )
702{
703 if (canUnindentTask(node)) {
704 Node *parentNode = node->getParent();
705 Node *grandParentNode = parentNode->getParent();
706 parentNode->delChildNode(node, false/*do not delete objekt*/);
707 grandParentNode->addChildNode(node,parentNode);
708 return true;
709 }
710 return false;
711}
712
713bool Project::canMoveTaskUp( Node* node )
714{
715 if (node == 0)
716 return false; // safety
717 // we have to find the parent of task to manipulate its list of children
718 Node* parentNode = node->getParent();
719 if (!parentNode) {
720 //kdDebug()<<k_funcinfo<<"No parent found"<<endl;
721 return false;
722 }
723 if (parentNode->findChildNode(node) == -1) {
724 kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
725 return false;
726 }
727 if (node->siblingBefore()) {
728 return true;
729 }
730 return false;
731}
732
733bool Project::moveTaskUp( Node* node )
734{
735 if (canMoveTaskUp(node)) {
736 return node->getParent()->moveChildUp(node);
737 }
738 return false;
739}
740
741bool Project::canMoveTaskDown( Node* node )
742{
743 if (node == 0)
744 return false; // safety
745 // we have to find the parent of task to manipulate its list of children
746 Node* parentNode = node->getParent();
747 if (!parentNode) {
748 return false;
749 }
750 if (parentNode->findChildNode(node) == -1) {
751 kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
752 return false;
753 }
754 if (node->siblingAfter()) {
755 return true;
756 }
757 return false;
758}
759
760bool Project::moveTaskDown( Node* node )
761{
762 if (canMoveTaskDown(node)) {
763 return node->getParent()->moveChildDown(node);
764 }
765 return false;
766}
767
768Task *Project::createTask(Node* parent) {
769 Task* node = new Task(parent);
770 node->setId(uniqueNodeId());
771 return node;
772}
773
774Task *Project::createTask(Task &def, Node* parent) {
775 Task* node = new Task(def, parent);
776 node->setId(uniqueNodeId());
777 return node;
778}
779
780TQString Project::uniqueNodeId(int seed) {
781 int i = seed;
782 while (findNode(TQString("%1").arg(i))) {
783 ++i;
784 }
785 return TQString("%1").arg(i);
786}
787
788bool Project::removeId(const TQString &id) {
789 kdDebug()<<k_funcinfo<<"id="<<id<<endl;
790 return (m_parent ? m_parent->removeId(id) : nodeIdDict.remove(id));
791}
792
793void Project::insertId(const TQString &id, const Node *node) {
794 kdDebug()<<k_funcinfo<<"id="<<id<<" "<<node->name()<<endl;
795 m_parent ? m_parent->insertId(id, node) : nodeIdDict.insert(id, node);
796}
797
798bool Project::registerNodeId(Node *node) {
799 if (node->id().isEmpty()) {
800 kdError()<<k_funcinfo<<"Id is empty."<<endl;
801 return false;
802 }
803 Node *rn = findNode(node->id());
804 if (rn == 0) {
805 insertId(node->id(), node);
806 return true;
807 }
808 if (rn != node) {
809 kdError()<<k_funcinfo<<"Id allready exists for different task: "<<node->id()<<endl;
810 return false;
811 }
812 return true;
813}
814
815
816ResourceGroup *Project::group(TQString id) {
817 return findResourceGroup(id);
818}
819
820Resource *Project::resource(TQString id) {
821 return findResource(id);
822}
823
824// TODO
825EffortCostMap Project::plannedEffortCostPrDay(const TQDate &/*start*/, const TQDate &/*end*/) const {
826 //kdDebug()<<k_funcinfo<<endl;
827 EffortCostMap ec;
828 return ec;
829
830}
831
832// Returns the total planned effort for this project (or subproject)
833Duration Project::plannedEffort() {
834 //kdDebug()<<k_funcinfo<<endl;
835 Duration eff;
836 TQPtrListIterator<Node> it(childNodeIterator());
837 for (; it.current(); ++it) {
838 eff += it.current()->plannedEffort();
839 }
840 return eff;
841}
842
843// Returns the total planned effort for this project (or subproject) on date
844Duration Project::plannedEffort(const TQDate &date) {
845 //kdDebug()<<k_funcinfo<<endl;
846 Duration eff;
847 TQPtrListIterator<Node> it(childNodeIterator());
848 for (; it.current(); ++it) {
849 eff += it.current()->plannedEffort(date);
850 }
851 return eff;
852}
853
854// Returns the total planned effort for this project (or subproject) upto and including date
855Duration Project::plannedEffortTo(const TQDate &date) {
856 //kdDebug()<<k_funcinfo<<endl;
857 Duration eff;
858 TQPtrListIterator<Node> it(childNodeIterator());
859 for (; it.current(); ++it) {
860 eff += it.current()->plannedEffortTo(date);
861 }
862 return eff;
863}
864
865// Returns the total actual effort for this project (or subproject)
866Duration Project::actualEffort() {
867 //kdDebug()<<k_funcinfo<<endl;
868 Duration eff;
869 TQPtrListIterator<Node> it(childNodeIterator());
870 for (; it.current(); ++it) {
871 eff += it.current()->actualEffort();
872 }
873 return eff;
874}
875
876// Returns the total actual effort for this project (or subproject) on date
877Duration Project::actualEffort(const TQDate &date) {
878 //kdDebug()<<k_funcinfo<<endl;
879 Duration eff;
880 TQPtrListIterator<Node> it(childNodeIterator());
881 for (; it.current(); ++it) {
882 eff += it.current()->actualEffort(date);
883 }
884 return eff;
885}
886
887// Returns the total actual effort for this project (or subproject) upto and including date
888Duration Project::actualEffortTo(const TQDate &date) {
889 //kdDebug()<<k_funcinfo<<endl;
890 Duration eff;
891 TQPtrListIterator<Node> it(childNodeIterator());
892 for (; it.current(); ++it) {
893 eff += it.current()->actualEffortTo(date);
894 }
895 return eff;
896}
897
898double Project::plannedCost() {
899 //kdDebug()<<k_funcinfo<<endl;
900 double c = 0;
901 TQPtrListIterator<Node> it(childNodeIterator());
902 for (; it.current(); ++it) {
903 c += it.current()->plannedCost();
904 }
905 return c;
906}
907
908// Returns the total planned effort for this project (or subproject) on date
909double Project::plannedCost(const TQDate &date) {
910 //kdDebug()<<k_funcinfo<<endl;
911 double c = 0;
912 TQPtrListIterator<Node> it(childNodeIterator());
913 for (; it.current(); ++it) {
914 c += it.current()->plannedCost(date);
915 }
916 return c;
917}
918
919// Returns the total planned effort for this project (or subproject) upto and including date
920double Project::plannedCostTo(const TQDate &date) {
921 //kdDebug()<<k_funcinfo<<endl;
922 double c = 0;
923 TQPtrListIterator<Node> it(childNodeIterator());
924 for (; it.current(); ++it) {
925 c += it.current()->plannedCostTo(date);
926 }
927 return c;
928}
929
930double Project::actualCost() {
931 //kdDebug()<<k_funcinfo<<endl;
932 double c = 0;
933 TQPtrListIterator<Node> it(childNodeIterator());
934 for (; it.current(); ++it) {
935 c += it.current()->actualCost();
936 }
937 return c;
938}
939
940// Returns the total planned effort for this project (or subproject) on date
941double Project::actualCost(const TQDate &date) {
942 //kdDebug()<<k_funcinfo<<endl;
943 double c = 0;
944 TQPtrListIterator<Node> it(childNodeIterator());
945 for (; it.current(); ++it) {
946 c += it.current()->actualCost(date);
947 }
948 return c;
949}
950
951// Returns the total planned effort for this project (or subproject) upto and including date
952double Project::actualCostTo(const TQDate &date) {
953 //kdDebug()<<k_funcinfo<<endl;
954 double c = 0;
955 TQPtrListIterator<Node> it(childNodeIterator());
956 for (; it.current(); ++it) {
957 c += it.current()->actualCostTo(date);
958 }
959 return c;
960}
961
962void Project::addCalendar(Calendar *calendar) {
963 //kdDebug()<<k_funcinfo<<calendar->name()<<endl;
964 m_calendars.append(calendar);
965}
966
967Calendar *Project::calendar(const TQString id) const {
968 return findCalendar(id);
969}
970
971TQPtrList<Calendar> Project::calendars() {
972 TQPtrList<Calendar> list;
973 TQPtrListIterator<Calendar> it = m_calendars;
974 for (; it.current(); ++it) {
975 if (!it.current()->isDeleted()) {
976 list.append(it.current());
977 }
978 }
979 return list;
980}
981
982void Project::setStandardWorktime(StandardWorktime * worktime) {
983 if (m_standardWorktime != worktime) {
984 delete m_standardWorktime;
985 m_standardWorktime = worktime;
986 }
987}
988
989bool Project::legalToLink(Node *par, Node *child) {
990 //kdDebug()<<k_funcinfo<<par.name()<<" ("<<par.numDependParentNodes()<<" parents) "<<child.name()<<" ("<<child.numDependChildNodes()<<" children)"<<endl;
991
992 if (!child || par->isDependChildOf(child)) {
993 return false;
994 }
995 bool legal = true;
996 // see if par/child is related
997 if (par->isParentOf(child) || child->isParentOf(par)) {
998 legal = false;
999 }
1000 if (legal)
1001 legal = legalChildren(par, child);
1002 if (legal)
1003 legal = legalParents(par, child);
1004
1005 return legal;
1006}
1007
1008bool Project::legalParents(Node *par, Node *child) {
1009 bool legal = true;
1010 //kdDebug()<<k_funcinfo<<par->name()<<" ("<<par->numDependParentNodes()<<" parents) "<<child->name()<<" ("<<child->numDependChildNodes()<<" children)"<<endl;
1011 for (int i=0; i < par->numDependParentNodes() && legal; ++i) {
1012 Node *pNode = par->getDependParentNode(i)->parent();
1013 if (child->isParentOf(pNode) || pNode->isParentOf(child)) {
1014 //kdDebug()<<k_funcinfo<<"Found: "<<pNode->name()<<" is related to "<<child->name()<<endl;
1015 legal = false;
1016 } else {
1017 legal = legalChildren(pNode, child);
1018 }
1019 if (legal)
1020 legal = legalParents(pNode, child);
1021 }
1022 return legal;
1023}
1024
1025bool Project::legalChildren(Node *par, Node *child) {
1026 bool legal = true;
1027 //kdDebug()<<k_funcinfo<<par->name()<<" ("<<par->numDependParentNodes()<<" parents) "<<child->name()<<" ("<<child->numDependChildNodes()<<" children)"<<endl;
1028 for (int j=0; j < child->numDependChildNodes() && legal; ++j) {
1029 Node *cNode = child->getDependChildNode(j)->child();
1030 if (par->isParentOf(cNode) || cNode->isParentOf(par)) {
1031 //kdDebug()<<k_funcinfo<<"Found: "<<par->name()<<" is related to "<<cNode->name()<<endl;
1032 legal = false;
1033 } else {
1034 legal = legalChildren(par, cNode);
1035 }
1036 }
1037 return legal;
1038}
1039
1040void Project::generateWBS(int count, WBSDefinition &def, TQString wbs) {
1041 if (type() == Type_Subproject || def.level0Enabled()) {
1042 Node::generateWBS(count, def, wbs);
1043 } else {
1044 TQPtrListIterator<Node> it = m_nodes;
1045 for (int i=0; it.current(); ++it) {
1046 it.current()->generateWBS(++i, def, m_wbs);
1047 }
1048 }
1049}
1050
1051void Project::setCurrentSchedule(long id) {
1052 setCurrentSchedulePtr(findSchedule(id));
1053 Node::setCurrentSchedule(id);
1054 TQDictIterator<Resource> it = resourceIdDict;
1055 for (; it.current(); ++it) {
1056 it.current()->setCurrentSchedule(id);
1057 }
1058}
1059
1060MainSchedule *Project::createSchedule(TQString name, Schedule::Type type) {
1061 //kdDebug()<<k_funcinfo<<"No of schedules: "<<m_schedules.count()<<endl;
1062 long i=1;
1063 while (m_schedules.find(i)) {
1064 ++i;
1065 }
1066 MainSchedule *sch = new MainSchedule(this, name, type, i);
1067 addSchedule(sch);
1068 return sch;
1069}
1070
1071bool Project::removeCalendarId(const TQString &id) {
1072 kdDebug()<<k_funcinfo<<"id="<<id<<endl;
1073 return calendarIdDict.remove(id);
1074}
1075
1076void Project::insertCalendarId(const TQString &id, const Calendar *calendar) {
1077 kdDebug()<<k_funcinfo<<"id="<<id<<": "<<calendar->name()<<endl;
1078 calendarIdDict.insert(id, calendar);
1079}
1080
1081#ifndef NDEBUG
1082void Project::printDebug(bool children, TQCString indent) {
1083
1084 kdDebug()<<indent<<"+ Project node: "<<name()<<endl;
1085 indent += "!";
1086 TQPtrListIterator<ResourceGroup> it(resourceGroups());
1087 for ( ; it.current(); ++it)
1088 it.current()->printDebug(indent);
1089
1090 Node::printDebug(children, indent);
1091}
1092void Project::printCalendarDebug(TQCString indent) {
1093 kdDebug()<<indent<<"-------- Calendars debug printout --------"<<endl;
1094 TQPtrListIterator<Calendar> it = m_calendars;
1095 for (; it.current(); ++it) {
1096 it.current()->printDebug(indent + "--");
1097 kdDebug()<<endl;
1098 }
1099 if (m_standardWorktime)
1100 m_standardWorktime->printDebug();
1101}
1102#endif
1103
1104} //KPlato namespace
Calendar defines the working and nonworking days and hours.
DateTime is a TQDateTime which knows about Duration.
Definition kptdatetime.h:33
The duration class can be used to store a timespan in a convenient format.
Definition kptduration.h:34
MainSchedule holds scheduling information for the main project node.
This class represents any node in the project, a node can be a project or a subproject or any task.
Definition kptnode.h:57
Node * parent() const
Definition kptrelation.h:72
This class represents a group of similar resources to be assigned to a task e.g.
Definition kptresource.h:66
Any resource that is used by a task.
The Schedule class holds data calculated during project calculation and scheduling,...
Definition kptschedule.h:55