kplato

kptproject.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 Thomas zander <zander@kde.org>
00003    Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kptproject.h"
00022 #include "kptappointment.h"
00023 #include "kpttask.h"
00024 #include "kptprojectdialog.h"
00025 #include "kptdatetime.h"
00026 #include "kptpart.h"
00027 #include "kptconfig.h"
00028 #include "kpteffortcostmap.h"
00029 #include "kptschedule.h"
00030 
00031 #include <qdom.h>
00032 #include <qstring.h>
00033 #include <qdatetime.h>
00034 #include <qbrush.h>
00035 #include <qcanvas.h>
00036 #include <qptrlist.h>
00037 
00038 #include <kdatetimewidget.h>
00039 #include <kdebug.h>
00040 
00041 namespace KPlato
00042 {
00043 
00044 
00045 Project::Project(Node *parent)
00046     : Node(parent),
00047       m_accounts(*this),
00048       m_baselined(false) {
00049     //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
00050     m_constraint = Node::MustStartOn;
00051     m_standardWorktime = new StandardWorktime();
00052     m_schedules.setAutoDelete(true);
00053     init();
00054 }
00055 
00056 void Project::init() {
00057     if (m_parent == 0) {
00058         // set sensible defaults for a project wo parent
00059         m_constraintStartTime = QDateTime(QDate::currentDate(), QTime());
00060         m_constraintEndTime = m_constraintStartTime.addDays(1);
00061     }
00062     m_calendars.setAutoDelete(true);
00063 }
00064 
00065 
00066 Project::~Project() {
00067     m_resourceGroups.setAutoDelete(true);
00068     m_resourceGroups.clear();
00069     delete m_standardWorktime;
00070 }
00071 
00072 int Project::type() const { return Node::Type_Project; }
00073 
00074 void Project::calculate(Schedule *schedule) {
00075     if (schedule == 0) {
00076         kdError()<<k_funcinfo<<"Schedule == 0, cannot calculate"<<endl;
00077         return;
00078     }
00079     m_currentSchedule = schedule;
00080     calculate();
00081 }
00082 
00083 void Project::calculate(Effort::Use estType) {
00084     m_currentSchedule = findSchedule((Schedule::Type)estType);
00085     if (m_currentSchedule == 0) {
00086         m_currentSchedule = createSchedule(i18n("Standard"), (Schedule::Type)estType);
00087     }
00088     calculate();
00089 }
00090 
00091 void Project::calculate() {
00092     if (m_currentSchedule == 0) {
00093         kdError()<<k_funcinfo<<"No current schedule to calculate"<<endl;
00094         return;
00095     }
00096     Effort::Use estType = (Effort::Use)m_currentSchedule->type();
00097     if (type() == Type_Project) {
00098         initiateCalculation(*m_currentSchedule);
00099         if (m_constraint == Node::MustStartOn) {
00100             //kdDebug()<<k_funcinfo<<"Node="<<m_name<<" Start="<<m_constraintStartTime.toString()<<endl;
00101             m_currentSchedule->startTime = m_constraintStartTime;
00102             m_currentSchedule->earliestStart = m_constraintStartTime;
00103             // Calculate from start time
00104             propagateEarliestStart(m_currentSchedule->earliestStart);
00105             m_currentSchedule->latestFinish = calculateForward(estType);
00106             propagateLatestFinish(m_currentSchedule->latestFinish);
00107             calculateBackward(estType);
00108             m_currentSchedule->endTime = scheduleForward(m_currentSchedule->startTime, estType);
00109             calcCriticalPath(false);
00110         } else {
00111             //kdDebug()<<k_funcinfo<<"Node="<<m_name<<" End="<<m_constraintEndTime.toString()<<endl;
00112             m_currentSchedule->endTime = m_constraintEndTime;
00113             m_currentSchedule->latestFinish = m_constraintEndTime;
00114             // Calculate from end time
00115             propagateLatestFinish(m_currentSchedule->latestFinish);
00116             m_currentSchedule->earliestStart = calculateBackward(estType);
00117             propagateEarliestStart(m_currentSchedule->earliestStart);
00118             calculateForward(estType);
00119             m_currentSchedule->startTime = scheduleBackward(m_currentSchedule->endTime, estType);
00120             calcCriticalPath(true);
00121         }
00122         makeAppointments();
00123         calcResourceOverbooked();
00124         m_currentSchedule->notScheduled = false;
00125     } else if (type() == Type_Subproject) {
00126         kdWarning()<<k_funcinfo<<"Subprojects not implemented"<<endl;
00127     } else {
00128         kdError()<<k_funcinfo<<"Illegal project type: "<<type()<<endl;
00129     }
00130 }
00131 
00132 bool Project::calcCriticalPath(bool fromEnd) {
00133     //kdDebug()<<k_funcinfo<<endl;
00134     if (fromEnd) {
00135         QPtrListIterator<Node> startnodes = m_startNodes;
00136         for (; startnodes.current(); ++startnodes) {
00137             startnodes.current()->calcCriticalPath(fromEnd);
00138         }
00139     } else {
00140         QPtrListIterator<Node> endnodes = m_endNodes;
00141         for (; endnodes.current(); ++endnodes) {
00142             endnodes.current()->calcCriticalPath(fromEnd);
00143         }
00144     }
00145     return false;
00146 }
00147 
00148 DateTime Project::startTime() const {
00149     //kdDebug()<<k_funcinfo<<(m_currentSchedule?m_currentSchedule->id():-1)<<" "<<(m_currentSchedule?m_currentSchedule->typeToString():"")<<endl;
00150     if (m_currentSchedule)
00151         return m_currentSchedule->startTime;
00152     
00153     return m_constraintStartTime;
00154 }
00155 
00156 DateTime Project::endTime() const {
00157     //kdDebug()<<k_funcinfo<<(m_currentSchedule?m_currentSchedule->id():-1)<<" "<<(m_currentSchedule?m_currentSchedule->typeToString():"")<<endl;
00158     if (m_currentSchedule)
00159         return m_currentSchedule->endTime;
00160     
00161     return m_constraintEndTime;
00162 }
00163 
00164 Duration *Project::getExpectedDuration() {
00165     //kdDebug()<<k_funcinfo<<endl;
00166     return new Duration(getLatestFinish() - getEarliestStart());
00167 }
00168 
00169 Duration *Project::getRandomDuration() {
00170     return 0L;
00171 }
00172 
00173 DateTime Project::calculateForward(int use) {
00174     //kdDebug()<<k_funcinfo<<m_name<<endl;
00175     if (type() == Node::Type_Project) {
00176         // Follow *parent* relations back and
00177         // calculate forwards following the child relations
00178         DateTime finish;
00179         DateTime time;
00180         QPtrListIterator<Node> endnodes = m_endNodes;
00181         for (; endnodes.current(); ++endnodes) {
00182             time = endnodes.current()->calculateForward(use);
00183             if (!finish.isValid() || time > finish)
00184                 finish = time;
00185         }
00186         //kdDebug()<<k_funcinfo<<m_name<<" finish="<<finish.toString()<<endl;
00187         return finish;
00188     } else {
00189         //TODO: subproject
00190     }
00191     return DateTime();
00192 }
00193 
00194 DateTime Project::calculateBackward(int use) {
00195     //kdDebug()<<k_funcinfo<<m_name<<endl;
00196     if (type() == Node::Type_Project) {
00197         // Follow *child* relations back and
00198         // calculate backwards following parent relation
00199         DateTime start;
00200         DateTime time;
00201         QPtrListIterator<Node> startnodes = m_startNodes;
00202         for (; startnodes.current(); ++startnodes) {
00203             time = startnodes.current()->calculateBackward(use);
00204             if (!start.isValid() || time < start)
00205                 start = time;
00206         }
00207         //kdDebug()<<k_funcinfo<<m_name<<" start="<<start.toString()<<endl;
00208         return start;
00209     } else {
00210         //TODO: subproject
00211     }
00212     return DateTime();
00213 }
00214 
00215 DateTime Project::scheduleForward(const DateTime &earliest, int use) {
00216     resetVisited();
00217     DateTime end = earliest;
00218     DateTime time;
00219     QPtrListIterator<Node> it(m_endNodes);
00220     for (; it.current(); ++it) {
00221         time = it.current()->scheduleForward(earliest, use);
00222         if (time > end)
00223             end = time;
00224     }
00225     // Fix summarytasks
00226     adjustSummarytask();
00227     return end;
00228 }
00229 
00230 DateTime Project::scheduleBackward(const DateTime &latest, int use) {
00231     resetVisited();
00232     DateTime start = latest;
00233     DateTime time;
00234     QPtrListIterator<Node> it(m_startNodes);
00235     for (; it.current(); ++it) {
00236         time = it.current()->scheduleBackward(latest, use);
00237         if (time < start)
00238             start = time;
00239     }
00240     // Fix summarytasks
00241     adjustSummarytask();
00242     return start;
00243 }
00244 
00245 void Project::adjustSummarytask() {
00246     QPtrListIterator<Node> it(m_summarytasks);
00247     for (; it.current(); ++it) {
00248         it.current()->adjustSummarytask();
00249     }
00250 }
00251 
00252 void Project::initiateCalculation(Schedule &sch) {
00253     //kdDebug()<<k_funcinfo<<m_name<<endl;
00254     // clear all resource appointments
00255     m_visitedForward = false;
00256     m_visitedBackward = false;
00257     QPtrListIterator<ResourceGroup> git(m_resourceGroups);
00258     for ( ; git.current(); ++git ) {
00259         git.current()->initiateCalculation(sch);
00260     }
00261     Node::initiateCalculation(sch);
00262     m_startNodes.clear();
00263     m_endNodes.clear();
00264     m_summarytasks.clear();
00265     initiateCalculationLists(m_startNodes, m_endNodes, m_summarytasks);
00266 }
00267 
00268 void Project::initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks) {
00269     //kdDebug()<<k_funcinfo<<m_name<<endl;
00270     if (type() == Node::Type_Project) {
00271         QPtrListIterator<Node> it = childNodeIterator();
00272         for (; it.current(); ++it) {
00273             it.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
00274         }
00275     } else {
00276         //TODO: subproject
00277     }
00278 }
00279 
00280 bool Project::load(QDomElement &element) {
00281     //kdDebug()<<k_funcinfo<<"--->"<<endl;
00282     QString s;
00283     bool ok = false;
00284     QString id = element.attribute("id");
00285     if (!setId(id)) {
00286         kdWarning()<<k_funcinfo<<"Id must be unique: "<<id<<endl;
00287     }
00288     m_name = element.attribute("name");
00289     m_leader = element.attribute("leader");
00290     m_description = element.attribute("description");
00291     
00292     //m_baselined = (bool)element.attribute("baselined","0").toInt(&ok);FIXME: Removed for this release  
00293     
00294     // Allow for both numeric and text
00295     s = element.attribute("scheduling","0");
00296     m_constraint = (Node::ConstraintType)s.toInt(&ok);
00297     if (!ok)
00298         setConstraint(s);
00299     if (m_constraint != Node::MustStartOn &&
00300         m_constraint != Node::MustFinishOn) {
00301         kdError()<<k_funcinfo<<"Illegal constraint: "<<constraintToString()<<endl;
00302         setConstraint(Node::MustStartOn);
00303     }
00304     s = element.attribute("start-time");
00305     if (!s.isEmpty())
00306         m_constraintStartTime = DateTime::fromString(s);
00307     s = element.attribute("end-time");
00308     if (!s.isEmpty())
00309         m_constraintEndTime = DateTime::fromString(s);
00310     
00311     // Load the project children
00312     // Must do these first
00313     QDomNodeList list = element.childNodes();
00314     for (unsigned int i=0; i<list.count(); ++i) {
00315         if (list.item(i).isElement()) {
00316             QDomElement e = list.item(i).toElement();
00317             if (e.tagName() == "calendar") {
00318                 // Load the calendar.
00319                 // References by resources
00320                 Calendar *child = new Calendar();
00321                 child->setProject(this);
00322                 if (child->load(e)) {
00323                     addCalendar(child);
00324                 } else {
00325                     // TODO: Complain about this
00326                     kdError()<<k_funcinfo<<"Failed to load calendar"<<endl;
00327                     delete child;
00328                 }
00329             } else if (e.tagName() == "standard-worktime") {
00330                 // Load standard worktime
00331                 StandardWorktime *child = new StandardWorktime();
00332                 if (child->load(e)) {
00333                     setStandardWorktime(child);
00334                 } else {
00335                     kdError()<<k_funcinfo<<"Failed to load standard worktime"<<endl;
00336                     delete child;
00337                 }
00338             }
00339         }
00340     }
00341     for (unsigned int i=0; i<list.count(); ++i) {
00342         if (list.item(i).isElement()) {
00343             QDomElement e = list.item(i).toElement();
00344     
00345             if (e.tagName() == "resource-group") {
00346                 // Load the resources
00347                 // References calendars
00348                 ResourceGroup *child = new ResourceGroup(this);
00349                 if (child->load(e)) {
00350                         addResourceGroup(child);
00351                 } else {
00352                     // TODO: Complain about this
00353                     delete child;
00354                 }
00355             }
00356         }
00357     }
00358     for (unsigned int i=0; i<list.count(); ++i) {
00359         if (list.item(i).isElement()) {
00360             QDomElement e = list.item(i).toElement();
00361     
00362             if (e.tagName() == "project") {
00363                 //kdDebug()<<k_funcinfo<<"Sub project--->"<<endl;
00364                 // Load the subproject
00365                 Project *child = new Project(this);
00366                 if (child->load(e)) {
00367                     if (!addTask(child, this)) {
00368                         delete child; // TODO: Complain about this
00369                     }
00370                 } else {
00371                     // TODO: Complain about this
00372                     delete child;
00373                 }
00374             } else if (e.tagName() == "task") {
00375                 //kdDebug()<<k_funcinfo<<"Task--->"<<endl;
00376                 // Load the task (and resourcerequests). 
00377                 // Depends on resources already loaded
00378                 Task *child = new Task(this);
00379                 if (child->load(e, *this)) {
00380                     if (!addTask(child, this)) {
00381                         delete child; // TODO: Complain about this
00382                     }
00383                 } else {
00384                     // TODO: Complain about this
00385                     delete child;
00386                 }
00387             }
00388         }
00389     }
00390     // These go last
00391     for (unsigned int i=0; i<list.count(); ++i) {
00392         if (list.item(i).isElement()) {
00393             QDomElement e = list.item(i).toElement();
00394             if (e.tagName() == "accounts") {
00395                 //kdDebug()<<k_funcinfo<<"Accounts--->"<<endl;
00396                 // Load accounts
00397                 // References tasks
00398                 if (!m_accounts.load(e, *this)) {
00399                     kdError()<<k_funcinfo<<"Failed to load accounts"<<endl;
00400                 }
00401             } else if (e.tagName() == "relation") {
00402                 //kdDebug()<<k_funcinfo<<"Relation--->"<<endl;
00403                 // Load the relation
00404                 // References tasks
00405                 Relation *child = new Relation();
00406                 if (!child->load(e, *this)) {
00407                     // TODO: Complain about this
00408                     kdError()<<k_funcinfo<<"Failed to load relation"<<endl;
00409                     delete child;
00410                 }
00411                 //kdDebug()<<k_funcinfo<<"Relation<---"<<endl;
00412             } else if (e.tagName() == "schedules") {
00413                 //kdDebug()<<k_funcinfo<<"Project schedules & task appointments--->"<<endl;
00414                 // Prepare for multiple schedules
00415                 // References tasks and resources
00416                 QDomNodeList lst = e.childNodes();
00417                 for (unsigned int i=0; i<lst.count(); ++i) {
00418                     if (lst.item(i).isElement()) {
00419                         QDomElement el = lst.item(i).toElement();
00420                         if (el.tagName() == "schedule") {
00421                             MainSchedule *sch = new MainSchedule();
00422                             if (sch->loadXML(el, *this)) {
00423                                 addSchedule(sch);
00424                                 sch->setNode(this);
00425                                 setParentSchedule(sch);
00426                                 // If it's here, it's scheduled!
00427                                 sch->setScheduled(true);
00428                             } else {
00429                                 kdError()<<k_funcinfo<<"Failed to load schedule"<<endl;
00430                                 delete sch;
00431                             }
00432                         }
00433                     }
00434                 }
00435                 //kdDebug()<<k_funcinfo<<"Node schedules<---"<<endl;
00436             }
00437         }
00438     }
00439     //kdDebug()<<k_funcinfo<<"Calendars--->"<<endl;
00440     // calendars references calendars in arbritary saved order
00441     QPtrListIterator<Calendar> calit(m_calendars);
00442     for (; calit.current(); ++calit) {
00443         if (calit.current()->id() == calit.current()->parentId()) {
00444             kdError()<<k_funcinfo<<"Calendar want itself as parent"<<endl;
00445             continue;
00446         }
00447         calit.current()->setParent(calendar(calit.current()->parentId()));
00448     }
00449     //kdDebug()<<k_funcinfo<<"Project schedules--->"<<endl;
00450     QIntDictIterator<Schedule> it = m_schedules;
00451     if (it.current()) {
00452         if (m_constraint == Node::MustFinishOn)
00453             m_constraintEndTime = it.current()->endTime;
00454         else
00455             m_constraintStartTime = it.current()->startTime;
00456     }
00457     //kdDebug()<<k_funcinfo<<"Project schedules<---"<<endl;
00458     //kdDebug()<<k_funcinfo<<"<---"<<endl;
00459     return true;
00460 }
00461 
00462 void Project::save(QDomElement &element)  const {
00463     QDomElement me = element.ownerDocument().createElement("project");
00464     element.appendChild(me);
00465 
00466     me.setAttribute("name", m_name);
00467     me.setAttribute("leader", m_leader);
00468     me.setAttribute("id", m_id);
00469     me.setAttribute("description", m_description);
00470     
00471     //me.setAttribute("baselined",(int)m_baselined); FIXME: Removed for this release  
00472 
00473     me.setAttribute("scheduling",constraintToString());    
00474     me.setAttribute("start-time", m_constraintStartTime.toString(Qt::ISODate));
00475     me.setAttribute("end-time", m_constraintEndTime.toString(Qt::ISODate));
00476     
00477     m_accounts.save(me);
00478     
00479     // save calendars
00480     QPtrListIterator<Calendar> calit(m_calendars);
00481     for (; calit.current(); ++calit) {
00482         calit.current()->save(me);
00483     }
00484     // save standard worktime
00485     if (m_standardWorktime)
00486         m_standardWorktime->save(me);
00487     
00488     // save project resources, must be after calendars
00489     QPtrListIterator<ResourceGroup> git(m_resourceGroups);
00490     for ( ; git.current(); ++git ) {
00491         git.current()->save(me);
00492     }
00493 
00494     // Only save parent relations
00495     QPtrListIterator<Relation> it(m_dependParentNodes);
00496     for ( ; it.current(); ++it ) {
00497         it.current()->save(me);
00498     }
00499 
00500     for (int i=0; i<numChildren(); i++)
00501     // Save all children
00502     getChildNode(i)->save(me);
00503 
00504     // Now we can save relations assuming no tasks have relations outside the project
00505     QPtrListIterator<Node> nodes(m_nodes);
00506     for ( ; nodes.current(); ++nodes ) {
00507         nodes.current()->saveRelations(me);
00508     }
00509     
00510     if (!m_schedules.isEmpty()) {
00511         QDomElement el = me.ownerDocument().createElement("schedules");
00512         me.appendChild(el);
00513         QIntDictIterator<Schedule> it = m_schedules;
00514         for (; it.current(); ++it) {
00515             if (!it.current()->isDeleted() && it.current()->isScheduled()) {
00516                 QDomElement schs = el.ownerDocument().createElement("schedule");
00517                 el.appendChild(schs);
00518                 it.current()->saveXML(schs);
00519                 //kdDebug()<<k_funcinfo<<m_name<<" id="<<it.current()->id()<<(it.current()->isDeleted()?"  Deleted":"")<<endl;
00520                 Node::saveAppointments(schs, it.current()->id());
00521             }
00522         }
00523     }
00524 }
00525 
00526 void Project::setParentSchedule(Schedule *sch) {
00527     QPtrListIterator<Node> it = m_nodes;
00528     for (; it.current(); ++it) {
00529         it.current()->setParentSchedule(sch);
00530     }
00531 }
00532 
00533 void Project::addResourceGroup(ResourceGroup * group) {
00534     m_resourceGroups.append(group);
00535 }
00536 
00537 
00538 void Project::removeResourceGroup(ResourceGroup * group){
00539     m_resourceGroups.remove(group);
00540 }
00541 
00542 
00543 void Project::removeResourceGroup(int /* number */){
00544    // always auto remove
00545 }
00546 
00547 
00548 void Project::insertResourceGroup( unsigned int /* index */,
00549                   ResourceGroup * /* resource */) {
00550 }
00551 
00552 QPtrList<ResourceGroup> &Project::resourceGroups() {
00553      return m_resourceGroups;
00554 }
00555 
00556 bool Project::addTask( Node* task, Node* position )
00557 {
00558     // we want to add a task at the given position. => the new node will
00559     // become next sibling right after position.
00560     if ( 0 == position ) {
00561       kdError()<<k_funcinfo<<"position=0, could not add task: "<<task->name()<<endl;
00562       return false;
00563     }
00564     //kdDebug()<<k_funcinfo<<"Add "<<task->name()<<" after "<<position->name()<<endl;
00565     // in case we want to add to the main project, we make it child element
00566     // of the root element.
00567     if ( Node::Type_Project == position->type() ) {
00568         return addSubTask(task, position);
00569     }
00570     // find the position
00571     // we have to tell the parent that we want to delete one of its children
00572     Node* parentNode = position->getParent();
00573     if ( !parentNode ) {
00574         kdDebug()<<k_funcinfo<<"parent node not found???"<<endl;
00575         return false;
00576     }
00577     int index = parentNode->findChildNode( position );
00578     if ( -1 == index ) {
00579         // ok, it does not exist
00580         kdDebug()<<k_funcinfo<<"Task not found???"<<endl;
00581         return false;
00582     }
00583     return addSubTask(task, index+1, parentNode);
00584 }
00585 
00586 bool Project::addSubTask( Node* task, Node* position )
00587 {
00588     // we want to add a subtask to the node "position". It will become
00589     // position's last child.
00590     if ( 0 == position ) {
00591         kdError()<<k_funcinfo<<"No parent, can not add subtask: "<<task->name()<<endl;
00592         return false;
00593     }
00594     if (!registerNodeId(task)) {
00595         kdError()<<k_funcinfo<<"Failed to register node id, can not add subtask: "<<task->name()<<endl;
00596         return false;
00597     }
00598     position->addChildNode(task);
00599     return true;
00600 }
00601 
00602 bool Project::addSubTask( Node* task, int index, Node* parent )
00603 {
00604     // we want to add a subtask to the node "parent" at the given index.
00605     if ( 0 == parent ) {
00606         kdError()<<k_funcinfo<<"No parent, can not add subtask: "<<task->name()<<endl;
00607         return false;
00608     }
00609     if (!registerNodeId(task)) {
00610         kdError()<<k_funcinfo<<"Failed to register node id, can not add subtask: "<<task->name()<<endl;
00611         return false;
00612     }
00613     parent->insertChildNode(index, task);
00614     return true;
00615 }
00616 
00617 void Project::delTask(Node *node)
00618 {
00619     Node *parent = node->getParent();
00620     if (parent == 0) {
00621         kdDebug()<<k_funcinfo<<"Node must have a parent!"<<endl;
00622         return;
00623     }
00624     removeId(node->id());
00625     parent->delChildNode(node, false/*take*/);
00626 }
00627 
00628 bool Project::canIndentTask(Node* node)
00629 {
00630     if (0 == node) {
00631         // should always be != 0. At least we would get the Project,
00632         // but you never know who might change that, so better be careful
00633         return false;
00634     }
00635     if (node->type() == Node::Type_Project) {
00636         //kdDebug()<<k_funcinfo<<"The root node cannot be indented"<<endl;
00637         return false;
00638     }
00639     // we have to find the parent of task to manipulate its list of children
00640     Node* parentNode = node->getParent();
00641     if ( !parentNode ) {
00642         return false;
00643     }
00644     if (parentNode->findChildNode(node) == -1) {
00645         kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
00646         return false;
00647     }
00648     Node *sib = node->siblingBefore();
00649     if (!sib) {
00650         //kdDebug()<<k_funcinfo<<"new parent node not found"<<endl;
00651         return false;
00652     }
00653     if (node->findParentRelation(sib) || node->findChildRelation(sib)) {
00654         //kdDebug()<<k_funcinfo<<"Cannot have relations to parent"<<endl;
00655         return false;
00656     }
00657     return true;
00658 }
00659 
00660 bool Project::indentTask( Node* node )
00661 {
00662     if (canIndentTask(node)) {
00663         Node *newParent = node->siblingBefore();
00664         node->getParent()->delChildNode(node, false/*do not delete objekt*/);
00665         newParent->addChildNode(node);
00666         return true;
00667     }
00668     return false;
00669 }
00670 
00671 bool Project::canUnindentTask( Node* node )
00672 {
00673     if ( 0 == node ) {
00674         // is always != 0. At least we would get the Project, but you
00675         // never know who might change that, so better be careful
00676         return false;
00677     }
00678     if ( Node::Type_Project == node->type() ) {
00679         //kdDebug()<<k_funcinfo<<"The root node cannot be unindented"<<endl;
00680         return false;
00681     }
00682     // we have to find the parent of task to manipulate its list of children
00683     // and we need the parent's parent too
00684     Node* parentNode = node->getParent();
00685     if ( !parentNode ) {
00686         return false;
00687     }
00688     Node* grandParentNode = parentNode->getParent();
00689     if ( !grandParentNode ) {
00690         //kdDebug()<<k_funcinfo<<"This node already is at the top level"<<endl;
00691         return false;
00692     }
00693     int index = parentNode->findChildNode( node );
00694     if ( -1 == index ) {
00695         kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
00696         return false;
00697     }
00698     return true;
00699 }
00700 
00701 bool Project::unindentTask( Node* node )
00702 {
00703     if (canUnindentTask(node)) {
00704         Node *parentNode = node->getParent();
00705         Node *grandParentNode = parentNode->getParent();
00706         parentNode->delChildNode(node, false/*do not delete objekt*/);
00707         grandParentNode->addChildNode(node,parentNode);
00708         return true;
00709     }
00710     return false;
00711 }
00712 
00713 bool Project::canMoveTaskUp( Node* node )
00714 {
00715     if (node == 0)
00716         return false; // safety
00717     // we have to find the parent of task to manipulate its list of children
00718     Node* parentNode = node->getParent();
00719     if (!parentNode) {
00720         //kdDebug()<<k_funcinfo<<"No parent found"<<endl;
00721         return false;
00722     }
00723     if (parentNode->findChildNode(node) == -1) {
00724         kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
00725         return false;
00726     }
00727     if (node->siblingBefore()) {
00728         return true;
00729     }
00730     return false;
00731 }
00732 
00733 bool Project::moveTaskUp( Node* node )
00734 {
00735     if (canMoveTaskUp(node)) {
00736         return node->getParent()->moveChildUp(node);
00737     }
00738     return false;
00739 }
00740 
00741 bool Project::canMoveTaskDown( Node* node )
00742 {
00743     if (node == 0)
00744         return false; // safety
00745     // we have to find the parent of task to manipulate its list of children
00746     Node* parentNode = node->getParent();
00747     if (!parentNode) {
00748         return false;
00749     }
00750     if (parentNode->findChildNode(node) == -1) {
00751         kdError()<<k_funcinfo<<"Tasknot found???"<<endl;
00752         return false;
00753     }
00754     if (node->siblingAfter()) {
00755         return true;
00756     }
00757     return false;
00758 }
00759 
00760 bool Project::moveTaskDown( Node* node )
00761 {
00762     if (canMoveTaskDown(node)) {
00763         return node->getParent()->moveChildDown(node);
00764     }
00765     return false;
00766 }
00767 
00768 Task *Project::createTask(Node* parent) {
00769     Task* node = new Task(parent);
00770     node->setId(uniqueNodeId());
00771     return node;
00772 }
00773 
00774 Task *Project::createTask(Task &def, Node* parent) {
00775     Task* node = new Task(def, parent);
00776     node->setId(uniqueNodeId());
00777     return node;
00778 }
00779 
00780 QString Project::uniqueNodeId(int seed) {
00781     int i = seed;
00782     while (findNode(QString("%1").arg(i))) {
00783         ++i;
00784     }
00785     return QString("%1").arg(i);
00786 }
00787 
00788 bool Project::removeId(const QString &id) {
00789     kdDebug()<<k_funcinfo<<"id="<<id<<endl;
00790     return (m_parent ? m_parent->removeId(id) : nodeIdDict.remove(id)); 
00791 }
00792 
00793 void Project::insertId(const QString &id, const Node *node) {
00794     kdDebug()<<k_funcinfo<<"id="<<id<<" "<<node->name()<<endl;
00795     m_parent ? m_parent->insertId(id, node) : nodeIdDict.insert(id, node); 
00796 }
00797 
00798 bool Project::registerNodeId(Node *node) {
00799     if (node->id().isEmpty()) {
00800        kdError()<<k_funcinfo<<"Id is empty."<<endl;
00801        return false;
00802     }
00803     Node *rn = findNode(node->id());
00804     if (rn == 0) {
00805        insertId(node->id(), node);
00806        return true;
00807     }
00808     if (rn != node) {
00809        kdError()<<k_funcinfo<<"Id allready exists for different task: "<<node->id()<<endl;
00810        return false;
00811     }
00812     return true;
00813 }
00814 
00815 
00816 ResourceGroup *Project::group(QString id) {
00817     return findResourceGroup(id);
00818 }
00819 
00820 Resource *Project::resource(QString id) {
00821     return findResource(id);
00822 }
00823 
00824 // TODO
00825 EffortCostMap Project::plannedEffortCostPrDay(const QDate &/*start*/, const QDate &/*end*/) const {
00826     //kdDebug()<<k_funcinfo<<endl;
00827     EffortCostMap ec;
00828     return ec;
00829 
00830 }
00831 
00832 // Returns the total planned effort for this project (or subproject) 
00833 Duration Project::plannedEffort() {
00834    //kdDebug()<<k_funcinfo<<endl;
00835     Duration eff;
00836     QPtrListIterator<Node> it(childNodeIterator());
00837     for (; it.current(); ++it) {
00838         eff += it.current()->plannedEffort();
00839     }
00840     return eff;
00841 }
00842 
00843 // Returns the total planned effort for this project (or subproject) on date
00844 Duration Project::plannedEffort(const QDate &date) {
00845    //kdDebug()<<k_funcinfo<<endl;
00846     Duration eff;
00847     QPtrListIterator<Node> it(childNodeIterator());
00848     for (; it.current(); ++it) {
00849         eff += it.current()->plannedEffort(date);
00850     }
00851     return eff;
00852 }
00853 
00854 // Returns the total planned effort for this project (or subproject) upto and including date
00855 Duration Project::plannedEffortTo(const QDate &date) {
00856    //kdDebug()<<k_funcinfo<<endl;
00857     Duration eff;
00858     QPtrListIterator<Node> it(childNodeIterator());
00859     for (; it.current(); ++it) {
00860         eff += it.current()->plannedEffortTo(date);
00861     }
00862     return eff;
00863 }
00864 
00865 // Returns the total actual effort for this project (or subproject) 
00866 Duration Project::actualEffort() {
00867    //kdDebug()<<k_funcinfo<<endl;
00868     Duration eff;
00869     QPtrListIterator<Node> it(childNodeIterator());
00870     for (; it.current(); ++it) {
00871         eff += it.current()->actualEffort();
00872     }
00873     return eff;
00874 }
00875 
00876 // Returns the total actual effort for this project (or subproject) on date
00877 Duration Project::actualEffort(const QDate &date) {
00878    //kdDebug()<<k_funcinfo<<endl;
00879     Duration eff;
00880     QPtrListIterator<Node> it(childNodeIterator());
00881     for (; it.current(); ++it) {
00882         eff += it.current()->actualEffort(date);
00883     }
00884     return eff;
00885 }
00886 
00887 // Returns the total actual effort for this project (or subproject) upto and including date
00888 Duration Project::actualEffortTo(const QDate &date) {
00889    //kdDebug()<<k_funcinfo<<endl;
00890     Duration eff;
00891     QPtrListIterator<Node> it(childNodeIterator());
00892     for (; it.current(); ++it) {
00893         eff += it.current()->actualEffortTo(date);
00894     }
00895     return eff;
00896 }
00897 
00898 double Project::plannedCost() {
00899     //kdDebug()<<k_funcinfo<<endl;
00900     double c = 0;
00901     QPtrListIterator<Node> it(childNodeIterator());
00902     for (; it.current(); ++it) {
00903         c += it.current()->plannedCost();
00904     }
00905     return c;
00906 }
00907 
00908 // Returns the total planned effort for this project (or subproject) on date
00909 double Project::plannedCost(const QDate &date) {
00910    //kdDebug()<<k_funcinfo<<endl;
00911     double c = 0;
00912     QPtrListIterator<Node> it(childNodeIterator());
00913     for (; it.current(); ++it) {
00914         c += it.current()->plannedCost(date);
00915     }
00916     return c;
00917 }
00918 
00919 // Returns the total planned effort for this project (or subproject) upto and including date
00920 double Project::plannedCostTo(const QDate &date) {
00921    //kdDebug()<<k_funcinfo<<endl;
00922     double c = 0;
00923     QPtrListIterator<Node> it(childNodeIterator());
00924     for (; it.current(); ++it) {
00925         c += it.current()->plannedCostTo(date);
00926     }
00927     return c;
00928 }
00929 
00930 double Project::actualCost() {
00931     //kdDebug()<<k_funcinfo<<endl;
00932     double c = 0;
00933     QPtrListIterator<Node> it(childNodeIterator());
00934     for (; it.current(); ++it) {
00935         c += it.current()->actualCost();
00936     }
00937     return c;
00938 }
00939 
00940 // Returns the total planned effort for this project (or subproject) on date
00941 double Project::actualCost(const QDate &date) {
00942    //kdDebug()<<k_funcinfo<<endl;
00943     double c = 0;
00944     QPtrListIterator<Node> it(childNodeIterator());
00945     for (; it.current(); ++it) {
00946         c += it.current()->actualCost(date);
00947     }
00948     return c;
00949 }
00950 
00951 // Returns the total planned effort for this project (or subproject) upto and including date
00952 double Project::actualCostTo(const QDate &date) {
00953    //kdDebug()<<k_funcinfo<<endl;
00954     double c = 0;
00955     QPtrListIterator<Node> it(childNodeIterator());
00956     for (; it.current(); ++it) {
00957         c += it.current()->actualCostTo(date);
00958     }
00959     return c;
00960 }
00961 
00962 void Project::addCalendar(Calendar *calendar) {
00963     //kdDebug()<<k_funcinfo<<calendar->name()<<endl;
00964     m_calendars.append(calendar);
00965 }
00966 
00967 Calendar *Project::calendar(const QString id) const {
00968     return findCalendar(id);
00969 }
00970 
00971 QPtrList<Calendar> Project::calendars() {
00972     QPtrList<Calendar> list;
00973     QPtrListIterator<Calendar> it = m_calendars;
00974     for (; it.current(); ++it) {
00975         if (!it.current()->isDeleted()) {
00976             list.append(it.current());
00977         }
00978     }
00979     return list;
00980 }
00981 
00982 void Project::setStandardWorktime(StandardWorktime * worktime) {
00983     if (m_standardWorktime != worktime) {
00984         delete m_standardWorktime; 
00985         m_standardWorktime = worktime; 
00986     }
00987 }
00988 
00989 bool Project::legalToLink(Node *par, Node *child) {
00990     //kdDebug()<<k_funcinfo<<par.name()<<" ("<<par.numDependParentNodes()<<" parents) "<<child.name()<<" ("<<child.numDependChildNodes()<<" children)"<<endl;
00991     
00992     if (!child || par->isDependChildOf(child)) {
00993         return false;
00994     }
00995     bool legal = true;
00996     // see if par/child is related
00997     if (par->isParentOf(child) || child->isParentOf(par)) {
00998         legal = false;
00999     }
01000     if (legal)
01001         legal = legalChildren(par, child);
01002     if (legal)
01003         legal = legalParents(par, child);
01004     
01005     return legal;
01006 }
01007 
01008 bool Project::legalParents(Node *par, Node *child) {
01009     bool legal = true;
01010     //kdDebug()<<k_funcinfo<<par->name()<<" ("<<par->numDependParentNodes()<<" parents) "<<child->name()<<" ("<<child->numDependChildNodes()<<" children)"<<endl;
01011     for (int i=0; i < par->numDependParentNodes() && legal; ++i) {
01012         Node *pNode = par->getDependParentNode(i)->parent();
01013         if (child->isParentOf(pNode) || pNode->isParentOf(child)) {
01014             //kdDebug()<<k_funcinfo<<"Found: "<<pNode->name()<<" is related to "<<child->name()<<endl;
01015             legal = false;
01016         } else {
01017             legal = legalChildren(pNode, child);
01018         }
01019         if (legal)
01020             legal = legalParents(pNode, child);
01021     }
01022     return legal;
01023 }
01024 
01025 bool Project::legalChildren(Node *par, Node *child) {
01026     bool legal = true;
01027     //kdDebug()<<k_funcinfo<<par->name()<<" ("<<par->numDependParentNodes()<<" parents) "<<child->name()<<" ("<<child->numDependChildNodes()<<" children)"<<endl;
01028     for (int j=0; j < child->numDependChildNodes() && legal; ++j) {
01029         Node *cNode = child->getDependChildNode(j)->child();
01030         if (par->isParentOf(cNode) || cNode->isParentOf(par)) {
01031             //kdDebug()<<k_funcinfo<<"Found: "<<par->name()<<" is related to "<<cNode->name()<<endl;
01032             legal = false;
01033         } else {
01034             legal = legalChildren(par, cNode);
01035         }
01036     }
01037     return legal;
01038 }
01039 
01040 void Project::generateWBS(int count, WBSDefinition &def, QString wbs) {
01041     if (type() == Type_Subproject || def.level0Enabled()) {
01042         Node::generateWBS(count, def, wbs);
01043     } else {
01044         QPtrListIterator<Node> it = m_nodes;
01045         for (int i=0; it.current(); ++it) {
01046             it.current()->generateWBS(++i, def, m_wbs);
01047         }
01048     }
01049 }
01050 
01051 void Project::setCurrentSchedule(long id) {
01052     setCurrentSchedulePtr(findSchedule(id));
01053     Node::setCurrentSchedule(id);
01054     QDictIterator<Resource> it = resourceIdDict;
01055     for (; it.current(); ++it) {
01056         it.current()->setCurrentSchedule(id);
01057     }
01058 }
01059 
01060 MainSchedule *Project::createSchedule(QString name, Schedule::Type type) {
01061     //kdDebug()<<k_funcinfo<<"No of schedules: "<<m_schedules.count()<<endl;
01062     long i=1;
01063     while (m_schedules.find(i)) {
01064         ++i;
01065     }
01066     MainSchedule *sch = new MainSchedule(this, name, type, i);
01067     addSchedule(sch);
01068     return sch;
01069 }
01070 
01071 bool Project::removeCalendarId(const QString &id) {
01072     kdDebug()<<k_funcinfo<<"id="<<id<<endl;
01073     return calendarIdDict.remove(id); 
01074 }
01075 
01076 void Project::insertCalendarId(const QString &id, const Calendar *calendar) { 
01077     kdDebug()<<k_funcinfo<<"id="<<id<<": "<<calendar->name()<<endl;
01078     calendarIdDict.insert(id, calendar);
01079 }
01080         
01081 #ifndef NDEBUG
01082 void Project::printDebug(bool children, QCString indent) {
01083 
01084     kdDebug()<<indent<<"+ Project node: "<<name()<<endl;
01085     indent += "!";
01086     QPtrListIterator<ResourceGroup> it(resourceGroups());
01087     for ( ; it.current(); ++it)
01088         it.current()->printDebug(indent);
01089 
01090     Node::printDebug(children, indent);
01091 }
01092 void Project::printCalendarDebug(QCString indent) {
01093     kdDebug()<<indent<<"-------- Calendars debug printout --------"<<endl;
01094     QPtrListIterator<Calendar> it = m_calendars;
01095     for (; it.current(); ++it) {
01096         it.current()->printDebug(indent + "--");
01097         kdDebug()<<endl;
01098     }
01099     if (m_standardWorktime)
01100         m_standardWorktime->printDebug();
01101 }
01102 #endif
01103 
01104 }  //KPlato namespace
KDE Home | KDE Accessibility Home | Description of Access Keys