diff --git a/src/libs/kernel/kptcalendar.h b/src/libs/kernel/kptcalendar.h index 14feefe3..8678538e 100644 --- a/src/libs/kernel/kptcalendar.h +++ b/src/libs/kernel/kptcalendar.h @@ -1,695 +1,694 @@ /* This file is part of the KDE project Copyright (C) 2003 - 2007 Dag Andersen Copyright (C) 2011 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTCALENDAR_H #define KPTCALENDAR_H #include "kptdatetime.h" #include "kptduration.h" #include "kptdebug.h" #include "plankernel_export.h" #include #include #include #include #include #ifdef HAVE_KHOLIDAYS namespace KHolidays { class HolidayRegion; } #endif class KUndo2Command; class QDomElement; class QStringList; /// The main namespace. namespace KPlato { class Calendar; class Project; class IntMap; //class DateTime; class Project; class Schedule; class XMLLoaderObject; class AppointmentIntervalList; class PLANKERNEL_EXPORT DateTimeInterval : public std::pair { public: DateTimeInterval() : std::pair() {} DateTimeInterval( const DateTime &t1, const DateTime &t2 ) : std::pair( t1, t2 ) {} DateTimeInterval &operator=( const DateTimeInterval &other ) { first = other.first; second = other.second; return *this; } bool isValid() const { return first.isValid() && second.isValid(); } void limitTo( const DateTime &start, const DateTime &end ) { if ( ! first.isValid() || ( start.isValid() && start > first ) ) { first = start; } if ( ! second.isValid() || ( end.isValid() && end < second ) ) { second = end; } if ( isValid() && first > second ) { first = second = DateTime(); } } void limitTo( const DateTimeInterval &interval ) { limitTo( interval.first, interval.second ); } DateTimeInterval limitedTo( const DateTime &start, const DateTime &end ) const { DateTimeInterval i = *this; i.limitTo( start, end ); return i; } DateTimeInterval limitedTo( const DateTimeInterval &interval ) const { return limitedTo( interval.first, interval.second ); } QString toString() const { return QStringLiteral( "%1 to %2" ) - .arg( first.isValid()?first.toString():QStringLiteral("''") ) - .arg( second.isValid()?second.toString():QStringLiteral("''") ); + .arg(first.isValid()?first.toString():QStringLiteral("''"), second.isValid()?second.toString():QStringLiteral("''")); } }; /// TimeInterval is defined as a start time and a length. /// The end time (start + length) must not exceed midnight class PLANKERNEL_EXPORT TimeInterval : public std::pair { public: TimeInterval() : std::pair( QTime(), -1 ) {} explicit TimeInterval( std::pair value ) : std::pair( value ) { init(); } TimeInterval( QTime start, int length ) : std::pair( start, length ) { init(); } TimeInterval( const TimeInterval &value ) : std::pair( value.first, value.second ) { init(); } /// Return the intervals start time QTime startTime() const { return first; } /// Return the intervals calculated end time. Note: It may return QTime(0,0,0) QTime endTime() const { return first.addMSecs( second ); } double hours() const { return (double)(second) / ( 1000. * 60. * 60. ); } /// Returns true if this interval ends at midnight, and thus endTime() returns QTime(0,0,0) bool endsMidnight() const { return endTime() == QTime( 0, 0, 0 ); } bool isValid() const { return first.isValid() && second > 0; } bool isNull() const { return first.isNull() || second < 0; } TimeInterval &operator=( const TimeInterval &ti ) { first = ti.first; second = ti.second; return *this; } /// Returns true if the intervals overlap in any way bool intersects( const TimeInterval &ti ) const { if ( ! isValid() || ! ti.isValid() ) { return false; } if ( endsMidnight() && ti.endsMidnight() ) { return true; } if ( endsMidnight() ) { return first < ti.endTime(); } if ( ti.endsMidnight() ) { return ti.first < endTime(); } return ( first < ti.endTime() && endTime() > ti.first ) || ( ti.first < endTime() && ti.endTime() > first ); } protected: void init() { int s = QTime( 0, 0, 0 ).msecsTo( first ); if ( ( s + second ) > 86400000 ) { second = 86400000 - s; errorPlan<<"Overflow, limiting length to"< timeIntervals() const { return m_timeIntervals; } void addInterval( QTime t1, int length ) { addInterval( new TimeInterval( t1, length ) ); } /** * Caller needs to ensure that intervals are not overlapping. */ void addInterval(TimeInterval *interval); void addInterval(TimeInterval interval) { addInterval(new TimeInterval(interval)); } void clearIntervals() { m_timeIntervals.clear(); } void setIntervals(const QList &intervals) { m_timeIntervals.clear(); m_timeIntervals = intervals; } void removeInterval( TimeInterval *interval ); bool hasInterval( const TimeInterval *interval ) const; int numIntervals() const; DateTime start() const; DateTime end() const; QDate date() const { return m_date; } void setDate(QDate date) { m_date = date; } int state() const { return m_state; } void setState(int state) { m_state = state; } bool operator==(const CalendarDay *day) const; bool operator==(const CalendarDay &day) const; bool operator!=(const CalendarDay *day) const; bool operator!=(const CalendarDay &day) const; Duration workDuration() const; /** * Returns the amount of 'worktime' that can be done on * this day between the times start and end. */ Duration effort(QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0); /** * Returns the amount of 'worktime' that can be done on * this day between the times start and end. */ Duration effort(QDate date, QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0); /** * Returns the actual 'work interval' for the interval start to end. * If no 'work interval' exists, returns the interval start, end. * Use @ref hasInterval() to check if a 'work interval' exists. */ TimeInterval interval(QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0) const; /** * Returns the actual 'work interval' for the interval start to end. * If no 'work interval' exists, returns the interval start, end. * Use @ref hasInterval() to check if a 'work interval' exists. */ TimeInterval interval(QDate date, QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0) const; bool hasInterval() const; /** * Returns true if at least a part of a 'work interval' exists * for the interval start to end. */ bool hasInterval(QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0) const; /** * Returns true if at least a part of a 'work interval' exists * for the interval @p start to @p start + @p length. * Assumes this day is date. (Used by weekday hasInterval().) * If @p sch is not 0, the schedule is checked for availability. */ bool hasInterval(QDate date, QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0) const; Duration duration() const; const CalendarDay ©(const CalendarDay &day); static QString stateToString( int st, bool trans = false ); static QStringList stateList( bool trans = false ); private: QDate m_date; //NOTE: inValid if used for weekdays int m_state; Calendar *m_calendar; QList m_timeIntervals; #ifndef NDEBUG public: void printDebug(const QString& indent=QString()); #endif }; class PLANKERNEL_EXPORT CalendarWeekdays { public: CalendarWeekdays(); explicit CalendarWeekdays( const CalendarWeekdays *weekdays ); ~CalendarWeekdays(); bool load( KoXmlElement &element, XMLLoaderObject &status ); void save(QDomElement &element) const; const QList weekdays() const { QList lst = m_weekdays.values(); return lst; } /** * Returns the pointer to CalendarDay for day. * @param day The weekday number, must be between 1 (monday) and 7 (sunday) */ CalendarDay *weekday(int day) const; CalendarDay *weekday(QDate date) const { return weekday(date.dayOfWeek()); } static int dayOfWeek( const QString &name ); const QMap &weekdayMap() const; IntMap stateMap() const; // void setWeekday(IntMap::iterator it, int state) { m_weekdays.at(it.key())->setState(state); } int state(QDate date) const; int state(int weekday) const; void setState(int weekday, int state); QList intervals(int weekday) const; void setIntervals(int weekday, const QList &intervals); void clearIntervals(int weekday); bool operator==(const CalendarWeekdays *weekdays) const; bool operator!=(const CalendarWeekdays *weekdays) const; Duration effort(QDate date, QTime start, int length, const QTimeZone &timeZone, Schedule *sch=0); /** * Returns the actual 'work interval' on the weekday defined by date * for the interval @p start to @p start + @p length. * If no 'work interval' exists, returns the interval start, end. * Use @ref hasInterval() to check if a 'work interval' exists. * If @p sch is not 0, the schedule is checked for availability. */ TimeInterval interval(QDate date, QTime start, int length, const QTimeZone &timeZone, Schedule *sch) const; /** * Returns true if at least a part of a 'work interval' exists * on the weekday defined by date for the interval start to end. */ bool hasInterval(QDate date, QTime start, int length, const QTimeZone &timeZone, Schedule *sch) const; bool hasInterval() const; Duration duration() const; Duration duration(int weekday) const; const CalendarWeekdays ©(const CalendarWeekdays &weekdays); int indexOf( const CalendarDay *day ) const; private: Calendar *m_calendar; QMap m_weekdays; #ifndef NDEBUG public: void printDebug(const QString& indent=QString()); #endif }; /** * Calendar defines the working and nonworking days and hours. * A day can have the three states Undefined, NonWorking, or Working. * A calendar can have a parent calendar that defines the days that are * undefined in this calendar. * If a calendar have no parent, an undefined day defaults to Nonworking. * A Working day has one or more work intervals to define the work hours. * * The definition can consist of two parts: Weekdays and Day. * Day has highest priority. * * A typical calendar hierarchy could include calendars on 4 levels: * 1. Definition of normal weekdays and national holidays/vacation days. * 2. Definition of the company's special workdays/-time and vacation days. * 3. Definitions for groups of resources. * 4. Definitions for individual resources. * * A calendar can define a timezone different from the projects. * This enables planning with resources that does not recide in the same place. * */ class PLANKERNEL_EXPORT Calendar : public QObject { Q_OBJECT public: Calendar(); explicit Calendar(const QString& name, Calendar *parent=0); //Calendar( const Calendar &c ); QObject doesn't allow a copy constructor ~Calendar(); const Calendar &operator=(const Calendar &calendar ) { return copy( calendar ); } QString name() const { return m_name; } void setName(const QString& name); Calendar *parentCal() const { return m_parent; } /** * Set parent calendar to @p parent. * Removes myself from current parent and * inserts myself as child to new parent. */ void setParentCal( Calendar *parent, int pos = -1 ); bool isChildOf( const Calendar *cal ) const; Project *project() const { return m_project; } void setProject(Project *project); QString id() const { return m_id; } void setId(const QString& id); const QList &calendars() const { return m_calendars; } void addCalendar( Calendar *calendar, int pos = -1 ); void takeCalendar( Calendar *calendar ); int indexOf( const Calendar *calendar ) const; /// Return number of children int childCount() const { return m_calendars.count(); } /// Return child calendar at @p index, 0 if index out of bounds Calendar *childAt( int index ) const { return m_calendars.value( index ); } bool load( KoXmlElement &element, XMLLoaderObject &status ); void save(QDomElement &element) const; int state(QDate date) const; void setState( CalendarDay *day, CalendarDay::State state ); void addWorkInterval( CalendarDay *day, TimeInterval *ti ); void takeWorkInterval( CalendarDay *day, TimeInterval *ti ); void setWorkInterval( TimeInterval *ti, const TimeInterval &value ); /** * Find the definition for the day @p date. * If @p skipUndefined = true the day is NOT returned if it has state Undefined. */ CalendarDay *findDay(QDate date, bool skipUndefined=false) const; void addDay(CalendarDay *day); CalendarDay *takeDay(CalendarDay *day); const QList &days() const { return m_days; } QList > consecutiveVacationDays() const; QList workingDays() const; int indexOf( const CalendarDay *day ) const { return m_days.indexOf( const_cast( day ) ); } CalendarDay *dayAt( int index ) { return m_days.value( index ); } int numDays() const { return m_days.count(); } void setDate( CalendarDay *day, QDate date ); CalendarDay *day( QDate date ) const; IntMap weekdayStateMap() const; CalendarWeekdays *weekdays() const { return m_weekdays; } CalendarDay *weekday(int day) const { return m_weekdays->weekday(day); } int indexOfWeekday( const CalendarDay *day ) const { return m_weekdays->indexOf( day ); } const QList weekdayList() const { return m_weekdays->weekdays(); } int numWeekdays() const { return weekdayList().count(); } /// Sets the @p weekday data to the data in @p day void setWeekday( int weekday, const CalendarDay &day ); QString parentId() const { return m_parentId; } void setParentId(const QString& id) { m_parentId = id; } bool hasParent(Calendar *cal); /** * Returns the work intervals in the interval from @p start to @p end * Sets the load of each interval to @p load */ AppointmentIntervalList workIntervals(const DateTime &start, const DateTime &end, double load) const; /** * Returns the amount of 'worktime' that can be done in the * interval from @p start to @p end * If @p sch is not 0, the schedule is checked for availability. */ Duration effort(const DateTime &start, const DateTime &end, Schedule *sch=0) const; /** * Returns the first 'work interval' for the interval * starting at @p start and ending at @p end. * If no 'work interval' exists, returns an interval with invalid DateTime. * You can also use @ref hasInterval() to check if a 'work interval' exists. * If @p sch is not 0, the schedule is checked for availability. */ DateTimeInterval firstInterval(const DateTime &start, const DateTime &end, Schedule *sch=0) const; /** * Returns true if at least a part of a 'work interval' exists * for the interval starting at @p start and ending at @p end. * If @p sch is not 0, the schedule is checked for availability. */ bool hasInterval(const DateTime &start, const DateTime &end, Schedule *sch=0) const; /** * Find the first available time after @p time before @p limit. * Return invalid datetime if not available. * If @p sch is not 0, the schedule is checked for availability. */ DateTime firstAvailableAfter(const DateTime &time, const DateTime &limit, Schedule *sch = 0); /** * Find the first available time backwards from @p time. Search until @p limit. * Return invalid datetime if not available. * If @p sch is not 0, the schedule is checked for availability. */ DateTime firstAvailableBefore(const DateTime &time, const DateTime &limit, Schedule *sch = 0); Calendar *findCalendar() const { return findCalendar(m_id); } Calendar *findCalendar(const QString &id) const; bool removeId() { return removeId(m_id); } bool removeId(const QString &id); void insertId(const QString &id); QTimeZone timeZone() const { return m_timeZone; } void setTimeZone( const QTimeZone &tz ); /// Return the project timezone, or local timezone if no project QTimeZone projectTimeZone() const; void setDefault( bool on ); bool isDefault() const { return m_default; } int cacheVersion() const; void incCacheVersion(); void setCacheVersion( int version ); bool loadCacheVersion( KoXmlElement &element, XMLLoaderObject &status ); void saveCacheVersion( QDomElement &element ) const; /// A calendar can be local to this project, or /// defined externally and shared with other projects bool isShared() const; /// Set calendar to be local if on = false, or shared if on = true void setShared(bool on); #ifdef HAVE_KHOLIDAYS bool isHoliday(QDate date) const; KHolidays::HolidayRegion *holidayRegion() const; void setHolidayRegion(const QString &code); QString holidayRegionCode() const; QStringList holidayRegionCodes() const; #endif Q_SIGNALS: void changed(KPlato::Calendar*); void changed(KPlato::CalendarDay*); void changed(KPlato::TimeInterval*); void weekdayToBeAdded(KPlato::CalendarDay *day, int index); void weekdayAdded(KPlato::CalendarDay *day); void weekdayToBeRemoved(KPlato::CalendarDay *day); void weekdayRemoved(KPlato::CalendarDay *day); void dayToBeAdded(KPlato::CalendarDay *day, int index); void dayAdded(KPlato::CalendarDay *day); void dayToBeRemoved(KPlato::CalendarDay *day); void dayRemoved(KPlato::CalendarDay *day); void workIntervalToBeAdded(KPlato::CalendarDay*, KPlato::TimeInterval*, int index); void workIntervalAdded(KPlato::CalendarDay*, KPlato::TimeInterval*); void workIntervalToBeRemoved(KPlato::CalendarDay*, KPlato::TimeInterval*); void workIntervalRemoved(KPlato::CalendarDay*, KPlato::TimeInterval*); protected: void init(); const Calendar ©(const Calendar &calendar); /** * Returns the amount of 'worktime' that can be done on * the @p date between the times @p start and @p start + @p length. * The date and times are in timespecification @p spec. * If @p sch is not 0, the schedule is checked for availability. */ Duration effort(QDate date, QTime start, int length, Schedule *sch=0) const; /** * Returns the amount of 'worktime' that can be done in the * interval from @p start to @p end * If @p sch is not 0, the schedule is checked for availability. */ Duration effort(const QDateTime &start, const QDateTime &end, Schedule *sch=0) const; /** * Returns the first 'work interval' on date for the interval * starting at @p start and ending at @p start + @p length. * If no 'work interval' exists, returns a null interval. * You can also use @ref hasInterval() to check if a 'work interval' exists. * The date and times are in timespecification spec. * If @p sch is not 0, the schedule is checked for availability. */ TimeInterval firstInterval(QDate date, QTime start, int length, Schedule *sch=0) const; /** * Returns the first 'work interval' for the interval * starting at @p start and ending at @p end. * If no 'work interval' exists, returns an interval with invalid DateTime. */ DateTimeInterval firstInterval( const QDateTime &start, const QDateTime &end, Schedule *sch=0) const; /** * Returns true if at least a part of a 'work interval' exists * for the interval on date, starting at @p start and ending at @p start + @p length. * If @p sch is not 0, the schedule is checked for availability. */ bool hasInterval(QDate date, QTime start, int length, Schedule *sch=0) const; /** * Returns the work intervals in the interval from @p start to @p end * Sets the load of each interval to @p load */ AppointmentIntervalList workIntervals(const QDateTime &start, const QDateTime &end, double load) const; /** * Find the first available time backwards from @p time. Search until @p limit. * Return invalid datetime if not available. * If @p sch is not 0, the schedule is checked for availability. */ DateTime firstAvailableBefore(const QDateTime &time, const QDateTime &limit, Schedule *sch = 0); private: QString m_name; Calendar *m_parent; Project *m_project; bool m_deleted; QString m_id; QString m_parentId; QList m_days; CalendarWeekdays *m_weekdays; QList m_calendars; QTimeZone m_timeZone; bool m_default; // this is the default calendar, only used for save/load bool m_shared; #ifdef HAVE_KHOLIDAYS KHolidays::HolidayRegion *m_region; QString m_regionCode; #endif int m_cacheversion; // incremented every time a calendar is changed friend class Project; int m_blockversion; // don't update if true #ifndef NDEBUG public: void printDebug(const QString& indent=QString()); #endif }; class PLANKERNEL_EXPORT StandardWorktime { public: explicit StandardWorktime( Project *project = 0 ); explicit StandardWorktime(StandardWorktime* worktime); ~StandardWorktime(); /// Set Project void setProject( Project *project ) { m_project = project; } /// The work time of a normal year. Duration durationYear() const { return m_year; } /// The work time of a normal year. double year() const { return m_year.toDouble(Duration::Unit_h); } /// Set the work time of a normal year. void setYear(const Duration year) { m_year = year; } /// Set the work time of a normal year. void setYear(double hours) { m_year = Duration((qint64)(hours*60.0*60.0*1000.0)); } /// The work time of a normal month Duration durationMonth() const { return m_month; } /// The work time of a normal month double month() const { return m_month.toDouble(Duration::Unit_h); } /// Set the work time of a normal month void setMonth(const Duration month) { m_month = month; } /// Set the work time of a normal month void setMonth(double hours) { m_month = Duration((qint64)(hours*60.0*60.0*1000.0)); } /// The work time of a normal week Duration durationWeek() const { return m_week; } /// The work time of a normal week double week() const { return m_week.toDouble(Duration::Unit_h); } /// Set the work time of a normal week void setWeek(const Duration week) { m_week = week; } /// Set the work time of a normal week void setWeek(double hours) { m_week = Duration((qint64)(hours*60.0*60.0*1000.0)); } /// The work time of a normal day Duration durationDay() const { return m_day; } /// The work time of a normal day double day() const { return m_day.toDouble(Duration::Unit_h); } /// Set the work time of a normal day void setDay(const Duration day) { m_day = day; changed(); } /// Set the work time of a normal day void setDay(double hours) { m_day = Duration(hours, Duration::Unit_h); changed(); } QList scales() const; bool load( KoXmlElement &element, XMLLoaderObject &status ); void save(QDomElement &element) const; void changed(); protected: void init(); private: Project *m_project; Duration m_year; Duration m_month; Duration m_week; Duration m_day; }; } //KPlato namespace #endif diff --git a/src/libs/kernel/kptnode.cpp b/src/libs/kernel/kptnode.cpp index 7bcc7d54..f1af1dc7 100644 --- a/src/libs/kernel/kptnode.cpp +++ b/src/libs/kernel/kptnode.cpp @@ -1,1868 +1,1868 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2002 - 2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptnode.h" #include "kptappointment.h" #include "kptaccount.h" #include "kptwbsdefinition.h" #include "kptresource.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include namespace KPlato { Node::Node(Node *parent) : QObject( 0 ), // We don't use qobjects parent m_nodes(), m_dependChildNodes(), m_dependParentNodes(), m_estimate( 0 ), m_blockChanged(false) { //debugPlan<<"("<removeRunning(*this); if (m_startupAccount) m_startupAccount->removeStartup(*this); if (m_shutdownAccount) m_shutdownAccount->removeShutdown(*this); foreach (Schedule *s, m_schedules) { delete s; } m_schedules.clear(); m_parent = 0; //safety } void Node::init() { m_documents.node = this; m_currentSchedule = 0; m_name=""; m_constraint = Node::ASAP; m_estimate = 0; m_visitedForward = false; m_visitedBackward = false; m_runningAccount = 0; m_startupAccount = 0; m_shutdownAccount = 0; m_startupCost = 0.0; m_shutdownCost = 0.0; } QString Node::typeToString( bool trans ) const { return typeToString( (Node::NodeTypes)type(), trans ); } // static QString Node::typeToString( Node::NodeTypes typ, bool trans ) { return typeToStringList( trans ).at( typ ); } // static QStringList Node::typeToStringList( bool trans ) { return QStringList() << ( trans ? i18n( "None" ) : QString( "None" ) ) << ( trans ? i18n( "Project" ) : QString( "Project" ) ) << ( trans ? i18n( "Sub-Project" ) : QString( "Sub-Project" ) ) << ( trans ? i18n( "Task" ) : QString( "Task" ) ) << ( trans ? i18n( "Milestone" ) : QString( "Milestone" ) ) << ( trans ? i18n( "Periodic" ) : QString( "Periodic" ) ) << ( trans ? i18n( "Summary" ) : QString( "Summary-Task" ) ); } void Node::setName(const QString &n) { #ifndef NDEBUG setObjectName( n ); #endif m_name = n; changed(this); } void Node::setLeader(const QString &l) { m_leader = l; changed(this); } void Node::setDescription(const QString &d) { m_description = d; changed(this); } Node *Node::projectNode() { if ((type() == Type_Project) || (type() == Type_Subproject)) { return this; } if (m_parent) return m_parent->projectNode(); // This happens for default tasks return 0; } const Node *Node::projectNode() const { if ((type() == Type_Project) || (type() == Type_Subproject)) { return this; } if (m_parent) return m_parent->projectNode(); // This happens for default tasks return 0; } void Node::takeChildNode( Node *node) { //debugPlan<<"find="<setParentNode(0); if ( t != type() ) { changed( Type ); } } void Node::takeChildNode( int number ) { int t = type(); if (number >= 0 && number < m_nodes.size()) { Node *n = m_nodes.takeAt(number); //debugPlan<<(n?n->id():"null")<<" :"<<(n?n->name():""); if (n) { n->setParentNode( 0 ); } } if ( t != type() ) { changed( Type ); } } void Node::insertChildNode( int index, Node *node ) { int t = type(); if (index == -1) m_nodes.append(node); else m_nodes.insert(index,node); node->setParentNode( this ); if ( t != type() ) { changed( Type ); } } void Node::addChildNode( Node *node, Node *after) { int t = type(); int index = m_nodes.indexOf(after); if (index == -1) { m_nodes.append(node); node->setParentNode( this ); if ( t != type() ) { changed( Type ); } return; } m_nodes.insert(index+1, node); node->setParentNode(this); if ( t != type() ) { changed( Type ); } } int Node::findChildNode( const Node* node ) const { return m_nodes.indexOf( const_cast( node ) ); } bool Node::isChildOf( const Node* node ) const { if ( node == 0 || m_parent == 0 ) { return false; } if ( node == m_parent ) { return true; } return m_parent->isChildOf( node ); } Node* Node::childNode(int number) { //debugPlan<= m_nodes.count() ) { return 0; } return m_nodes.at( number ); } int Node::indexOf( const Node *node ) const { return m_nodes.indexOf( const_cast(node) ); } Duration *Node::getDelay() { /* TODO Calculate the delay of this node. Use the calculated startTime and the set startTime. */ return 0L; } void Node::addDependChildNode( Node *node, Relation::Type p) { addDependChildNode(node,p,Duration()); } void Node::addDependChildNode( Node *node, Relation::Type p, Duration lag) { Relation *relation = new Relation(this, node, p, lag); if (node->addDependParentNode(relation)) m_dependChildNodes.append(relation); else delete relation; } void Node::insertDependChildNode( unsigned int index, Node *node, Relation::Type p) { Relation *relation = new Relation(this, node, p, Duration()); if (node->addDependParentNode(relation)) m_dependChildNodes.insert(index, relation); else delete relation; } bool Node::addDependChildNode( Relation *relation) { if(m_dependChildNodes.indexOf(relation) != -1) return false; m_dependChildNodes.append(relation); return true; } void Node::takeDependChildNode( Relation *rel ) { int i = m_dependChildNodes.indexOf(rel); if ( i != -1 ) { //debugPlan<addDependChildNode(relation)) m_dependParentNodes.append(relation); else delete relation; } void Node::insertDependParentNode( unsigned int index, Node *node, Relation::Type p) { Relation *relation = new Relation(this, node, p, Duration()); if (node->addDependChildNode(relation)) m_dependParentNodes.insert(index,relation); else delete relation; } bool Node::addDependParentNode( Relation *relation) { if(m_dependParentNodes.indexOf(relation) != -1) return false; m_dependParentNodes.append(relation); return true; } void Node::takeDependParentNode( Relation *rel ) { int i = m_dependParentNodes.indexOf(rel); if ( i != -1 ) { //debugPlan<( node ) ) != -1) return true; QListIterator nit(childNodeIterator()); while (nit.hasNext()) { if (nit.next()->isParentOf(node)) return true; } return false; } Relation *Node::findParentRelation( const Node *node ) const { for (int i=0; iparent() == node) return rel; } return (Relation *)0; } Relation *Node::findChildRelation( const Node *node) const { for (int i=0; ichild() == node) return rel; } return (Relation *)0; } Relation *Node::findRelation( const Node *node ) const { Relation *rel = findParentRelation(node); if (!rel) rel = findChildRelation(node); return rel; } bool Node::isDependChildOf( const Node *node ) const { //debugPlan<<" '"<name()<<"'"; for (int i=0; iparent() == node) return true; if (rel->parent()->isDependChildOf(node)) return true; } return false; } QList Node::getParentNodes() { this->m_parentNodes.clear(); foreach(Relation * currentRelation, this->dependParentNodes()) { if (!this->m_parentNodes.contains(currentRelation->parent())) { this->m_parentNodes.append(currentRelation->parent()); } } return this->m_parentNodes; } bool Node::canMoveTo( const Node *newParent ) const { if ( m_parent == newParent ) { return true; } if ( newParent->isChildOf( this ) ) { return false; } if ( isDependChildOf( newParent ) || newParent->isDependChildOf( this ) ) { debugPlan<<"Can't move, node is dependent on new parent"; return false; } foreach ( Node *n, m_nodes ) { if ( !n->canMoveTo( newParent ) ) { return false; } } return true; } void Node::makeAppointments() { QListIterator nit(m_nodes); while (nit.hasNext()) { nit.next()->makeAppointments(); } } void Node::calcResourceOverbooked() { QListIterator nit(m_nodes); while (nit.hasNext()) { nit.next()->calcResourceOverbooked(); } } // Returns the (previously) calculated duration Duration Node::duration( long id ) const { Schedule *s = schedule( id ); return s ? s->duration : Duration::zeroDuration; } double Node::variance( long id, Duration::Unit unit ) const { double d = deviation( id, unit ); return d * d; } double Node::deviation( long id, Duration::Unit unit ) const { Schedule *s = schedule( id ); double d = 0.0; if ( s && m_estimate ) { d = s->duration.toDouble( unit ); double o = ( d * ( 100 + m_estimate->optimisticRatio() ) ) / 100; double p = ( d * ( 100 + m_estimate->pessimisticRatio() ) ) / 100; d = ( p - o ) / 6; } return d; } DateTime Node::startTime( long id ) const { Schedule *s = schedule( id ); return s ? s->startTime : DateTime(); } DateTime Node::endTime( long id ) const { Schedule *s = schedule( id ); return s ? s->endTime : DateTime(); } DateTime Node::appointmentStartTime( long id ) const { Schedule *s = schedule( id ); return s ? s->appointmentStartTime() : DateTime(); } DateTime Node::appointmentEndTime( long id ) const { Schedule *s = schedule( id ); return s ? s->appointmentEndTime() : DateTime(); } void Node::setDuration(const Duration &duration, long id ) { Schedule *s = schedule( id ); if ( s ) { s->duration = duration; } } void Node::setEarlyStart(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->earlyStart = dt; } DateTime Node::earlyStart( long id ) const { Schedule *s = schedule( id ); return s ? s->earlyStart : DateTime(); } void Node::setLateStart(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->lateStart = dt; } DateTime Node::lateStart( long id ) const { Schedule *s = schedule( id ); return s ? s->lateStart : DateTime(); } void Node::setEarlyFinish(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->earlyFinish = dt; } DateTime Node::earlyFinish( long id ) const { Schedule *s = schedule( id ); return s ? s->earlyFinish : DateTime(); } void Node::setLateFinish(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->lateFinish = dt; } DateTime Node::lateFinish( long id ) const { Schedule *s = schedule( id ); return s ? s->lateFinish : DateTime(); } DateTime Node::workStartTime( long id ) const { Schedule *s = schedule( id ); return s ? s->workStartTime : DateTime(); } void Node::setWorkStartTime(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->workStartTime = dt; } DateTime Node::workEndTime( long id ) const { Schedule *s = schedule( id ); return s ? s->workEndTime : DateTime(); } void Node::setWorkEndTime(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->workEndTime = dt; } bool Node::inCriticalPath( long id ) const { Schedule *s = schedule( id ); return s ? s->inCriticalPath : false; } QStringList Node::schedulingStatus( long id, bool trans ) const { Schedule *s = schedule( id ); QStringList lst; if ( s ) { lst = s->state(); } if ( lst.isEmpty() ) { lst.append( trans ? i18n( "Not scheduled" ) : QString( "Not scheduled" ) ); } return lst; } bool Node::resourceError( long id ) const { Schedule *s = schedule( id ); return s ? s->resourceError : false; } bool Node::resourceOverbooked( long id ) const { Schedule *s = schedule( id ); return s ? s->resourceOverbooked : false; } bool Node::resourceNotAvailable( long id ) const { Schedule *s = schedule( id ); return s ? s->resourceNotAvailable : false; } bool Node::constraintError( long id ) const { Schedule *s = schedule( id ); return s ? s->constraintError : false; } bool Node::schedulingError( long id ) const { Schedule *s = schedule( id ); return s ? s->schedulingError : false; } bool Node::notScheduled( long id ) const { if ( type() == Type_Summarytask ) { // i am scheduled if al least on child is scheduled foreach ( Node *n, m_nodes ) { if ( ! n->notScheduled( id ) ) { return false; } } return true; } Schedule *s = schedule( id ); return s == 0 || s->isDeleted() || s->notScheduled; } QStringList Node::overbookedResources( long id ) const { Schedule *s = schedule( id ); return s ? s->overbookedResources() : QStringList(); } void Node::saveWorkPackageXML( QDomElement &, long ) const { return; } void Node::saveRelations(QDomElement &element) const { QListIterator it(m_dependChildNodes); while (it.hasNext()) { it.next()->save(element); } QListIterator nodes(m_nodes); while (nodes.hasNext()) { nodes.next()->saveRelations(element); } } void Node::setConstraint(Node::ConstraintType type) { m_constraint = type; changed( this ); } void Node::setConstraint(const QString &type) { // Do not i18n these, they are used in load() if (type == "ASAP") setConstraint(ASAP); else if (type == "ALAP") setConstraint(ALAP); else if (type == "MustStartOn") setConstraint(MustStartOn); else if (type == "MustFinishOn") setConstraint(MustFinishOn); else if (type == "StartNotEarlier") setConstraint(StartNotEarlier); else if (type == "FinishNotLater") setConstraint(FinishNotLater); else if (type == "FixedInterval") setConstraint(FixedInterval); else setConstraint(ASAP); // default } QString Node::constraintToString( bool trans ) const { return constraintList( trans ).at( m_constraint ); } QStringList Node::constraintList( bool trans ) { // keep theses in the same order as the enum! return QStringList() << (trans ? i18n("As Soon As Possible") : QString("ASAP")) << (trans ? i18n("As Late As Possible") : QString("ALAP")) << (trans ? i18n("Must Start On") : QString("MustStartOn")) << (trans ? i18n("Must Finish On") : QString("MustFinishOn")) << (trans ? i18n("Start Not Earlier") : QString("StartNotEarlier")) << (trans ? i18n("Finish Not Later") : QString("FinishNotLater")) << (trans ? i18n("Fixed Interval") : QString("FixedInterval")); } void Node::propagateEarliestStart(DateTime &time) { if (m_currentSchedule == 0) { return; } if ( type() != Type_Project ) { m_currentSchedule->earlyStart = time; if ( m_currentSchedule->lateStart.isValid() && m_currentSchedule->lateStart < time ) { m_currentSchedule->lateStart = time; } //m_currentSchedule->logDebug( "propagateEarliestStart: " + time.toString() ); switch ( m_constraint ) { case FinishNotLater: case MustFinishOn: if ( m_constraintEndTime < time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( QString( "%1: end constraint %2 < %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); + m_currentSchedule->logDebug(QString("%1: end constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString())); #endif } break; case MustStartOn: case FixedInterval: if ( m_constraintStartTime < time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( QString( "%1: start constraint %2 < %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); + m_currentSchedule->logDebug(QString("%1: start constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString())); #endif } break; default: break; } } //debugPlan<earlyStart; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->propagateEarliestStart(time); } } void Node::propagateLatestFinish(DateTime &time) { if (m_currentSchedule == 0) { return; } if ( type() != Type_Project ) { m_currentSchedule->lateFinish = time; if ( m_currentSchedule->earlyFinish.isValid() && m_currentSchedule->earlyFinish > time ) { m_currentSchedule->earlyFinish = time; } switch ( m_constraint ) { case StartNotEarlier: case MustStartOn: if ( m_constraintStartTime > time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( QString( "%1: start constraint %2 < %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); + m_currentSchedule->logDebug(QString("%1: start constraint %2 < %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString())); #endif } break; case MustFinishOn: case FixedInterval: if ( m_constraintEndTime > time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( QString( "%1: end constraint %2 > %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); + m_currentSchedule->logDebug(QString("%1: end constraint %2 > %3").arg(constraintToString(true), m_constraintEndTime.toString(), time.toString())); #endif } break; default: break; } } //debugPlan<lateFinish; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->propagateLatestFinish(time); } } void Node::moveEarliestStart(DateTime &time) { if (m_currentSchedule == 0) return; if (m_currentSchedule->earlyStart < time) { //m_currentSchedule->logDebug( "moveEarliestStart: " + m_currentSchedule->earlyStart.toString() + " -> " + time.toString() ); m_currentSchedule->earlyStart = time; } QListIterator it = m_nodes; while (it.hasNext()) { it.next()->moveEarliestStart(time); } } void Node::moveLatestFinish(DateTime &time) { if (m_currentSchedule == 0) return; if (m_currentSchedule->lateFinish > time) m_currentSchedule->lateFinish = time; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->moveLatestFinish(time); } } void Node::initiateCalculation(MainSchedule &sch) { m_visitedForward = false; m_visitedBackward = false; m_durationForward = Duration::zeroDuration; m_durationBackward = Duration::zeroDuration; m_earlyStart = DateTime(); m_earlyFinish = DateTime(); m_lateFinish = DateTime(); QListIterator it = m_nodes; while (it.hasNext()) { it.next()->initiateCalculation(sch); } } void Node::resetVisited() { m_visitedForward = false; m_visitedBackward = false; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->resetVisited(); } } Node *Node::siblingBefore() { //debugPlan; if (parentNode()) return parentNode()->childBefore(this); return 0; } Node *Node::childBefore(Node *node) { //debugPlan; int index = m_nodes.indexOf(node); if (index > 0){ return m_nodes.at(index-1); } return 0; } Node *Node::siblingAfter() { //debugPlan; if (parentNode()) return parentNode()->childAfter(this); return 0; } Node *Node::childAfter(Node *node) { //debugPlan; Q_ASSERT( m_nodes.contains( node ) ); int index = m_nodes.indexOf(node); if (index < m_nodes.count()-1) { return m_nodes.at(index+1); } return 0; } bool Node::moveChildUp(Node* node) { if (findChildNode(node) == -1) return false; // not my node! Node *sib = node->siblingBefore(); if (!sib) return false; sib = sib->siblingBefore(); takeChildNode(node); if (sib) { addChildNode(node, sib); } else { insertChildNode(0, node); } return true; } bool Node::moveChildDown(Node* node) { if (findChildNode(node) == -1) return false; // not my node! Node *sib = node->siblingAfter(); if (!sib) return false; takeChildNode(node); addChildNode(node, sib); return true; } bool Node::legalToLink( const Node *node ) const { Node *p = const_cast(this)->projectNode(); if (p) return p->legalToLink(this, node); return false; } bool Node::isEndNode() const { return m_dependChildNodes.isEmpty(); } bool Node::isStartNode() const { return m_dependParentNodes.isEmpty(); } void Node::setId(const QString& id) { //debugPlan<startTime = startTime; } void Node::setEndTime(const DateTime &endTime, long id ) { Schedule *s = schedule( id ); if ( s ) s->endTime = endTime; } void Node::saveAppointments(QDomElement &element, long id) const { //debugPlan<id()<<","<add(appointment); } void Node::addAppointment(ResourceSchedule *resource, const DateTime &start, const DateTime &end, double load) { Schedule *node = findSchedule(resource->id()); if (node == 0) { node = createSchedule(resource->parent()); } node->setCalculationMode( resource->calculationMode() ); node->addAppointment(resource, start, end, load); } bool Node::isBaselined( long id ) const { Schedule *s = schedule( id ); return s ? s->isBaselined() : false; } void Node::takeSchedule(const Schedule *schedule) { if (schedule == 0) return; if (m_currentSchedule == schedule) m_currentSchedule = 0; m_schedules.take(schedule->id()); } void Node::addSchedule(Schedule *schedule) { if (schedule == 0) return; m_schedules.insert(schedule->id(), schedule); } Schedule *Node::createSchedule(const QString& name, Schedule::Type type, long id) { //debugPlan<removeStartup( *this ); } m_startupAccount = acc; changed(); } void Node::setShutdownCost(double cost) { m_shutdownCost = cost; changed(ShutdownCost); } void Node::setShutdownAccount(Account *acc) { //debugPlan<removeShutdown( *this ); } m_shutdownAccount = acc; changed(); } void Node::setRunningAccount(Account *acc) { //debugPlan<removeRunning( *this ); } m_runningAccount = acc; changed(); } void Node::blockChanged(bool on) { m_blockChanged = on; } void Node::changed(Node *node, int property) { if (m_blockChanged) { return; } switch ( property) { case Type: case StartupCost: case ShutdownCost: case CompletionEntry: case CompletionStarted: case CompletionFinished: case CompletionStartTime: case CompletionFinishTime: case CompletionPercentage: case CompletionRemainingEffort: case CompletionActualEffort: case CompletionUsedEffort: foreach ( Schedule *s, m_schedules ) { s->clearPerformanceCache(); } break; default: break; } if (m_parent) { m_parent->changed(node, property); } } Duration Node::plannedEffort( const Resource *resource, long id, EffortCostCalculationType type ) const { Duration e; foreach ( Node *n, m_nodes ) { e += n->plannedEffort( resource, id, type ); } return e; } Duration Node::plannedEffort( const Resource *resource, QDate date, long id, EffortCostCalculationType type ) const { Duration e; foreach ( Node *n, m_nodes ) { e += n->plannedEffort( resource, date, id, type ); } return e; } Duration Node::plannedEffortTo( const Resource *resource, QDate date, long id, EffortCostCalculationType type ) const { Duration e; foreach ( Node *n, m_nodes ) { e += n->plannedEffortTo( resource, date, id, type ); } return e; } EffortCost Node::plannedCost( long id, EffortCostCalculationType type ) const { EffortCost ec; foreach ( Node *n, m_nodes ) { ec += n->plannedCost( id, type ); } return ec; } EffortCostMap Node::bcwsPrDay( long int id, EffortCostCalculationType type ) const { return const_cast( this )->bcwsPrDay( id, type ); } EffortCostMap Node::bcwsPrDay( long int id, EffortCostCalculationType type ) { Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache &ec = s->bcwsPrDayCache( type ); if ( ! ec.cached ) { ec.effortcostmap = EffortCostMap(); foreach ( Node *n, m_nodes ) { ec.effortcostmap += n->bcwsPrDay( id, type ); } ec.cached = true; } return ec.effortcostmap; } EffortCostMap Node::bcwpPrDay( long int id, EffortCostCalculationType type ) const { return const_cast( this )->bcwpPrDay( id, type); } EffortCostMap Node::bcwpPrDay( long int id, EffortCostCalculationType type ) { Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache &ec = s->bcwpPrDayCache( type ); if ( ! ec.cached ) { ec.effortcostmap = EffortCostMap(); foreach ( Node *n, m_nodes ) { ec.effortcostmap += n->bcwpPrDay( id, type ); } ec.cached = true; } return ec.effortcostmap; } EffortCostMap Node::acwp( long id, EffortCostCalculationType type ) const { return const_cast( this )->acwp( id, type ); } EffortCostMap Node::acwp( long id, EffortCostCalculationType type ) { Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache &ec = s->acwpCache( type ); if ( ! ec.cached ) { ec.effortcostmap = EffortCostMap(); foreach ( Node *n, m_nodes ) { ec.effortcostmap += n->acwp( id, type ); } ec.cached = true; } return ec.effortcostmap; } EffortCost Node::acwp( QDate date, long id ) const { EffortCost ec; foreach ( Node *n, m_nodes ) { ec += n->acwp( date, id ); } return ec; } void Node::slotStandardWorktimeChanged(KPlato::StandardWorktime*) { //debugPlan<m_expectedCached = false; m_estimate->m_optimisticCached = false; m_estimate->m_pessimisticCached = false; } } void Node::emitDocumentAdded( Node *node, Document *doc, int idx ) { if ( m_parent ) { m_parent->emitDocumentAdded( node, doc, idx ); } } void Node::emitDocumentRemoved( Node *node, Document *doc, int idx ) { if ( m_parent ) { m_parent->emitDocumentRemoved( node, doc, idx ); } } void Node::emitDocumentChanged( Node *node, Document *doc, int idx ) { if ( m_parent ) { m_parent->emitDocumentChanged( node, doc, idx ); } } ////////////////////////// Estimate ///////////////////////////////// Estimate::Estimate( Node *parent ) : m_parent( parent ) { m_pertCached = false; setUnit( Duration::Unit_h ); setExpectedEstimate( 8.0 ); setPessimisticEstimate( 8.0 ); setOptimisticEstimate( 8.0 ); m_type = Type_Effort; m_calendar = 0; m_risktype = Risk_None; } Estimate::Estimate(const Estimate &estimate, Node *parent) : m_parent( parent ) { copy( estimate ); } Estimate::~Estimate() { } void Estimate::clear() { m_pertCached = false; setExpectedEstimate( 0.0 ); setPessimisticEstimate( 0.0 ); setOptimisticEstimate( 0.0 ); m_type = Type_Effort; m_calendar = 0; m_risktype = Risk_None; m_unit = Duration::Unit_h; changed(); } Estimate &Estimate::operator=( const Estimate &estimate ) { copy( estimate ); return *this; } void Estimate::copy( const Estimate &estimate ) { //m_parent = 0; // don't touch m_expectedEstimate = estimate.m_expectedEstimate; m_optimisticEstimate = estimate.m_optimisticEstimate; m_pessimisticEstimate = estimate.m_pessimisticEstimate; m_expectedValue = estimate.m_expectedValue; m_optimisticValue = estimate.m_optimisticValue; m_pessimisticValue = estimate.m_pessimisticValue; m_expectedCached = estimate.m_expectedCached; m_optimisticCached = estimate.m_optimisticCached; m_pessimisticCached = estimate.m_pessimisticCached; m_pertExpected = estimate.m_pertExpected; m_pertCached = estimate.m_pertCached; m_type = estimate.m_type; m_calendar = estimate.m_calendar; m_risktype = estimate.m_risktype; m_unit = estimate.m_unit; changed(); } double Estimate::variance() const { double d = deviation(); return d * d; } double Estimate::variance( Duration::Unit unit ) const { double d = deviation( unit ); return d * d; } double Estimate::deviation() const { return ( m_pessimisticEstimate - m_optimisticEstimate ) / 6; } double Estimate::deviation( Duration::Unit unit ) const { if ( unit == m_unit ) { return deviation(); } double p = pessimisticValue().toDouble( unit ); double o = optimisticValue().toDouble( unit ); double v = ( p - o ) / 6; return v; } Duration Estimate::pertExpected() const { if (m_risktype == Risk_Low) { if ( ! m_pertCached ) { m_pertExpected = (optimisticValue() + pessimisticValue() + (expectedValue()*4))/6; m_pertCached = true; } return m_pertExpected; } else if (m_risktype == Risk_High) { if ( ! m_pertCached ) { m_pertExpected = (optimisticValue() + (pessimisticValue()*2) + (expectedValue()*4))/7; m_pertCached = true; } return m_pertExpected; } return expectedValue(); // risk==none } Duration Estimate::pertOptimistic() const { if (m_risktype != Risk_None) { return pertExpected() - Duration( variance( Duration::Unit_ms ) ); } return optimisticValue(); } Duration Estimate::pertPessimistic() const { if (m_risktype != Risk_None) { return pertExpected() + Duration( variance( Duration::Unit_ms ) ); } return pessimisticValue(); } Duration Estimate::value(int valueType, bool pert) const { if (valueType == Estimate::Use_Expected) { return pert ? pertExpected() : expectedValue(); } else if (valueType == Estimate::Use_Optimistic) { return pert ? pertOptimistic() : optimisticValue(); } else if (valueType == Estimate::Use_Pessimistic) { return pert ? pertPessimistic() : pessimisticValue(); } return expectedValue(); } void Estimate::setUnit( Duration::Unit unit ) { m_unit = unit; m_expectedCached = false; m_optimisticCached = false; m_pessimisticCached = false; m_pertCached = false; changed(); } bool Estimate::load(KoXmlElement &element, XMLLoaderObject &status) { setType(element.attribute("type")); setRisktype(element.attribute("risk")); if ( status.version() <= "0.6" ) { m_unit = (Duration::Unit)(element.attribute("display-unit", QString().number(Duration::Unit_h) ).toInt()); QList s = status.project().standardWorktime()->scales(); m_expectedEstimate = scale( Duration::fromString(element.attribute("expected")), m_unit, s ); m_optimisticEstimate = scale( Duration::fromString(element.attribute("optimistic")), m_unit, s ); m_pessimisticEstimate = scale( Duration::fromString(element.attribute("pessimistic")), m_unit, s ); } else { if ( status.version() <= "0.6.2" ) { // 0 pos in unit is now Unit_Y, so add 3 to get the correct new unit m_unit = (Duration::Unit)(element.attribute("unit", QString().number(Duration::Unit_ms - 3) ).toInt() + 3); } else { m_unit = Duration::unitFromString( element.attribute( "unit" ) ); } m_expectedEstimate = element.attribute("expected", "0.0").toDouble(); m_optimisticEstimate = element.attribute("optimistic", "0.0").toDouble(); m_pessimisticEstimate = element.attribute("pessimistic", "0.0").toDouble(); m_calendar = status.project().findCalendar(element.attribute("calendar-id")); } return true; } void Estimate::save(QDomElement &element) const { QDomElement me = element.ownerDocument().createElement("estimate"); element.appendChild(me); me.setAttribute("expected", QString::number(m_expectedEstimate)); me.setAttribute("optimistic", QString::number(m_optimisticEstimate)); me.setAttribute("pessimistic", QString::number(m_pessimisticEstimate)); me.setAttribute("type", typeToString()); if ( m_calendar ) { me.setAttribute("calendar-id", m_calendar->id() ); } me.setAttribute("risk", risktypeToString()); me.setAttribute("unit", Duration::unitToString( m_unit ) ); } QString Estimate::typeToString( bool trans ) const { return typeToStringList( trans ).at( m_type ); } QString Estimate::typeToString( Estimate::Type typ, bool trans ) { return typeToStringList( trans ).value( typ ); } QStringList Estimate::typeToStringList( bool trans ) { return QStringList() << (trans ? i18n("Effort") : QString("Effort")) << (trans ? i18n("Duration") : QString("Duration")); } void Estimate::setType(Type type) { m_type = type; m_expectedCached = false; m_optimisticCached = false; m_pessimisticCached = false; m_pertCached = false; changed(); } void Estimate::setType(const QString& type) { if (type == "Effort") setType(Type_Effort); else if (type == "Duration" || /*old format*/ type == "FixedDuration") setType(Type_Duration); else if (/*old format*/type == "Length") setType(Type_Duration); else if (type == "Type_FixedDuration") // Typo, keep old xml files working setType(Type_Duration); else setType(Type_Effort); // default } QString Estimate::risktypeToString( bool trans ) const { return risktypeToStringList( trans ).at( m_risktype ); } QStringList Estimate::risktypeToStringList( bool trans ) { return QStringList() << (trans ? i18n("None") : QString("None")) << (trans ? i18n("Low") : QString("Low")) << (trans ? i18n("High") : QString("High")); } void Estimate::setRisktype(const QString& type) { if (type == "High") setRisktype(Risk_High); else if (type == "Low") setRisktype(Risk_Low); else setRisktype(Risk_None); // default } void Estimate::setRisktype(Risktype type) { m_pertCached = false; m_risktype = type; changed(); } void Estimate::setCalendar( Calendar *calendar ) { m_calendar = calendar; m_expectedCached = false; m_optimisticCached = false; m_pessimisticCached = false; m_pertCached = false; changed(); } void Estimate::setExpectedEstimate( double value) { m_expectedEstimate = value; m_expectedCached = false; m_pertCached = false; changed(); } void Estimate::setOptimisticEstimate( double value ) { m_optimisticEstimate = value; m_optimisticCached = false; m_pertCached = false; changed(); } void Estimate::setPessimisticEstimate( double value ) { m_pessimisticEstimate = value; m_pessimisticCached = false; m_pertCached = false; changed(); } void Estimate::setOptimisticRatio(int percent) { int p = percent>0 ? -percent : percent; m_optimisticValue = expectedValue()*(100+p)/100; m_optimisticEstimate = scale( m_optimisticValue, m_unit, scales() ); m_optimisticCached = true; m_pertCached = false; changed(); } int Estimate::optimisticRatio() const { if (m_expectedEstimate == 0.0) return 0; return (int)((optimisticValue()*100)/expectedValue())-100; } void Estimate::setPessimisticRatio(int percent) { int p = percent<0 ? -percent : percent; m_pessimisticValue = expectedValue()*(100+p)/100; m_pessimisticEstimate = scale( m_pessimisticValue, m_unit, scales() ); m_pessimisticCached = true; m_pertCached = false; changed(); } int Estimate::pessimisticRatio() const { if (m_expectedEstimate == 0.0) return 0; return (int)((pessimisticValue()*100)/expectedValue())-100; } // internal void Estimate::setOptimisticValue() { m_optimisticValue = scale( m_optimisticEstimate, m_unit, scales() ); m_optimisticCached = true; m_pertCached = false; } // internal void Estimate::setExpectedValue() { m_expectedValue = scale( m_expectedEstimate, m_unit, scales() ); m_expectedCached = true; m_pertCached = false; } // internal void Estimate::setPessimisticValue() { m_pessimisticValue = scale( m_pessimisticEstimate, m_unit, scales() ); m_pessimisticCached = true; m_pertCached = false; } Duration Estimate::optimisticValue() const { if ( ! m_optimisticCached ) { const_cast(this)->setOptimisticValue(); } return m_optimisticValue; } Duration Estimate::pessimisticValue() const { if ( ! m_pessimisticCached ) { const_cast(this)->setPessimisticValue(); } return m_pessimisticValue; } Duration Estimate::expectedValue() const { if ( ! m_expectedCached ) { const_cast(this)->setExpectedValue(); } return m_expectedValue; } double Estimate::scale( const Duration &value, Duration::Unit unit, const QList &scales ) { //debugPlan< lst = scales; switch ( lst.count() ) { case Duration::Unit_Y: lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year case Duration::Unit_M: lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month case Duration::Unit_w: lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week case Duration::Unit_d: lst << 24 * 60 * 60 * 1000; // add milliseconds in day case Duration::Unit_h: lst << 60 * 60 * 1000; // add milliseconds in hour case Duration::Unit_m: lst << 60 * 1000; // add milliseconds in minute case Duration::Unit_s: lst << 1000; // add milliseconds in second case Duration::Unit_ms: lst << 1; // add milliseconds in a millisecond default: break; } double v = ( double )( value.milliseconds() ); v /= lst[ unit ]; //debugPlan< &scales ) { //debugPlan< lst = scales; switch ( lst.count() ) { case Duration::Unit_Y: lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year case Duration::Unit_M: lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month case Duration::Unit_w: lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week case Duration::Unit_d: lst << 24 * 60 * 60 * 1000; // add milliseconds in day case Duration::Unit_h: lst << 60 * 60 * 1000; // add milliseconds in hour case Duration::Unit_m: lst << 60 * 1000; // add milliseconds in minute case Duration::Unit_s: lst << 1000; // add milliseconds in second case Duration::Unit_ms: lst << 1; // add milliseconds in a millisecond default: break; } qint64 v = ( qint64 )( value * lst[ unit ] ); //debugPlan< Estimate::defaultScales() { QList lst; lst << (qint64)(365 * 24) * 60 * 60 * 1000 // add milliseconds in a year << (qint64)(30 * 24) * 60 * 60 * 1000 // add milliseconds in a month << (qint64)(7 * 24) * 60 * 60 * 1000 // add milliseconds in a week << 24 * 60 * 60 * 1000 // add milliseconds in day << 60 * 60 * 1000 // add milliseconds in hour << 60 * 1000 // add milliseconds in minute << 1000 // add milliseconds in second << 1; // add milliseconds in a millisecond return lst; } QList Estimate::scales() const { QList s; if ( m_type == Type_Duration && m_calendar == 0 ) { return s; // Use default scaling ( 24h a day...) } if ( m_parent == 0 ) { return s; } Project *p = static_cast( m_parent->projectNode() ); if ( p == 0 ) { return s; } s << p->standardWorktime()->scales(); return s; } } //KPlato namespace diff --git a/src/libs/kernel/kptxmlloaderobject.h b/src/libs/kernel/kptxmlloaderobject.h index e594216a..7f54f177 100644 --- a/src/libs/kernel/kptxmlloaderobject.h +++ b/src/libs/kernel/kptxmlloaderobject.h @@ -1,139 +1,139 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2007 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef XMLLOADEROBJECT_H #define XMLLOADEROBJECT_H #include "plankernel_export.h" #include "kptproject.h" #include "kptdatetime.h" #include #include #include #include #include namespace KPlato { class PLANKERNEL_EXPORT XMLLoaderObject { public: enum Severity { None=0, Errors=1, Warnings=2, Diagnostics=3, Debug=4 }; XMLLoaderObject() : m_project(0), m_errors(0), m_warnings(0), m_logLevel(Diagnostics), m_log(), m_baseCalendar( 0 ) {} ~XMLLoaderObject() {} void setProject(Project *proj) { m_project = proj; } Project &project() const { return *m_project; } QString version() const { return m_version; } void setVersion( const QString &ver ) { m_version = ver; } QString workVersion() const { return m_workversion; } void setWorkVersion( const QString &ver ) { m_workversion = ver; } QString mimetype() const { return m_mimetype; } void setMimetype( const QString &mime ) { m_mimetype = mime; } const QTimeZone &projectTimeZone() const { return m_projectTimeZone; } void setProjectTimeZone( const QTimeZone &timeZone ) { m_projectTimeZone = timeZone; } void startLoad() { m_timer.start(); m_starttime = QDateTime::currentDateTime(); m_errors = m_warnings = 0; m_log.clear(); addMsg(QStringLiteral("Loading started at %1").arg(m_starttime.toString())); } void stopLoad() { m_elapsed = m_timer.elapsed(); - addMsg(QStringLiteral("Loading finished at %1, took %2").arg(QDateTime::currentDateTime().toString()).arg(formatElapsed())); + addMsg(QStringLiteral("Loading finished at %1, took %2").arg(QDateTime::currentDateTime().toString(), formatElapsed())); } QDateTime lastLoaded() const { return m_starttime; } int elapsed() const { return m_elapsed; } QString formatElapsed() { return QStringLiteral("%1 seconds").arg((double)m_elapsed/1000); } void setLogLevel(Severity sev) { m_logLevel = sev; } const QStringList &log() const { return m_log; } void error( const QString &msg ) { addMsg( Errors, msg ); } void warning( const QString &msg ) { addMsg( Errors, msg ); } void diagnostic( const QString &msg ) { addMsg( Diagnostics, msg ); } void debug( const QString &msg ) { addMsg( Debug, msg ); } void message( const QString &msg ) { addMsg( None, msg ); } void addMsg(int sev, const QString& msg) { increment(sev); if (m_logLevel < sev) return; QString s; if (sev == Errors) s = QLatin1String("ERROR"); else if (sev == Warnings) s = QLatin1String("WARNING"); else if (sev == Diagnostics) s = QLatin1String("Diagnostic"); else if (sev == Debug) s = QLatin1String("Debug"); else s = QLatin1String("Message"); m_log< 0; } void incWarnings() { ++m_warnings; } int warnings() const { return m_warnings; } bool warning() const { return m_warnings > 0; } // help to handle version < 0.6 void setBaseCalendar( Calendar *cal ) { m_baseCalendar = cal; } Calendar *baseCalendar() const { return m_baseCalendar; } void setUpdater( KoUpdater *updater ) { m_updater = updater; } void setProgress( int value ) { if ( m_updater ) m_updater->setProgress( value ); } protected: Project *m_project; int m_errors; int m_warnings; int m_logLevel; QStringList m_log; QDateTime m_starttime; QTime m_timer; int m_elapsed; QString m_version; QString m_workversion; QString m_mimetype; QTimeZone m_projectTimeZone; Calendar *m_baseCalendar; // help to handle version < 0.6 QPointer m_updater; }; } //namespace KPlato #endif diff --git a/src/libs/kernel/tests/debug.cpp b/src/libs/kernel/tests/debug.cpp index c3374642..9804c348 100644 --- a/src/libs/kernel/tests/debug.cpp +++ b/src/libs/kernel/tests/debug.cpp @@ -1,382 +1,382 @@ /* This file is part of the KDE project Copyright (C) 2009, 2010 Dag Andersen Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kptappointment.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptproject.h" #include "kptresource.h" #include "kptnode.h" #include "kpttask.h" #include "kptschedule.h" #include #include #include namespace QTest { template<> char *toString(const KPlato::DateTime &dt) { QString s; switch ( dt.timeSpec() ) { case Qt::LocalTime: s = " LocalTime"; break; case Qt::UTC: s = " UTC"; break; case Qt::OffsetFromUTC: s = " OffsetFromUTC"; break; case Qt::TimeZone: s = " TimeZone (" + dt.timeZone().id() + ')'; break; } - return toString( QString( "%1T%2 %3" ).arg( dt.date().toString(Qt::ISODate) ).arg( dt.time().toString( "hh:mm:ss.zzz" ) ).arg( s ) ); + return toString(QString("%1T%2 %3").arg(dt.date().toString(Qt::ISODate), dt.time().toString("hh:mm:ss.zzz"), s)); } template<> char *toString(const KPlato::Duration &d) { return toString( d.toString() ); } } namespace KPlato { class Debug { public: Debug() {} static void print( Calendar *c, const QString &str, bool full = true ) { Q_UNUSED(full); QTimeZone tz = c->timeZone(); QString s = tz.isValid() ? QString::fromLatin1(tz.id()) : QStringLiteral("LocalTime"); qDebug()<<"Debug info: Calendar"<name()<weekday( wd ); qDebug()<<" "<stateToString( d->state() ); foreach ( TimeInterval *t, d->timeIntervals() ) { qDebug()<<" interval:"<first<second<<'('<second) ).toString()<<')'; } } foreach ( const CalendarDay *d, c->days() ) { qDebug()<<" "<date()<<':'; foreach ( TimeInterval *t, d->timeIntervals() ) { qDebug()<<" interval:"<first<second; } } } static QString printAvailable( Resource *r, const QString &lead = QString() ) { QStringList sl; sl<availableFrom().isValid() ? r->availableFrom().toString() : ( r->project() ? ('('+r->project()->constraintStartTime().toString()+')' ) : QString() ) ) <<( r->availableUntil().isValid() ? r->availableUntil().toString() : ( r->project() ? ('('+r->project()->constraintEndTime().toString()+')' ) : QString() ) ) <units() )<<"%" <<"cost: normal"<normalRate() )<<" overtime"<overtimeRate() ); return sl.join( " " ); } static void print( Resource *r, const QString &str, bool full = true ) { qDebug()<<"Debug info: Resource"<name()<parentGroup() ? ( r->parentGroup()->name() + " Type: "+ r->parentGroup()->typeToString() ) : QString( "None" ) ); qDebug()<<"Available:" <<( r->availableFrom().isValid() ? r->availableFrom().toString() : ( r->project() ? ('('+r->project()->constraintStartTime().toString()+')' ) : QString() ) ) <<( r->availableUntil().isValid() ? r->availableUntil().toString() : ( r->project() ? ('('+r->project()->constraintEndTime().toString()+')' ) : QString() ) ) <units()<<'%'; qDebug()<<"Type:"<typeToString(); if ( r->type() == Resource::Type_Team ) { qDebug()<<"Team members:"<teamMembers().count(); foreach ( Resource *tm, r->teamMembers() ) { qDebug()<<" "<name()<<"Available:" <<( r->availableFrom().isValid() ? r->availableFrom().toString() : ( r->project() ? ('('+r->project()->constraintStartTime().toString()+')' ) : QString() ) ) <<( r->availableUntil().isValid() ? r->availableUntil().toString() : ( r->project() ? ('('+r->project()->constraintEndTime().toString()+')' ) : QString() ) ) <units()<<'%'; } } else { Calendar *cal = r->calendar( true ); QString s; if ( cal ) { s = cal->name(); } else { cal = r->calendar( false ); if ( cal ) { s = cal->name() + " (Default)"; } else { s = "No calendar"; } } qDebug()<<"Calendar:"<numExternalAppointments(); foreach ( Appointment *a, r->externalAppointmentList() ) { qDebug()<<" appointment:"<startTime().toString()<endTime().toString(); foreach( const AppointmentInterval &i, a->intervals().map() ) { qDebug()<<" "<name()<constraintStartTime()) ); qDebug()<<" project target end:"<constraintEndTime()) ); if ( p->isScheduled() ) { qDebug()<<" project start time:"<startTime()) ); qDebug()<<" project end time:"<endTime()) ); } else { qDebug()<<" Not scheduled"; } if ( ! all ) { return; } for ( int i = 0; i < p->numChildren(); ++i ) { qDebug(); print( static_cast( p->childNode( i ) ), true ); } } static void print( Project *p, Task *t, const QString &str, bool full = true ) { Q_UNUSED(full); print( p, str ); print( t ); } static void print( Task *t, const QString &str, bool full = true ) { qDebug()<<"Debug info: Task"<name()<level() > 0 ) { pad = QString("%1").arg( "", t->level()*2, ' ' ); } qDebug()<name()<typeToString()<constraintToString(); if (t->isScheduled()) { qDebug()<earlyStart()) ); qDebug()<lateStart()) ); qDebug()<earlyFinish()) ); qDebug()<lateFinish()) ); qDebug()<startTime()) ); qDebug()<endTime()) ); } else { qDebug()<constraint() ) { case Node::MustStartOn: case Node::StartNotEarlier: qDebug()<constraintStartTime()) ); break; case Node::FixedInterval: qDebug()<constraintStartTime()) ); case Node::MustFinishOn: case Node::FinishNotLater: qDebug()<constraintEndTime()) ); break; default: break; } qDebug()<estimate()->expectedEstimate()<estimate()->unit()) <estimate()->typeToString() <<(t->estimate()->type() == Estimate::Type_Duration ? (t->estimate()->calendar()?t->estimate()->calendar()->name():"Fixed") : QString( "%1 h" ).arg( t->estimate()->expectedValue().toDouble( Duration::Unit_h ) ) ); foreach ( ResourceGroupRequest *gr, t->requests().requests() ) { qDebug()<group()->name()<units(); foreach ( ResourceRequest *rr, gr->resourceRequests() ) { qDebug()<resource(), " " + rr->resource()->name() ); } } if (t->isStartNode()) { qDebug()<dependChildNodes()) { QString type; switch(r->type()) { case Relation::StartStart: type = "SS"; break; case Relation::FinishFinish: type = "FF"; break; default: type = "FS"; break; } rel << QString("(%1) -> %2, %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction)); } if (!rel.isEmpty()) { qDebug()<isEndNode()) { qDebug()<dependParentNodes()) { QString type; switch(r->type()) { case Relation::StartStart: type = "SS"; break; case Relation::FinishFinish: type = "FF"; break; default: type = "FS"; break; } rel << QString("%1 -> (%2), %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction)); } if (!rel.isEmpty()) { qDebug()<currentSchedule(); if ( s ) { qDebug()<appointments().count(); foreach ( Appointment *a, s->appointments() ) { qDebug()<resource()->resource()->name()<<"booked:"<startTime()) )<endTime()) )<<"effort:"<effort( a->startTime(), a->endTime() ).toDouble( Duration::Unit_h )<<'h'; if ( ! full ) { continue; } foreach( const AppointmentInterval &i, a->intervals().map() ) { qDebug()<runningAccount() ) { qDebug()<runningAccount()->name(); } if ( t->startupAccount() ) { qDebug()<startupAccount()->name()<<" cost:"<startupCost(); } if ( t->shutdownAccount() ) { qDebug()<shutdownAccount()->name()<<" cost:"<shutdownCost(); } if ( full ) { for ( int i = 0; i < t->numChildren(); ++i ) { qDebug()<( t->childNode( i ) ), full ); } } } static void print( const Completion &c, const QString &name, const QString &s = QString() ) { qDebug()<<"Completion:"<actualEffortMap().keys() ) { qDebug()<<" "<name()<<':'; qDebug()<<" "<logMessages() ) { qDebug()<name()<name()<<(a->isDefaultAccount() ? "Default" : ""); EffortCostMap ec = a->plannedCost( id ); qDebug()<<"Planned cost:"<isElement() ) { foreach ( Account *c, a->accountList() ) { print( c ); } return; } qDebug()<<"Cost places:"; foreach ( Account::CostPlace *cp, a->costPlaces() ) { qDebug()<<" Node:"<<(cp->node() ? cp->node()->name() : ""); qDebug()<<" running:"<running(); qDebug()<<" startup:"<startup(); qDebug()<<" shutdown:"<shutdown(); } } static void print( const AppointmentInterval &i, const QString &indent = QString() ) { QString s = indent + "Interval:"; if ( ! i.isValid() ) { qDebug()< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptaccountsmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptaccount.h" #include "kptdatetime.h" #include "kptschedule.h" #include "kptdebug.h" #include #include namespace KPlato { //-------------------------------------- AccountModel::AccountModel() : QObject(), m_project( 0 ) { } const QMetaEnum AccountModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } int AccountModel::propertyCount() const { return columnMap().keyCount(); } QVariant AccountModel::data( const Account *a, int property, int role ) const { QVariant result; if ( a == 0 ) { return QVariant(); } switch ( property ) { case AccountModel::Name: result = name( a, role ); break; case AccountModel::Description: result = description( a, role ); break; default: debugPlan<<"data: invalid display value column"<name()<<","<name(); case Qt::ToolTipRole: if ( a->isDefaultAccount() ) { return xi18nc( "1=account name", "%1 (Default account)", a->name() ); } return a->name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::CheckStateRole: if ( a->isDefaultAccount() ) { return m_project && m_project->isBaselined() ? Qt::PartiallyChecked : Qt::Checked; } return m_project && m_project->isBaselined() ? QVariant() : Qt::Unchecked; case Qt::DecorationRole: if ( a->isBaselined() ) { return koIcon("view-time-schedule-baselined"); } break; } return QVariant(); } QVariant AccountModel::description( const Account *a, int role ) const { //debugPlan<name()<<","<description(); break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant AccountModel::headerData( int property, int role ) const { if ( role == Qt::DisplayRole ) { switch ( property ) { case AccountModel::Name: return i18n( "Name" ); case AccountModel::Description: return i18n( "Description" ); default: return QVariant(); } } if ( role == Qt::TextAlignmentRole ) { return QVariant(); } if ( role == Qt::ToolTipRole ) { switch ( property ) { case AccountModel::Name: return ToolTip::accountName(); case AccountModel::Description: return ToolTip::accountDescription(); default: return QVariant(); } } return QVariant(); } //---------------------------------------- AccountItemModel::AccountItemModel( QObject *parent ) : ItemModelBase( parent ), m_account( 0 ) { } AccountItemModel::~AccountItemModel() { } const QMetaEnum AccountItemModel::columnMap() const { return m_model.columnMap(); } void AccountItemModel::slotAccountToBeInserted( const Account *parent, int row ) { //debugPlan<name(); Q_ASSERT( m_account == 0 ); m_account = const_cast(parent); beginInsertRows( index( parent ), row, row ); } void AccountItemModel::slotAccountInserted( const Account *account ) { //debugPlan<name(); Q_ASSERT( account->parent() == m_account ); Q_UNUSED( account ); endInsertRows(); m_account = 0; } void AccountItemModel::slotAccountToBeRemoved( const Account *account ) { //debugPlan<name(); Q_ASSERT( m_account == 0 ); m_account = const_cast(account); int row = index( account ).row(); beginRemoveRows( index( account->parent() ), row, row ); } void AccountItemModel::slotAccountRemoved( const Account *account ) { //debugPlan<name(); Q_ASSERT( account == m_account ); Q_UNUSED( account ); endRemoveRows(); m_account = 0; } void AccountItemModel::setProject( Project *project ) { if ( m_project ) { Accounts *acc = &( m_project->accounts() ); disconnect( acc , SIGNAL(changed(KPlato::Account*)), this, SLOT(slotAccountChanged(KPlato::Account*)) ); disconnect( acc, SIGNAL(accountAdded(const KPlato::Account*)), this, SLOT(slotAccountInserted(const KPlato::Account*)) ); disconnect( acc, SIGNAL(accountToBeAdded(const KPlato::Account*,int)), this, SLOT(slotAccountToBeInserted(const KPlato::Account*,int)) ); disconnect( acc, SIGNAL(accountRemoved(const KPlato::Account*)), this, SLOT(slotAccountRemoved(const KPlato::Account*)) ); disconnect( acc, SIGNAL(accountToBeRemoved(const KPlato::Account*)), this, SLOT(slotAccountToBeRemoved(const KPlato::Account*)) ); } m_project = project; m_model.m_project = project; if ( project ) { Accounts *acc = &( project->accounts() ); debugPlan<isBaselined() ) { flags |= Qt::ItemIsEditable; flags |= Qt::ItemIsUserCheckable; } break; } default: flags |= Qt::ItemIsEditable; break; } } return flags; } QModelIndex AccountItemModel::parent( const QModelIndex &index ) const { if ( !index.isValid() || m_project == 0 ) { return QModelIndex(); } //debugPlan<parent(); if ( par ) { a = par->parent(); int row = -1; if ( a ) { row = a->accountList().indexOf( par ); } else { row = m_project->accounts().accountList().indexOf( par ); } //debugPlan<name()<<":"<= columnCount() || row < 0 ) { return QModelIndex(); } Account *par = account( parent ); if ( par == 0 ) { if ( row < m_project->accounts().accountList().count() ) { return createIndex( row, column, m_project->accounts().accountList().at( row ) ); } } else if ( row < par->accountList().count() ) { return createIndex( row, column, par->accountList().at( row ) ); } return QModelIndex(); } QModelIndex AccountItemModel::index( const Account *account, int column ) const { Account *a = const_cast(account); if ( m_project == 0 || account == 0 ) { return QModelIndex(); } int row = -1; Account *par = a->parent(); if ( par == 0 ) { row = m_project->accounts().accountList().indexOf( a ); } else { row = par->accountList().indexOf( a ); } if ( row == -1 ) { return QModelIndex(); } return createIndex( row, column, a ); } int AccountItemModel::columnCount( const QModelIndex & ) const { return m_model.propertyCount(); } int AccountItemModel::rowCount( const QModelIndex &parent ) const { if ( m_project == 0 ) { return 0; } Account *par = account( parent ); if ( par == 0 ) { return m_project->accounts().accountList().count(); } return par->accountList().count(); } bool AccountItemModel::setName( Account *a, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() != a->name() ) { emit executeCommand( new RenameAccountCmd( a, value.toString(), kundo2_i18n( "Modify account name" ) ) ); } return true; case Qt::CheckStateRole: { switch ( value.toInt() ) { case Qt::Unchecked: if ( a->isDefaultAccount() ) { emit executeCommand( new ModifyDefaultAccountCmd( m_project->accounts(), a, 0, kundo2_i18n( "De-select as default account" ) ) ); return true; } break; case Qt::Checked: if ( ! a->isDefaultAccount() ) { emit executeCommand( new ModifyDefaultAccountCmd( m_project->accounts(), m_project->accounts().defaultAccount(), a, kundo2_i18n( "Select as default account" ) ) ); return true; } break; default: break; } } default: break; } return false; } bool AccountItemModel::setDescription( Account *a, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() != a->description() ) { emit executeCommand( new ModifyAccountDescriptionCmd( a, value.toString(), kundo2_i18n( "Modify account description" ) ) ); } return true; } return false; } QVariant AccountItemModel::data( const QModelIndex &index, int role ) const { QVariant result; Account *a = account( index ); if ( a == 0 ) { return QVariant(); } result = m_model.data( a, index.column(), role ); return result; } bool AccountItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { if ( ! index.isValid() ) { return ItemModelBase::setData( index, value, role ); } if ( ( flags( index ) &( Qt::ItemIsEditable | Qt::CheckStateRole ) ) == 0 ) { Q_ASSERT( true ); return false; } Account *a = account( index ); debugPlan<name()<( index.internalPointer() ); } void AccountItemModel::slotAccountChanged( Account *account ) { Account *par = account->parent(); if ( par ) { int row = par->accountList().indexOf( account ); emit dataChanged( createIndex( row, 0, account ), createIndex( row, columnCount() - 1, account ) ); } else { int row = m_project->accounts().accountList().indexOf( account ); emit dataChanged( createIndex( row, 0, account ), createIndex( row, columnCount() - 1, account ) ); } } QModelIndex AccountItemModel::insertAccount( Account *account, Account *parent, int index ) { debugPlan; if ( account->name().isEmpty() || m_project->accounts().findAccount( account->name() ) ) { QString s = parent == 0 ? account->name() : parent->name(); account->setName( m_project->accounts().uniqueId( s ) ); //m_project->accounts().insertId( account ); } emit executeCommand( new AddAccountCmd( *m_project, account, parent, index, kundo2_i18n( "Add account" ) ) ); int row = -1; if ( parent ) { row = parent->accountList().indexOf( account ); } else { row = m_project->accounts().accountList().indexOf( account ); } if ( row != -1 ) { //debugPlan<<"Inserted:"<name(); return createIndex( row, 0, account ); } debugPlan<<"Can't find"<name(); return QModelIndex(); } void AccountItemModel::removeAccounts( QList lst ) { MacroCommand *cmd = 0; KUndo2MagicString c = kundo2_i18np( "Delete Account", "Delete %1 Accounts", lst.count() ); while ( ! lst.isEmpty() ) { bool del = true; Account *acc = lst.takeFirst(); foreach ( Account *a, lst ) { if ( acc->isChildOf( a ) ) { del = false; // acc will be deleted when a is deleted break; } } if ( del ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new RemoveAccountCmd( *m_project, acc ) ); } } if ( cmd ) emit executeCommand( cmd ); } //---------------------------------------- CostBreakdownItemModel::CostBreakdownItemModel( QObject *parent ) : ItemModelBase( parent ), m_manager( 0 ), m_cumulative( false ), m_periodtype( Period_Day ), m_startmode( StartMode_Project ), m_endmode( EndMode_Project ), m_showmode( ShowMode_Both ) { m_format = QString( "%1 [%2]" ); } CostBreakdownItemModel::~CostBreakdownItemModel() { } const QMetaEnum CostBreakdownItemModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } int CostBreakdownItemModel::propertyCount() const { return columnMap().keyCount(); } void CostBreakdownItemModel::slotAccountToBeInserted( const Account *parent, int row ) { //debugPlan<name(); beginInsertRows( index( parent ), row, row ); } void CostBreakdownItemModel::slotAccountInserted( const Account *account ) { Q_UNUSED(account); //debugPlan<name(); endInsertRows(); } void CostBreakdownItemModel::slotAccountToBeRemoved( const Account *account ) { //debugPlan<name(); int row = index( account ).row(); beginRemoveRows( index( account->parent() ), row, row ); } void CostBreakdownItemModel::slotAccountRemoved( const Account *account ) { Q_UNUSED(account); //debugPlan<name(); endRemoveRows(); } void CostBreakdownItemModel::slotDataChanged() { fetchData(); foreach ( Account *a, m_plannedCostMap.keys() ) { QModelIndex idx1 = index( a ); QModelIndex idx2 = index( idx1.row(), columnCount() - 1, parent( idx1 ) ); //debugPlan<name()<accounts() ); disconnect( acc , SIGNAL(changed(KPlato::Account*)), this, SLOT(slotAccountChanged(KPlato::Account*)) ); disconnect( acc, SIGNAL(accountAdded(const KPlato::Account*)), this, SLOT(slotAccountInserted(const KPlato::Account*)) ); disconnect( acc, SIGNAL(accountToBeAdded(const KPlato::Account*,int)), this, SLOT(slotAccountToBeInserted(const KPlato::Account*,int)) ); disconnect( acc, SIGNAL(accountRemoved(const KPlato::Account*)), this, SLOT(slotAccountRemoved(const KPlato::Account*)) ); disconnect( acc, SIGNAL(accountToBeRemoved(const KPlato::Account*)), this, SLOT(slotAccountToBeRemoved(const KPlato::Account*)) ); disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); disconnect( m_project , SIGNAL(nodeChanged(KPlato::Node*)), this, SLOT(slotDataChanged()) ); disconnect( m_project , SIGNAL(nodeAdded(KPlato::Node*)), this, SLOT(slotDataChanged()) ); disconnect( m_project , SIGNAL(nodeRemoved(KPlato::Node*)), this, SLOT(slotDataChanged()) ); disconnect( m_project , SIGNAL(resourceChanged(KPlato::Resource*)), this, SLOT(slotDataChanged()) ); disconnect( m_project , SIGNAL(resourceAdded(const KPlato::Resource*)), this, SLOT(slotDataChanged()) ); disconnect( m_project , SIGNAL(resourceRemoved(const KPlato::Resource*)), this, SLOT(slotDataChanged()) ); } m_project = project; if ( project ) { Accounts *acc = &( project->accounts() ); debugPlan<scheduleId(); } EffortCostMap CostBreakdownItemModel::fetchPlannedCost( Account *account ) { EffortCostMap ec; ec = account->plannedCost( id() ); m_plannedCostMap.insert( account, ec ); QDate s = ec.startDate(); if ( ! m_plannedStart.isValid() || s < m_plannedStart ) { m_plannedStart = s; } QDate e = ec.endDate(); if ( ! m_plannedEnd.isValid() || e > m_plannedEnd ) { m_plannedEnd = e; } return ec; } EffortCostMap CostBreakdownItemModel::fetchActualCost( Account *account ) { debugPlan<name(); EffortCostMap ec; ec = account->actualCost( id() ); m_actualCostMap.insert( account, ec ); QDate s = ec.startDate(); if ( ! m_actualStart.isValid() || s < m_actualStart ) { m_actualStart = s; } QDate e = ec.endDate(); if ( ! m_actualEnd.isValid() || e > m_actualEnd ) { m_actualEnd = e; } debugPlan<name()<accounts().allAccounts() ) { fetchPlannedCost( a ); fetchActualCost( a ); } } QModelIndex CostBreakdownItemModel::parent( const QModelIndex &index ) const { if ( !index.isValid() || m_project == 0 ) { return QModelIndex(); } //debugPlan<parent(); if ( par ) { a = par->parent(); int row = -1; if ( a ) { row = a->accountList().indexOf( par ); } else { row = m_project->accounts().accountList().indexOf( par ); } //debugPlan<name()<<":"<= columnCount() || row < 0 ) { return QModelIndex(); } Account *par = account( parent ); if ( par == 0 ) { if ( row < m_project->accounts().accountList().count() ) { return createIndex( row, column, m_project->accounts().accountList().at( row ) ); } } else if ( row < par->accountList().count() ) { return createIndex( row, column, par->accountList().at( row ) ); } return QModelIndex(); } QModelIndex CostBreakdownItemModel::index( const Account *account ) const { Account *a = const_cast(account); if ( m_project == 0 || account == 0 ) { return QModelIndex(); } int row = -1; Account *par = a->parent(); if ( par == 0 ) { row = m_project->accounts().accountList().indexOf( a ); } else { row = par->accountList().indexOf( a ); } if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, a ); } int CostBreakdownItemModel::columnCount( const QModelIndex & ) const { int c = propertyCount(); if ( startDate().isValid() && endDate().isValid() ) { switch ( m_periodtype ) { case Period_Day: { c += startDate().daysTo( endDate()) + 1; break; } case Period_Week: { int days = QLocale().firstDayOfWeek() - startDate().dayOfWeek(); if ( days > 0 ) { days -= 7; } QDate start = startDate().addDays( days ); c += (start.daysTo( endDate() ) / 7) + 1; break; } case Period_Month: { int days = startDate().daysInMonth() - startDate().day() + 1; for ( QDate d = startDate(); d < endDate(); d = d.addDays( days ) ) { ++c; days = qMin( d.daysTo( endDate() ), static_cast(d.daysInMonth()) ); } break; } } } return c; } int CostBreakdownItemModel::rowCount( const QModelIndex &parent ) const { if ( m_project == 0 ) { return 0; } Account *par = account( parent ); if ( par == 0 ) { return m_project->accounts().accountList().count(); } return par->accountList().count(); } QString CostBreakdownItemModel::formatMoney( double cost1, double cost2 ) const { if ( m_showmode == ShowMode_Planned ) { return m_project->locale()->formatMoney( cost1, "", 0 ); } if ( m_showmode == ShowMode_Actual ) { return m_project->locale()->formatMoney( cost2, "", 0 ); } if ( m_showmode == ShowMode_Both ) { - return QString(m_format).arg( m_project->locale()->formatMoney( cost2, "", 0 ) ).arg( m_project->locale()->formatMoney( cost1, "", 0 ) ); + return QString(m_format).arg(m_project->locale()->formatMoney( cost2, "", 0), m_project->locale()->formatMoney(cost1, "", 0)); } if ( m_showmode == ShowMode_Deviation ) { return m_project->locale()->formatMoney( cost1 - cost2, "", 0 ); } return ""; } QVariant CostBreakdownItemModel::data( const QModelIndex &index, int role ) const { QVariant result; Account *a = account( index ); if ( a == 0 ) { return QVariant(); } if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case Name: return a->name(); case Description: return a->description(); case Total: { return formatMoney( m_plannedCostMap.value( a ).totalCost(), m_actualCostMap.value( a ).totalCost() ); } case Planned: return m_project->locale()->formatMoney( m_plannedCostMap.value( a ).totalCost(), "", 0 ); case Actual: return m_project->locale()->formatMoney( m_actualCostMap.value( a ).totalCost(), "", 0 ); default: { int col = index.column() - propertyCount(); EffortCostMap pc = m_plannedCostMap.value( a ); EffortCostMap ac = m_actualCostMap.value( a ); switch ( m_periodtype ) { case Period_Day: { double planned = 0.0; if ( m_cumulative ) { planned = pc.costTo( startDate().addDays( col ) ); } else { planned = pc.costOnDate( startDate().addDays( col ) ); } double actual = 0.0; if ( m_cumulative ) { actual = ac.costTo( startDate().addDays( col ) ); } else { actual = ac.costOnDate( startDate().addDays( col ) ); } return formatMoney( planned, actual ); } case Period_Week: { int days = QLocale().firstDayOfWeek() - startDate().dayOfWeek(); if ( days > 0 ) { days -= 7; ; } QDate start = startDate().addDays( days ); int week = col; double planned = 0.0; if ( m_cumulative ) { planned = pc.costTo( start.addDays( ++week * 7 ) ); } else { planned = week == 0 ? pc.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : pc.cost( start.addDays( week * 7 ) ); } double actual = 0.0; if ( m_cumulative ) { actual = ac.costTo( start.addDays( ++week * 7 ) ); } else { actual = week == 0 ? ac.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : ac.cost( start.addDays( week * 7 ) ); } return formatMoney( planned, actual ); } case Period_Month: { int days = startDate().daysInMonth() - startDate().day() + 1; QDate start = startDate(); for ( int i = 0; i < col; ++i ) { start = start.addDays( days ); days = start.daysInMonth(); } int planned = 0.0; if ( m_cumulative ) { planned = pc.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) ); } else { planned = pc.cost( start, start.daysInMonth() - start.day() + 1); } int actual = 0.0; if ( m_cumulative ) { actual = ac.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) ); } else { actual = ac.cost( start, start.daysInMonth() - start.day() + 1); } return formatMoney( planned, actual ); } default: return 0.0; break; } } } } else if ( role == Qt::ToolTipRole ) { switch ( index.column() ) { case Name: return a->name(); case Description: return a->description(); case Total: { double act = m_actualCostMap.value( a ).totalCost(); double pl = m_plannedCostMap.value( a ).totalCost(); return i18n( "Actual total cost: %1, planned total cost: %2", m_project->locale()->formatMoney( act, "", 0 ), m_project->locale()->formatMoney( pl, "", 0 ) ); } case Planned: case Actual: default: break; } } else if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } else { switch ( index.column() ) { case Name: case Description: case Planned: case Actual: return QVariant(); default: { return cost( a, index.column() - propertyCount(), role ); } } } return QVariant(); } QVariant CostBreakdownItemModel::cost( const Account *a, int offset, int role ) const { EffortCostMap costmap; if ( role == Role::Planned ) { costmap = m_plannedCostMap.value( const_cast( a ) ); } else if ( role == Role::Actual ) { costmap = m_actualCostMap.value( const_cast( a ) ); } else { return QVariant(); } double cost = 0.0; switch ( m_periodtype ) { case Period_Day: { if ( m_cumulative ) { cost = costmap.costTo( startDate().addDays( offset ) ); } else { cost = costmap.costOnDate( startDate().addDays( offset ) ); } break; } case Period_Week: { int days = QLocale().firstDayOfWeek() - startDate().dayOfWeek(); if ( days > 0 ) { days -= 7; ; } QDate start = startDate().addDays( days ); int week = offset; if ( m_cumulative ) { cost = costmap.costTo( start.addDays( ++week * 7 ) ); } else { cost = week == 0 ? costmap.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : costmap.cost( start.addDays( week * 7 ) ); } break; } case Period_Month: { int days = startDate().daysInMonth() - startDate().day() + 1; QDate start = startDate(); for ( int i = 0; i < offset; ++i ) { start = start.addDays( days ); days = start.daysInMonth(); } if ( m_cumulative ) { cost = costmap.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) ); } else { cost = costmap.cost( start, start.daysInMonth() - start.day() + 1); } break; } default: break; } return cost; } int CostBreakdownItemModel::periodType() const { return m_periodtype; } void CostBreakdownItemModel::setPeriodType( int period ) { if ( m_periodtype != period ) { beginResetModel(); m_periodtype = period; endResetModel(); } } int CostBreakdownItemModel::startMode() const { return m_startmode; } void CostBreakdownItemModel::setStartMode( int mode ) { beginResetModel(); m_startmode = mode; endResetModel(); } int CostBreakdownItemModel::endMode() const { return m_endmode; } void CostBreakdownItemModel::setEndMode( int mode ) { beginResetModel(); m_endmode = mode; endResetModel(); } QDate CostBreakdownItemModel::startDate() const { if ( m_project == 0 || m_manager == 0 ) { return m_start; } switch ( m_startmode ) { case StartMode_Project: { QDate d = m_project->startTime( id() ).date(); if ( m_plannedStart.isValid() && m_plannedStart < d ) { d = m_plannedStart; } if ( m_actualStart.isValid() && m_actualStart < d ) { d = m_actualStart; } return d; } default: break; } return m_start; } void CostBreakdownItemModel::setStartDate( const QDate &date ) { beginResetModel(); m_start = date; endResetModel(); } QDate CostBreakdownItemModel::endDate() const { if ( m_project == 0 || m_manager == 0 ) { return m_end; } switch ( m_endmode ) { case EndMode_Project: { QDate d = m_project->endTime( id() ).date(); if ( m_plannedEnd.isValid() && m_plannedEnd > d ) { d = m_plannedEnd; } if ( m_actualEnd.isValid() && m_actualEnd > d ) { d = m_actualEnd; } return d; } case EndMode_CurrentDate: return QDate::currentDate(); default: break; } return m_end; } void CostBreakdownItemModel::setEndDate( const QDate &date ) { beginResetModel(); m_end = date; endResetModel(); } bool CostBreakdownItemModel::cumulative() const { return m_cumulative; } void CostBreakdownItemModel::setCumulative( bool on ) { beginResetModel(); m_cumulative = on; endResetModel(); } int CostBreakdownItemModel::showMode() const { return m_showmode; } void CostBreakdownItemModel::setShowMode( int show ) { m_showmode = show; } QVariant CostBreakdownItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if ( role == Qt::DisplayRole ) { switch (section) { case Name: return i18n( "Name" ); case Description: return i18n( "Description" ); case Total: return i18n( "Total" ); case Planned: return i18n("Planned"); case Actual: return i18n("Actual"); default: break; } int col = section - propertyCount(); switch ( m_periodtype ) { case Period_Day: { return startDate().addDays( col ).toString( Qt::ISODate ); } case Period_Week: { return startDate().addDays( ( col ) * 7 ).weekNumber(); } case Period_Month: { int days = startDate().daysInMonth() - startDate().day() + 1; QDate start = startDate(); for ( int i = 0; i < col; ++i ) { start = start.addDays( days ); days = start.daysInMonth(); } return QDate::shortMonthName( start.month() ); } default: return section; break; } return QVariant(); } if ( role == Qt::EditRole ) { switch (section) { case Name: return QStringLiteral( "Name" ); case Description: return QStringLiteral( "Description" ); case Total: return QStringLiteral( "Total" ); case Planned: return QStringLiteral("Planned"); case Actual: return QStringLiteral("Actual"); default: break; } int col = section - propertyCount(); switch ( m_periodtype ) { case Period_Day: { return startDate().addDays( col ); } case Period_Week: { return startDate().addDays( ( col ) * 7 ).weekNumber(); } case Period_Month: { int days = startDate().daysInMonth() - startDate().day() + 1; QDate start = startDate(); for ( int i = 0; i < col; ++i ) { start = start.addDays( days ); days = start.daysInMonth(); } return start.month(); } default: return section; break; } return QVariant(); } if ( role == Qt::ToolTipRole ) { switch ( section ) { case Name: return ToolTip::accountName(); case Description: return ToolTip::accountDescription(); case Total: return i18n( "The total cost for the account shown as: Actual cost [ Planned cost ]" ); case Planned: case Actual: default: return QVariant(); } } if ( role == Qt::TextAlignmentRole ) { switch ( section ) { case Name: return QVariant(); case Description: return QVariant(); default: return (int)(Qt::AlignRight|Qt::AlignVCenter); } return QVariant(); } } return ItemModelBase::headerData(section, orientation, role); } Account *CostBreakdownItemModel::account( const QModelIndex &index ) const { return static_cast( index.internalPointer() ); } void CostBreakdownItemModel::slotAccountChanged( Account *account ) { Q_UNUSED(account); fetchData(); foreach ( Account *a, m_plannedCostMap.keys() ) { QModelIndex idx1 = index( a ); QModelIndex idx2 = index( idx1.row(), columnCount() - 1, parent( idx1 ) ); //debugPlan<name()<