summaryrefslogtreecommitdiffstats
path: root/kplato/kptcalendar.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kplato/kptcalendar.cc')
-rw-r--r--kplato/kptcalendar.cc1036
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