diff options
Diffstat (limited to 'kplato/kptcalendar.cc')
-rw-r--r-- | kplato/kptcalendar.cc | 1036 |
1 files changed, 1036 insertions, 0 deletions
diff --git a/kplato/kptcalendar.cc b/kplato/kptcalendar.cc new file mode 100644 index 00000000..38c2a286 --- /dev/null +++ b/kplato/kptcalendar.cc @@ -0,0 +1,1036 @@ +/* This file is part of the KDE project + Copyright (C) 2003 - 2006 Dag Andersen <danders@get2net.dk> + + 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. +*/ + +#include "kptcalendar.h" +#include "kptduration.h" +#include "kptdatetime.h" +#include "kptproject.h" + +#include <qdom.h> +#include <qptrlist.h> + +#include <klocale.h> +#include <kdebug.h> + +namespace KPlato +{ + +///// CalendarDay //// +CalendarDay::CalendarDay() + : m_date(), + m_state(0), + m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_workingIntervals.setAutoDelete(true); +} + +CalendarDay::CalendarDay(int state) + : m_date(), + m_state(state), + m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_workingIntervals.setAutoDelete(true); +} + +CalendarDay::CalendarDay(QDate date, int state) + : m_date(date), + m_state(state), + m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; + m_workingIntervals.setAutoDelete(true); +} + +CalendarDay::CalendarDay(CalendarDay *day) + : m_workingIntervals() { + + //kdDebug()<<k_funcinfo<<"("<<this<<") from ("<<day<<")"<<endl; + m_workingIntervals.setAutoDelete(true); + copy(*day); +} + +CalendarDay::~CalendarDay() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +const CalendarDay &CalendarDay::copy(const CalendarDay &day) { + //kdDebug()<<k_funcinfo<<"("<<&day<<") date="<<day.date().toString()<<endl; + m_date = day.date(); + m_state = day.state(); + m_workingIntervals.clear(); + QPtrListIterator<QPair<QTime, QTime> > it = day.workingIntervals(); + for(; it.current(); ++it) { + m_workingIntervals.append(new QPair<QTime, QTime>(it.current()->first, it.current()->second)); + } + return *this; +} + +bool CalendarDay::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + bool ok=false; + m_state = QString(element.attribute("state", "-1")).toInt(&ok); + if (m_state < 0) + return false; + //kdDebug()<<k_funcinfo<<" state="<<m_state<<endl; + QString s = element.attribute("date"); + if (s != "") { + m_date = QDate::fromString(s, Qt::ISODate); + if (!m_date.isValid()) + m_date = QDate::fromString(s); + } + clearIntervals(); + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "interval") { + //kdDebug()<<k_funcinfo<<"Interval start="<<e.attribute("start")<<" end="<<e.attribute("end")<<endl; + QString st = e.attribute("start"); + QString en = e.attribute("end"); + if (st != "" && en != "") { + QTime start = QTime::fromString(st); + QTime end = QTime::fromString(en); + addInterval(new QPair<QTime, QTime>(start,end)); + } + } + } + } + return true; +} + +void CalendarDay::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<m_date.toString()<<endl; + if (m_state == Map::None) + return; + if (m_date.isValid()) { + element.setAttribute("date", m_date.toString(Qt::ISODate)); + } + element.setAttribute("state", m_state); + if (m_workingIntervals.count() == 0) + return; + + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + QDomElement me = element.ownerDocument().createElement("interval"); + element.appendChild(me); + me.setAttribute("end", it.current()->second.toString()); + me.setAttribute("start", it.current()->first.toString()); + } +} + +void CalendarDay::addInterval(QPair<QTime, QTime> *interval) { + m_workingIntervals.append(interval); +} + +QTime CalendarDay::startOfDay() const { + QTime t; + if (!m_workingIntervals.isEmpty()) { + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + t = it.current()->first; + for (++it; it.current(); ++it) { + if (t > it.current()->first) + t = it.current()->first; + } + } + return t; +} + +QTime CalendarDay::endOfDay() const { + QTime t; + if (!m_workingIntervals.isEmpty()) { + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + t = it.current()->second; + for (++it; it.current(); ++it) { + if (t > it.current()->second) + t = it.current()->second; + } + } + return t; +} + +bool CalendarDay::operator==(const CalendarDay *day) const { + return operator==(*day); +} +bool CalendarDay::operator==(const CalendarDay &day) const { + //kdDebug()<<k_funcinfo<<endl; + if (m_date.isValid() && day.date().isValid()) { + if (m_date != day.date()) { + //kdDebug()<<k_funcinfo<<m_date.toString()<<" != "<<day.date().toString()<<endl; + return false; + } + } else if (m_date.isValid() != day.date().isValid()) { + //kdDebug()<<k_funcinfo<<"one of the dates is not valid"<<endl; + return false; + } + if (m_state != day.state()) { + //kdDebug()<<k_funcinfo<<m_state<<" != "<<day.state()<<endl; + return false; + } + if (m_workingIntervals.count() != day.workingIntervals().count()) { + //kdDebug()<<k_funcinfo<<m_workingIntervals.count()<<" != "<<day.workingIntervals().count()<<endl; + return false; + } + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + QPtrListIterator<QPair<QTime, QTime> > dit = day.workingIntervals(); + for (; it.current(); ++it) { + bool res = false; + QPair<QTime, QTime> *a = it.current(); + for (dit.toFirst(); dit.current(); ++dit) { + QPair<QTime, QTime> *b = dit.current(); + if (a->first == b->first && a->second == b->second) { + res = true; + break; + } + } + if (res == false) { + //kdDebug()<<k_funcinfo<<"interval mismatch "<<a->first.toString()<<"-"<<a->second.toString()<<endl; + return false; + } + } + return true; +} +bool CalendarDay::operator!=(const CalendarDay *day) const { + return operator!=(*day); +} +bool CalendarDay::operator!=(const CalendarDay &day) const { + return !operator==(day); +} + +Duration CalendarDay::effort(const QTime &start, const QTime &end) { + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + Duration eff; + if (m_state != Map::Working) { + //kdDebug()<<k_funcinfo<<"Non working day"<<endl; + return eff; + } + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + //kdDebug()<<k_funcinfo<<"Interval: "<<it.current()->first.toString()<<" - "<<it.current()->second.toString()<<endl; + if (end > it.current()->first && start < it.current()->second) { + DateTime dtStart(QDate::currentDate(), start); + if (start < it.current()->first) { + dtStart.setTime(it.current()->first); + } + DateTime dtEnd(QDate::currentDate(), end); + if (end > it.current()->second) { + dtEnd.setTime(it.current()->second); + } + eff += dtEnd - dtStart; + //kdDebug()<<k_funcinfo<<dtStart.time().toString()<<" - "<<dtEnd.time().toString()<<"="<<eff.toString(Duration::Format_Day)<<endl; + } + } + //kdDebug()<<k_funcinfo<<(m_date.isValid()?m_date.toString(Qt::ISODate):"Weekday")<<": "<<start.toString()<<" - "<<end.toString()<<": total="<<eff.toString(Duration::Format_Day)<<endl; + return eff; +} + +QPair<QTime, QTime> CalendarDay::interval(const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<endl; + QTime t1, t2; + if (m_state == Map::Working) { + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + if (start < it.current()->second && end > it.current()->first) { + t1 = start > it.current()->first ? start : it.current()->first; + t2 = end < it.current()->second ? end : it.current()->second; + //kdDebug()<<k_funcinfo<<t1.toString()<<" to "<<t2.toString()<<endl; + return QPair<QTime, QTime>(t1, t2); + } + } + } + //kdError()<<k_funcinfo<<"No interval "<<m_date<<": "<<start<<","<<end<<endl; + return QPair<QTime, QTime>(t1, t2); +} + +bool CalendarDay::hasInterval() const { + return m_state == Map::Working && m_workingIntervals.count() > 0; +} + +bool CalendarDay::hasInterval(const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<(m_date.isValid()?m_date.toString(Qt::ISODate):"Weekday")<<" "<<start.toString()<<" - "<<end.toString()<<endl; + if (m_state != Map::Working) { + return false; + } + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + if (start < it.current()->second && end > it.current()->first) { + //kdDebug()<<k_funcinfo<<"true:"<<(m_date.isValid()?m_date.toString(Qt::ISODate):"Weekday")<<" "<<it.current()->first.toString()<<" - "<<it.current()->second.toString()<<endl; + return true; + } + } + return false; +} + +Duration CalendarDay::duration() const { + Duration dur; + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + DateTime start(QDate::currentDate(), it.current()->first); + DateTime end(QDate::currentDate(), it.current()->second); + dur += end - start; + } + return dur; +} + +///// CalendarWeekdays //// +CalendarWeekdays::CalendarWeekdays() + : m_weekdays(), + m_workHours(40) { + + //kdDebug()<<k_funcinfo<<"--->"<<endl; + for (int i=0; i < 7; ++i) { + m_weekdays.append(new CalendarDay()); + } + m_weekdays.setAutoDelete(false); + //kdDebug()<<k_funcinfo<<"<---"<<endl; +} + +CalendarWeekdays::CalendarWeekdays(CalendarWeekdays *weekdays) + : m_weekdays() { + //kdDebug()<<k_funcinfo<<"--->"<<endl; + copy(*weekdays); + //kdDebug()<<k_funcinfo<<"<---"<<endl; +} + +CalendarWeekdays::~CalendarWeekdays() { + m_weekdays.setAutoDelete(true); + //kdDebug()<<k_funcinfo<<endl; +} + +const CalendarWeekdays &CalendarWeekdays::copy(const CalendarWeekdays &weekdays) { + //kdDebug()<<k_funcinfo<<endl; + m_weekdays.setAutoDelete(true); + m_weekdays.clear(); + m_weekdays.setAutoDelete(false); + QPtrListIterator<CalendarDay> it = weekdays.weekdays(); + for (; it.current(); ++it) { + m_weekdays.append(new CalendarDay(it.current())); + } + return *this; +} + +bool CalendarWeekdays::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + bool ok; + int dayNo = QString(element.attribute("day","-1")).toInt(&ok); + if (dayNo < 0 || dayNo > 6) { + kdError()<<k_funcinfo<<"Illegal weekday: "<<dayNo<<endl; + return true; // we continue anyway + } + CalendarDay *day = m_weekdays.at(dayNo); + if (!day) + day = new CalendarDay(); + if (!day->load(element)) + day->setState(Map::None); + return true; +} + +void CalendarWeekdays::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (int i=0; it.current(); ++it) { + QDomElement me = element.ownerDocument().createElement("weekday"); + element.appendChild(me); + me.setAttribute("day", i++); + it.current()->save(me); + } +} + +IntMap CalendarWeekdays::map() { + IntMap days; + for (unsigned int i=0; i < m_weekdays.count(); ++i) { + if (m_weekdays.at(i)->state() > 0) + days.insert(i+1, m_weekdays.at(i)->state()); //Note: day numbers 1..7 + } + return days; +} + +int CalendarWeekdays::state(const QDate &date) const { + return state(date.dayOfWeek()-1); +} + +int CalendarWeekdays::state(int weekday) const { + CalendarDay *day = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(weekday); + return day ? day->state() : Map::None; +} + +void CalendarWeekdays::setState(int weekday, int state) { + CalendarDay *day = m_weekdays.at(weekday); + if (!day) + return; + day->setState(state); +} + +const QPtrList<QPair<QTime, QTime> > &CalendarWeekdays::intervals(int weekday) const { + CalendarDay *day = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(weekday); + Q_ASSERT(day); + return day->workingIntervals(); +} + +void CalendarWeekdays::setIntervals(int weekday, QPtrList<QPair<QTime, QTime> >intervals) { + CalendarDay *day = m_weekdays.at(weekday); + if (day) + day->setIntervals(intervals); +} + +void CalendarWeekdays::clearIntervals(int weekday) { + CalendarDay *day = m_weekdays.at(weekday); + if (day) + day->clearIntervals(); +} + +bool CalendarWeekdays::operator==(const CalendarWeekdays *wd) const { + if (m_weekdays.count() != wd->weekdays().count()) + return false; + for (unsigned int i=0; i < m_weekdays.count(); ++i) { + // is there a better way to get around this const stuff? + CalendarDay *day1 = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(i); + CalendarDay *day2 = const_cast<QPtrList<CalendarDay>&>(wd->weekdays()).at(i); + if (day1 != day2) + return false; + } + return true; +} +bool CalendarWeekdays::operator!=(const CalendarWeekdays *wd) const { + if (m_weekdays.count() != wd->weekdays().count()) + return true; + for (unsigned int i=0; i < m_weekdays.count(); ++i) { + // is there a better way to get around this const stuff? + CalendarDay *day1 = const_cast<CalendarWeekdays*>(this)->m_weekdays.at(i); + CalendarDay *day2 = const_cast<QPtrList<CalendarDay>&>(wd->weekdays()).at(i); + if (day1 != day2) + return true; + } + return false; +} + +Duration CalendarWeekdays::effort(const QDate &date, const QTime &start, const QTime &end) { + //kdDebug()<<k_funcinfo<<"Day of week="<<date.dayOfWeek()-1<<endl; + CalendarDay *day = weekday(date.dayOfWeek()-1); + if (day && day->state() == Map::Working) { + return day->effort(start, end); + } + return Duration::zeroDuration; +} + +QPair<QTime, QTime> CalendarWeekdays::interval(const QDate date, const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<endl; + CalendarDay *day = weekday(date.dayOfWeek()-1); + if (day && day->state() == Map::Working) { + if (day->hasInterval(start, end)) { + return day->interval(start, end); + } + } + return QPair<QTime, QTime>(QTime(), QTime()); +} + +bool CalendarWeekdays::hasInterval(const QDate date, const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<date.toString()<<": "<<start.toString()<<" - "<<end.toString()<<endl; + CalendarDay *day = weekday(date.dayOfWeek()-1); + return day && day->hasInterval(start, end); +} + +bool CalendarWeekdays::hasInterval() const { + //kdDebug()<<k_funcinfo<<endl; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (; it.current(); ++it) { + if (it.current()->hasInterval()) + return true; + } + return false; +} + +CalendarDay *CalendarWeekdays::weekday(int day) const { + QPtrListIterator<CalendarDay> it = m_weekdays; + for (int i=0; it.current(); ++it, ++i) { + if (i == day) + return it.current(); + } + return 0; +} + +Duration CalendarWeekdays::duration() const { + Duration dur; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (; it.current(); ++it) { + dur += it.current()->duration(); + } + return dur; +} + +Duration CalendarWeekdays::duration(int _weekday) const { + CalendarDay *day = weekday(_weekday); + if (day) + return day->duration(); + return Duration(); +} + +QTime CalendarWeekdays::startOfDay(int _weekday) const { + CalendarDay *day = weekday(_weekday); + if (day) + return day->startOfDay(); + return QTime(); +} + +QTime CalendarWeekdays::endOfDay(int _weekday) const { + CalendarDay *day = weekday(_weekday); + if (day) + return day->endOfDay(); + return QTime(); +} + + +///// Calendar //// + +Calendar::Calendar() + : m_parent(0), + m_project(0), + m_deleted(false) { + + init(); +} + +Calendar::Calendar(QString name, Calendar *parent) + : m_name(name), + m_parent(parent), + m_project(0), + m_deleted(false), + m_days() { + + init(); +} + +Calendar::~Calendar() { + //kdDebug()<<k_funcinfo<<"deleting "<<m_name<<endl; + removeId(); + delete m_weekdays; +} +Calendar::Calendar(Calendar *calendar) + : m_project(0), + m_days() { + m_days.setAutoDelete(true); + copy(*calendar); +} + +const Calendar &Calendar::copy(Calendar &calendar) { + m_name = calendar.name(); + m_parent = calendar.parent(); + m_deleted = calendar.isDeleted(); + m_id = calendar.id(); + + QPtrListIterator<CalendarDay> it = calendar.days(); + for (; it.current(); ++it) { + m_days.append(new CalendarDay(it.current())); + } + m_weekdays = new CalendarWeekdays(calendar.weekdays()); + return *this; +} + +void Calendar::init() { + m_days.setAutoDelete(true); + m_weekdays = new CalendarWeekdays(); +} + +void Calendar::setProject(Project *project) { + m_project = project; + generateId(); +} + +void Calendar::setDeleted(bool yes) { + if (yes) { + removeId(); + } else { + setId(m_id); + } + m_deleted = yes; +} +bool Calendar::setId(QString id) { + //kdDebug()<<k_funcinfo<<id<<endl; + if (id.isEmpty()) { + kdError()<<k_funcinfo<<"id is empty"<<endl; + m_id = id; + return false; + } + Calendar *c = findCalendar(); + if (c == this) { + kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl; + removeId(); + } else if (c) { + //can happen when making a copy + kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different node: "<<c->name()<<endl; + } + if (findCalendar(id)) { + kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different node: "<<findCalendar(id)->name()<<endl; + m_id = QString(); // hmmm + return false; + } + m_id = id; + insertId(id); + //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl; + return true; +} + +void Calendar::generateId() { + if (!m_id.isEmpty()) { + removeId(); + } + for (int i=0; i<32000 ; ++i) { + m_id = m_id.setNum(i); + if (!findCalendar()) { + insertId(m_id); + return; + } + } + m_id = QString(); +} + +bool Calendar::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<element.text()<<endl; + //bool ok; + setId(element.attribute("id")); + m_parentId = element.attribute("parent"); + m_name = element.attribute("name",""); + //TODO parent + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "weekday") { + if (!m_weekdays->load(e)) + return false; + } + if (e.tagName() == "day") { + CalendarDay *day = new CalendarDay(); + if (day->load(e)) { + if (!day->date().isValid()) { + delete day; + kdError()<<k_funcinfo<<m_name<<": Failed to load calendarDay - Invalid date"<<endl; + } else { + CalendarDay *d = findDay(day->date()); + if (d) { + // already exists, keep the new + removeDay(d); + kdWarning()<<k_funcinfo<<m_name<<" Load calendarDay - Date already exists"<<endl; + } + addDay(day); + } + } else { + delete day; + kdError()<<k_funcinfo<<"Failed to load calendarDay"<<endl; + return true; //false; don't throw away the whole calendar + } + } + } + } + return true; +} + +void Calendar::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<m_name<<endl; + if (m_deleted) + return; + + QDomElement me = element.ownerDocument().createElement("calendar"); + element.appendChild(me); + if (m_parent && !m_parent->isDeleted()) + me.setAttribute("parent", m_parent->id()); + me.setAttribute("name", m_name); + me.setAttribute("id", m_id); + m_weekdays->save(me); + QPtrListIterator<CalendarDay> it = m_days; + for (; it.current(); ++it) { + QDomElement e = me.ownerDocument().createElement("day"); + me.appendChild(e); + it.current()->save(e); + } + +} + +CalendarDay *Calendar::findDay(const QDate &date, bool skipNone) const { + //kdDebug()<<k_funcinfo<<date.toString()<<endl; + QPtrListIterator<CalendarDay> it = m_days; + for (; it.current(); ++it) { + if (it.current()->date() == date) { + if (skipNone && it.current()->state() == Map::None) { + continue; // hmmm, break? + } + return it.current(); + } + } + //kdDebug()<<k_funcinfo<<date.toString()<<" not found"<<endl; + return 0; +} + +bool Calendar::hasParent(Calendar *cal) { + //kdDebug()<<k_funcinfo<<endl; + if (!m_parent) + return false; + if (m_parent == cal) + return true; + return m_parent->hasParent(cal); +} + +Duration Calendar::effort(const QDate &date, const QTime &start, const QTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<date.toString(Qt::ISODate)<<" "<<start.toString()<<" - "<<end.toString()<<endl; + if (start == end) { + return Duration::zeroDuration; + } + QTime _start = start; + QTime _end = end; + if (start > end) { + _start = end; + _end = start; + } + // first, check my own day + CalendarDay *day = findDay(date, true); + if (day) { + if (day->state() == Map::Working) { + return day->effort(_start, _end); + } else if (day->state() == Map::NonWorking) { + return Duration::zeroDuration; + } else { + kdError()<<k_funcinfo<<"Invalid state: "<<day->state()<<endl; + return Duration::zeroDuration; + } + } + // check my own weekdays + if (m_weekdays) { + if (m_weekdays->state(date) == Map::Working) { + return m_weekdays->effort(date, _start, _end); + } + if (m_weekdays->state(date) == Map::NonWorking) { + return Duration::zeroDuration; + } + } + if (m_parent && !m_parent->isDeleted()) { + return m_parent->effort(date, start, end); + } + // Check default calendar + return project()->defaultCalendar()->effort(date, start, end); +} + +Duration Calendar::effort(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<start<<" to "<<end<<endl; + Duration eff; + if (!start.isValid() || !end.isValid() || end <= start) { + return eff; + } + QDate date = start.date(); + QTime startTime = start.time(); + QTime endTime = end.time(); + if (end.date() > date) { + endTime.setHMS(23, 59, 59, 999); + } + eff = effort(date, startTime, endTime); // first day + // Now get all the rest of the days + for (date = date.addDays(1); date <= end.date(); date = date.addDays(1)) { + if (date < end.date()) + eff += effort(date, QTime(), endTime); // whole days + else + eff += effort(date, QTime(), end.time()); // last day + //kdDebug()<<k_funcinfo<<": eff now="<<eff.toString(Duration::Format_Day)<<endl; + } + //kdDebug()<<k_funcinfo<<start.date().toString()<<"- "<<end.date().toString()<<": total="<<eff.toString(Duration::Format_Day)<<endl; + return eff; +} + + +QPair<QTime, QTime> Calendar::firstInterval(const QDate &date, const QTime &startTime, const QTime &endTime) const { + CalendarDay *day = findDay(date, true); + if (day) { + return day->interval(startTime, endTime); + } + if (m_weekdays) { + if (m_weekdays->state(date) == Map::Working) { + return m_weekdays->interval(date, startTime, endTime); + } + if (m_weekdays->state(date) == Map::NonWorking) { + return QPair<QTime, QTime>(QTime(), QTime()); + } + } + if (m_parent && !m_parent->isDeleted()) { + return m_parent->firstInterval(date, startTime, endTime); + } + return project()->defaultCalendar()->firstInterval(date, startTime, endTime); +} + +QPair<DateTime, DateTime> Calendar::firstInterval(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<start.toString()<<" - "<<end.toString()<<endl; + if (!start.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid start time"<<endl; + return QPair<DateTime, DateTime>(DateTime(), DateTime()); + } + if (!end.isValid()) { + kdWarning()<<k_funcinfo<<"Invalid end time"<<endl; + return QPair<DateTime, DateTime>(DateTime(), DateTime()); + } + QTime startTime; + QTime endTime; + QDate date = start.date(); + int i=0; + for (; date <= end.date(); date = date.addDays(1)) { + if (date < end.date()) + endTime = QTime(23, 59, 59, 999); + else + endTime = end.time(); + if (date > start.date()) + startTime = QTime(); + else + startTime = start.time(); + + QPair<QTime, QTime> res = firstInterval(date, startTime, endTime); + if (res.first < res.second) { + return QPair<DateTime, DateTime>(DateTime(date,res.first),DateTime(date, res.second)); + } + } + //kdError()<<k_funcinfo<<"Didn't find an interval ("<<start<<", "<<end<<")"<<endl; + return QPair<DateTime, DateTime>(DateTime(), DateTime()); +} + + +bool Calendar::hasInterval(const QDate &date, const QTime &startTime, const QTime &endTime) const { + CalendarDay *day = findDay(date, true); + if (day) { + //kdDebug()<<k_funcinfo<<m_name<<" "<<date<<": "<<startTime<<" to "<<endTime<<endl; + return day->hasInterval(startTime, endTime); + } + if (m_weekdays) { + if (m_weekdays->state(date) == Map::Working) { + return m_weekdays->hasInterval(date, startTime, endTime); + } else if (m_weekdays->state(date) == Map::NonWorking) { + return false; + } + } + if (m_parent && !m_parent->isDeleted()) { + return m_parent->hasInterval(date, startTime, endTime); + } + return project()->defaultCalendar()->hasInterval(date, startTime, endTime); +} + +bool Calendar::hasInterval(const DateTime &start, const DateTime &end) const { + //kdDebug()<<k_funcinfo<<m_name<<": "<<start<<" - "<<end<<endl; + if (!start.isValid() || !end.isValid() || end <= start) { + //kdError()<<k_funcinfo<<"Invalid input: "<<(start.isValid()?"":"(start invalid) ")<<(end.isValid()?"":"(end invalid) ")<<(start>end?"":"(start<=end)")<<endl; + //kdDebug()<<kdBacktrace(8)<<endl; + return false; + } + QTime startTime; + QTime endTime; + QDate date = start.date(); + for (; date <= end.date(); date = date.addDays(1)) { + if (date < end.date()) + endTime = QTime(23, 59, 59, 999); + else + endTime = end.time(); + if (date > start.date()) + startTime = QTime(); + else + startTime = start.time(); + + if (hasInterval(date, startTime, endTime)) + return true; + } + return false; +} + +DateTime Calendar::firstAvailableAfter(const DateTime &time, const DateTime &limit) { + //kdDebug()<<k_funcinfo<<m_name<<": check from "<<time.toString()<<" limit="<<limit.toString()<<endl; + if (!time.isValid() || !limit.isValid() || time >= limit) { + kdError()<<k_funcinfo<<"Invalid input: "<<(time.isValid()?"":"(time invalid) ")<<(limit.isValid()?"":"(limit invalid) ")<<(time>limit?"":"(time>=limit)")<<endl; + return DateTime(); + } + if (!hasInterval(time, limit)) { + return DateTime(); + } + DateTime t = firstInterval(time, limit).first; + //kdDebug()<<k_funcinfo<<m_name<<": "<<t.toString()<<endl; + return t; +} + +DateTime Calendar::firstAvailableBefore(const DateTime &time, const DateTime &limit) { + //kdDebug()<<k_funcinfo<<m_name<<": check from "<<time.toString()<<" limit="<<limit.toString()<<endl; + if (!time.isValid() || !limit.isValid() || time <= limit) { + kdError()<<k_funcinfo<<"Invalid input: "<<(time.isValid()?"":"(time invalid) ")<<(limit.isValid()?"":"(limit invalid) ")<<(time>limit?"":"(time<=limit)")<<endl; + return DateTime(); + } + DateTime lmt = time; + DateTime t = DateTime(time.date()); // start of first day + if (t == lmt) + t = t.addDays(-1); // in case time == start of day + if (t < limit) + t = limit; // always stop at limit (lower boundary) + DateTime res; + //kdDebug()<<k_funcinfo<<m_name<<": t="<<t<<", "<<lmt<<" limit="<<limit<<endl; + while (!res.isValid() && t >= limit) { + // check intervals for 1 day + DateTime r = firstInterval(t, lmt).second; + res = r; + // Find the last interval + while(r.isValid() && r < lmt) { + r = firstInterval(r, lmt).second; + if (r.isValid()) + res = r; + //kdDebug()<<k_funcinfo<<m_name<<": r="<<r<<", "<<lmt<<" res="<<res<<endl; + } + if (!res.isValid()) { + if (t == limit) { + break; + } + lmt = t; + t = t.addDays(-1); + if (t < limit) { + t = limit; + } + if (t == lmt) + break; + } + } + //kdDebug()<<k_funcinfo<<m_name<<": "<<res<<endl; + return res; +} + +Calendar *Calendar::findCalendar(const QString &id) const { + return (m_project ? m_project->findCalendar(id) : 0); +} + +bool Calendar::removeId(const QString &id) { + return (m_project ? m_project->removeCalendarId(id) : false); +} + +void Calendar::insertId(const QString &id){ + if (m_project) + m_project->insertCalendarId(id, this); +} + +///////////// +StandardWorktime::StandardWorktime() { + init(); +} + +StandardWorktime::StandardWorktime(StandardWorktime *worktime) { + if (worktime) { + m_year = worktime->durationYear(); + m_month = worktime->durationMonth(); + m_week = worktime->durationWeek(); + m_day = worktime->durationDay(); + m_calendar = new Calendar(*(worktime->calendar())); + } else { + init(); + } +} + +StandardWorktime::~StandardWorktime() { + //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl; +} + +void StandardWorktime::init() { + // Some sane default values + m_year = Duration(0, 1760, 0); + m_month = Duration(0, 176, 0); + m_week = Duration(0, 40, 0); + m_day = Duration(0, 8, 0); + m_calendar = new Calendar; + m_calendar->setName(i18n("Base")); + QPair<QTime, QTime> t = QPair<QTime, QTime>(QTime(8,0,0), QTime(16,0,0)); + for (int i=0; i < 5; ++i) { + m_calendar->weekday(i)->addInterval(t); + m_calendar->weekday(i)->setState(Map::Working); + } + m_calendar->weekday(5)->setState(Map::NonWorking); + m_calendar->weekday(6)->setState(Map::NonWorking); +} + +bool StandardWorktime::load(QDomElement &element) { + //kdDebug()<<k_funcinfo<<endl; + m_year = Duration::fromString(element.attribute("year"), Duration::Format_Hour); + m_month = Duration::fromString(element.attribute("month"), Duration::Format_Hour); + m_week = Duration::fromString(element.attribute("week"), Duration::Format_Hour); + m_day = Duration::fromString(element.attribute("day"), Duration::Format_Hour); + + QDomNodeList list = element.childNodes(); + for (unsigned int i=0; i<list.count(); ++i) { + if (list.item(i).isElement()) { + QDomElement e = list.item(i).toElement(); + if (e.tagName() == "calendar") { + delete m_calendar; + m_calendar = new Calendar; + m_calendar->load(e); + } + } + } + return true; +} + +void StandardWorktime::save(QDomElement &element) const { + //kdDebug()<<k_funcinfo<<endl; + QDomElement me = element.ownerDocument().createElement("standard-worktime"); + element.appendChild(me); + me.setAttribute("year", m_year.toString(Duration::Format_Hour)); + me.setAttribute("month", m_month.toString(Duration::Format_Hour)); + me.setAttribute("week", m_week.toString(Duration::Format_Hour)); + me.setAttribute("day", m_day.toString(Duration::Format_Hour)); + + m_calendar->save(me); +} + +#ifndef NDEBUG +void CalendarDay::printDebug(QCString indent) { + QString s[] = {"None", "Non-working", "Working"}; + kdDebug()<<indent<<" "<<m_date.toString()<<" = "<<s[m_state]<<endl; + if (m_state == Map::Working) { + indent += " "; + QPtrListIterator<QPair<QTime, QTime> > it = m_workingIntervals; + for (; it.current(); ++it) { + kdDebug()<<indent<<" Interval: "<<it.current()->first<<" to "<<it.current()->second<<endl; + } + } + +} +void CalendarWeekdays::printDebug(QCString indent) { + kdDebug()<<indent<<"Weekdays ------"<<endl; + QPtrListIterator<CalendarDay> it = m_weekdays; + for (char c='0'; it.current(); ++it) { + it.current()->printDebug(indent + " Day " + c++ + ": "); + } + +} +void Calendar::printDebug(QCString indent) { + kdDebug()<<indent<<"Calendar "<<m_id<<": '"<<m_name<<"' Deleted="<<m_deleted<<endl; + kdDebug()<<indent<<" Parent: "<<(m_parent ? m_parent->name() : "No parent")<<endl; + m_weekdays->printDebug(indent + " "); + kdDebug()<<indent<<" Days --------"<<endl; + QPtrListIterator<CalendarDay> it = m_days; + for (; it.current(); ++it) { + it.current()->printDebug(indent + " "); + } +} + +void StandardWorktime::printDebug(QCString indent) { + kdDebug()<<indent<<"StandardWorktime "<<endl; + kdDebug()<<indent<<"Year: "<<m_year.toString()<<endl; + kdDebug()<<indent<<"Month: "<<m_month.toString()<<endl; + kdDebug()<<indent<<"Week: "<<m_week.toString()<<endl; + kdDebug()<<indent<<"Day: "<<m_day.toString()<<endl; +} + +#endif + +} //KPlato namespace |