todo.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the kcal library. 00003 00004 Copyright (c) 2001-2003 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (C) 2009 Allen Winter <winter@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00034 #include "todo.h" 00035 #include "incidenceformatter.h" 00036 00037 #include <kglobal.h> 00038 #include <klocale.h> 00039 #include <kdebug.h> 00040 #include <ksystemtimezone.h> 00041 00042 using namespace KCal; 00043 00048 //@cond PRIVATE 00049 class KCal::Todo::Private 00050 { 00051 public: 00052 Private() 00053 : mPercentComplete( 0 ), 00054 mHasDueDate( false ), 00055 mHasStartDate( false ), 00056 mHasCompletedDate( false ) 00057 {} 00058 Private( const KCal::Todo::Private &other ) 00059 { init( other ); } 00060 00061 void init( const KCal::Todo::Private &other ); 00062 00063 KDateTime mDtDue; // to-do due date (if there is one) 00064 // ALSO the first occurrence of a recurring to-do 00065 KDateTime mDtRecurrence; // next occurrence (for recurring to-dos) 00066 KDateTime mCompleted; // to-do completion date (if it has been completed) 00067 int mPercentComplete; // to-do percent complete [0,100] 00068 bool mHasDueDate; // true if the to-do has a due date 00069 bool mHasStartDate; // true if the to-do has a starting date 00070 bool mHasCompletedDate; // true if the to-do has a completion date 00071 00075 bool recurTodo( Todo *todo ); 00076 }; 00077 00078 void KCal::Todo::Private::init( const KCal::Todo::Private &other ) 00079 { 00080 mDtDue = other.mDtDue; 00081 mDtRecurrence = other.mDtRecurrence; 00082 mCompleted = other.mCompleted; 00083 mPercentComplete = other.mPercentComplete; 00084 mHasDueDate = other.mHasDueDate; 00085 mHasStartDate = other.mHasStartDate; 00086 mHasCompletedDate = other.mHasCompletedDate; 00087 } 00088 00089 //@endcond 00090 00091 Todo::Todo() 00092 : d( new KCal::Todo::Private ) 00093 { 00094 } 00095 00096 Todo::Todo( const Todo &other ) 00097 : Incidence( other ), 00098 d( new KCal::Todo::Private( *other.d ) ) 00099 { 00100 } 00101 00102 Todo::~Todo() 00103 { 00104 delete d; 00105 } 00106 00107 Todo *Todo::clone() 00108 { 00109 return new Todo( *this ); 00110 } 00111 00112 Todo &Todo::operator=( const Todo &other ) 00113 { 00114 // check for self assignment 00115 if ( &other == this ) { 00116 return *this; 00117 } 00118 00119 Incidence::operator=( other ); 00120 d->init( *other.d ); 00121 return *this; 00122 } 00123 00124 bool Todo::operator==( const Todo &todo ) const 00125 { 00126 return 00127 Incidence::operator==( todo ) && 00128 dtDue() == todo.dtDue() && 00129 hasDueDate() == todo.hasDueDate() && 00130 hasStartDate() == todo.hasStartDate() && 00131 completed() == todo.completed() && 00132 hasCompletedDate() == todo.hasCompletedDate() && 00133 percentComplete() == todo.percentComplete(); 00134 } 00135 00136 QByteArray Todo::type() const 00137 { 00138 return "Todo"; 00139 } 00140 00141 //KDE5: 00142 //QString Todo::typeStr() const 00143 //{ 00144 // return i18nc( "incidence type is to-do/task", "to-do" ); 00145 //} 00146 00147 void Todo::setDtDue( const KDateTime &dtDue, bool first ) 00148 { 00149 //int diffsecs = d->mDtDue.secsTo(dtDue); 00150 00151 /*if (mReadOnly) return; 00152 const Alarm::List& alarms = alarms(); 00153 for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) { 00154 if (alarm->enabled()) { 00155 alarm->setTime(alarm->time().addSecs(diffsecs)); 00156 } 00157 }*/ 00158 00159 d->mHasDueDate = true; 00160 if ( recurs() && !first ) { 00161 d->mDtRecurrence = dtDue; 00162 } else { 00163 d->mDtDue = dtDue; 00164 // TODO: This doesn't seem right... 00165 recurrence()->setStartDateTime( dtDue ); 00166 recurrence()->setAllDay( allDay() ); 00167 } 00168 00169 if ( recurs() && dtDue < recurrence()->startDateTime() ) { 00170 setDtStart( dtDue ); 00171 } 00172 00173 /*const Alarm::List& alarms = alarms(); 00174 for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) 00175 alarm->setAlarmStart(d->mDtDue);*/ 00176 00177 updated(); 00178 } 00179 00180 KDateTime Todo::dtDue( bool first ) const 00181 { 00182 if ( !hasDueDate() ) { 00183 return KDateTime(); 00184 } 00185 if ( recurs() && !first && d->mDtRecurrence.isValid() ) { 00186 return d->mDtRecurrence; 00187 } 00188 00189 return d->mDtDue; 00190 } 00191 00192 QString Todo::dtDueTimeStr( bool shortfmt, const KDateTime::Spec &spec ) const 00193 { 00194 if ( spec.isValid() ) { 00195 00196 QString timeZone; 00197 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00198 timeZone = ' ' + spec.timeZone().name(); 00199 } 00200 00201 return KGlobal::locale()->formatTime( 00202 dtDue( !recurs() ).toTimeSpec( spec ).time(), !shortfmt ) + timeZone; 00203 } else { 00204 return KGlobal::locale()->formatTime( 00205 dtDue( !recurs() ).time(), !shortfmt ); 00206 } 00207 } 00208 00209 QString Todo::dtDueDateStr( bool shortfmt, const KDateTime::Spec &spec ) const 00210 { 00211 if ( spec.isValid() ) { 00212 00213 QString timeZone; 00214 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00215 timeZone = ' ' + spec.timeZone().name(); 00216 } 00217 00218 return KGlobal::locale()->formatDate( 00219 dtDue( !recurs() ).toTimeSpec( spec ).date(), 00220 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; 00221 } else { 00222 return KGlobal::locale()->formatDate( 00223 dtDue( !recurs() ).date(), 00224 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); 00225 } 00226 } 00227 00228 QString Todo::dtDueStr( bool shortfmt, const KDateTime::Spec &spec ) const 00229 { 00230 if ( allDay() ) { 00231 return IncidenceFormatter::dateToString( dtDue(), shortfmt, spec ); 00232 } 00233 00234 if ( spec.isValid() ) { 00235 00236 QString timeZone; 00237 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00238 timeZone = ' ' + spec.timeZone().name(); 00239 } 00240 00241 return KGlobal::locale()->formatDateTime( 00242 dtDue( !recurs() ).toTimeSpec( spec ).dateTime(), 00243 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; 00244 } else { 00245 return KGlobal::locale()->formatDateTime( 00246 dtDue( !recurs() ).dateTime(), 00247 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); 00248 } 00249 } 00250 00251 bool Todo::hasDueDate() const 00252 { 00253 return d->mHasDueDate; 00254 } 00255 00256 void Todo::setHasDueDate( bool f ) 00257 { 00258 if ( mReadOnly ) { 00259 return; 00260 } 00261 d->mHasDueDate = f; 00262 updated(); 00263 } 00264 00265 bool Todo::hasStartDate() const 00266 { 00267 return d->mHasStartDate; 00268 } 00269 00270 void Todo::setHasStartDate( bool f ) 00271 { 00272 if ( mReadOnly ) { 00273 return; 00274 } 00275 00276 if ( recurs() && !f ) { 00277 if ( !comments().filter( "NoStartDate" ).count() ) { 00278 addComment( "NoStartDate" ); //TODO: --> custom flag? 00279 } 00280 } else { 00281 QString s( "NoStartDate" ); 00282 removeComment( s ); 00283 } 00284 d->mHasStartDate = f; 00285 updated(); 00286 } 00287 00288 KDateTime Todo::dtStart() const 00289 { 00290 return dtStart( false ); 00291 } 00292 00293 KDateTime Todo::dtStart( bool first ) const 00294 { 00295 if ( !hasStartDate() ) { 00296 return KDateTime(); 00297 } 00298 if ( recurs() && !first ) { 00299 KDateTime dt = d->mDtRecurrence.addDays( dtDue( true ).daysTo( IncidenceBase::dtStart() ) ); 00300 dt.setTime( IncidenceBase::dtStart().time() ); 00301 return dt; 00302 } else { 00303 return IncidenceBase::dtStart(); 00304 } 00305 } 00306 00307 void Todo::setDtStart( const KDateTime &dtStart ) 00308 { 00309 // TODO: This doesn't seem right (rfc 2445/6 says, recurrence is calculated from the dtstart...) 00310 if ( recurs() ) { 00311 recurrence()->setStartDateTime( d->mDtDue ); 00312 recurrence()->setAllDay( allDay() ); 00313 } 00314 d->mHasStartDate = true; 00315 IncidenceBase::setDtStart( dtStart ); 00316 } 00317 00318 QString Todo::dtStartTimeStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const 00319 { 00320 if ( spec.isValid() ) { 00321 00322 QString timeZone; 00323 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00324 timeZone = ' ' + spec.timeZone().name(); 00325 } 00326 00327 return KGlobal::locale()->formatTime( 00328 dtStart( first ).toTimeSpec( spec ).time(), !shortfmt ) + timeZone; 00329 } else { 00330 return KGlobal::locale()->formatTime( 00331 dtStart( first ).time(), !shortfmt ); 00332 } 00333 } 00334 00335 QString Todo::dtStartTimeStr( bool shortfmt, const KDateTime::Spec &spec ) const 00336 { 00337 return IncidenceFormatter::timeToString( dtStart(), shortfmt, spec ); 00338 } 00339 00340 QString Todo::dtStartDateStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const 00341 { 00342 if ( spec.isValid() ) { 00343 00344 QString timeZone; 00345 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00346 timeZone = ' ' + spec.timeZone().name(); 00347 } 00348 00349 return KGlobal::locale()->formatDate( 00350 dtStart( first ).toTimeSpec( spec ).date(), 00351 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; 00352 } else { 00353 return KGlobal::locale()->formatDate( 00354 dtStart( first ).date(), 00355 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); 00356 } 00357 } 00358 00359 QString Todo::dtStartDateStr( bool shortfmt, const KDateTime::Spec &spec ) const 00360 { 00361 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec ); 00362 } 00363 00364 QString Todo::dtStartStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const 00365 { 00366 if ( allDay() ) { 00367 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec ); 00368 } 00369 00370 if ( spec.isValid() ) { 00371 00372 QString timeZone; 00373 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00374 timeZone = ' ' + spec.timeZone().name(); 00375 } 00376 00377 return KGlobal::locale()->formatDateTime( 00378 dtStart( first ).toTimeSpec( spec ).dateTime(), 00379 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; 00380 } else { 00381 return KGlobal::locale()->formatDateTime( 00382 dtStart( first ).dateTime(), 00383 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); 00384 } 00385 } 00386 00387 QString Todo::dtStartStr( bool shortfmt, const KDateTime::Spec &spec ) const 00388 { 00389 if ( allDay() ) { 00390 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec ); 00391 } 00392 00393 if ( spec.isValid() ) { 00394 00395 QString timeZone; 00396 if ( spec.timeZone() != KSystemTimeZones::local() ) { 00397 timeZone = ' ' + spec.timeZone().name(); 00398 } 00399 00400 return KGlobal::locale()->formatDateTime( 00401 dtStart().toTimeSpec( spec ).dateTime(), 00402 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; 00403 } else { 00404 return KGlobal::locale()->formatDateTime( 00405 dtStart().dateTime(), 00406 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); 00407 } 00408 } 00409 00410 bool Todo::isCompleted() const 00411 { 00412 if ( d->mPercentComplete == 100 ) { 00413 return true; 00414 } else { 00415 return false; 00416 } 00417 } 00418 00419 void Todo::setCompleted( bool completed ) 00420 { 00421 if ( completed ) { 00422 d->mPercentComplete = 100; 00423 } else { 00424 d->mPercentComplete = 0; 00425 d->mHasCompletedDate = false; 00426 d->mCompleted = KDateTime(); 00427 } 00428 updated(); 00429 } 00430 00431 KDateTime Todo::completed() const 00432 { 00433 if ( hasCompletedDate() ) { 00434 return d->mCompleted; 00435 } else { 00436 return KDateTime(); 00437 } 00438 } 00439 00440 QString Todo::completedStr( bool shortfmt ) const 00441 { 00442 return 00443 KGlobal::locale()->formatDateTime( d->mCompleted.dateTime(), 00444 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); 00445 } 00446 00447 void Todo::setCompleted( const KDateTime &completed ) 00448 { 00449 if ( !d->recurTodo( this ) ) { 00450 d->mHasCompletedDate = true; 00451 d->mPercentComplete = 100; 00452 d->mCompleted = completed.toUtc(); 00453 } 00454 updated(); 00455 } 00456 00457 bool Todo::hasCompletedDate() const 00458 { 00459 return d->mHasCompletedDate; 00460 } 00461 00462 int Todo::percentComplete() const 00463 { 00464 return d->mPercentComplete; 00465 } 00466 00467 void Todo::setPercentComplete( int percent ) 00468 { 00469 //TODO: (?) assert percent between 0 and 100, inclusive 00470 d->mPercentComplete = percent; 00471 if ( percent != 100 ) { 00472 d->mHasCompletedDate = false; 00473 } 00474 updated(); 00475 } 00476 00477 bool Todo::isInProgress( bool first ) const 00478 { 00479 if ( isOverdue() ) { 00480 return false; 00481 } 00482 00483 if ( d->mPercentComplete > 0 ) { 00484 return true; 00485 } 00486 00487 if ( d->mHasStartDate && d->mHasDueDate ) { 00488 if ( allDay() ) { 00489 QDate currDate = QDate::currentDate(); 00490 if ( dtStart( first ).date() <= currDate && currDate < dtDue( first ).date() ) { 00491 return true; 00492 } 00493 } else { 00494 KDateTime currDate = KDateTime::currentUtcDateTime(); 00495 if ( dtStart( first ) <= currDate && currDate < dtDue( first ) ) { 00496 return true; 00497 } 00498 } 00499 } 00500 00501 return false; 00502 } 00503 00504 bool Todo::isOpenEnded() const 00505 { 00506 if ( !d->mHasDueDate && !isCompleted() ) { 00507 return true; 00508 } 00509 return false; 00510 00511 } 00512 00513 bool Todo::isNotStarted( bool first ) const 00514 { 00515 if ( d->mPercentComplete > 0 ) { 00516 return false; 00517 } 00518 00519 if ( !d->mHasStartDate ) { 00520 return false; 00521 } 00522 00523 if ( allDay() ) { 00524 if ( dtStart( first ).date() >= QDate::currentDate() ) { 00525 return false; 00526 } 00527 } else { 00528 if ( dtStart( first ) >= KDateTime::currentUtcDateTime() ) { 00529 return false; 00530 } 00531 } 00532 return true; 00533 } 00534 00535 void Todo::shiftTimes( const KDateTime::Spec &oldSpec, 00536 const KDateTime::Spec &newSpec ) 00537 { 00538 Incidence::shiftTimes( oldSpec, newSpec ); 00539 d->mDtDue = d->mDtDue.toTimeSpec( oldSpec ); 00540 d->mDtDue.setTimeSpec( newSpec ); 00541 if ( recurs() ) { 00542 d->mDtRecurrence = d->mDtRecurrence.toTimeSpec( oldSpec ); 00543 d->mDtRecurrence.setTimeSpec( newSpec ); 00544 } 00545 if ( d->mHasCompletedDate ) { 00546 d->mCompleted = d->mCompleted.toTimeSpec( oldSpec ); 00547 d->mCompleted.setTimeSpec( newSpec ); 00548 } 00549 } 00550 00551 void Todo::setDtRecurrence( const KDateTime &dt ) 00552 { 00553 d->mDtRecurrence = dt; 00554 } 00555 00556 KDateTime Todo::dtRecurrence() const 00557 { 00558 return d->mDtRecurrence.isValid() ? d->mDtRecurrence : d->mDtDue; 00559 } 00560 00561 bool Todo::recursOn( const QDate &date, const KDateTime::Spec &timeSpec ) const 00562 { 00563 QDate today = QDate::currentDate(); 00564 return 00565 Incidence::recursOn( date, timeSpec ) && 00566 !( date < today && d->mDtRecurrence.date() < today && 00567 d->mDtRecurrence > recurrence()->startDateTime() ); 00568 } 00569 00570 bool Todo::isOverdue() const 00571 { 00572 if ( !dtDue().isValid() ) { 00573 return false; // if it's never due, it can't be overdue 00574 } 00575 00576 bool inPast = allDay() ? 00577 dtDue().date() < QDate::currentDate() : 00578 dtDue() < KDateTime::currentUtcDateTime(); 00579 return inPast && !isCompleted(); 00580 } 00581 00582 KDateTime Todo::endDateRecurrenceBase() const 00583 { 00584 return dtDue(); 00585 } 00586 00587 //@cond PRIVATE 00588 bool Todo::Private::recurTodo( Todo *todo ) 00589 { 00590 if ( todo->recurs() ) { 00591 Recurrence *r = todo->recurrence(); 00592 KDateTime endDateTime = r->endDateTime(); 00593 KDateTime nextDate = r->getNextDateTime( todo->dtDue() ); 00594 00595 if ( ( r->duration() == -1 || 00596 ( nextDate.isValid() && endDateTime.isValid() && 00597 nextDate <= endDateTime ) ) ) { 00598 00599 while ( !todo->recursAt( nextDate ) || 00600 nextDate <= KDateTime::currentUtcDateTime() ) { 00601 00602 if ( !nextDate.isValid() || 00603 ( nextDate > endDateTime && r->duration() != -1 ) ) { 00604 00605 return false; 00606 } 00607 00608 nextDate = r->getNextDateTime( nextDate ); 00609 } 00610 00611 todo->setDtDue( nextDate ); 00612 todo->setCompleted( false ); 00613 todo->setRevision( todo->revision() + 1 ); 00614 00615 return true; 00616 } 00617 } 00618 00619 return false; 00620 } 00621 //@endcond