00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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;
00064
00065 KDateTime mDtRecurrence;
00066 KDateTime mCompleted;
00067 int mPercentComplete;
00068 bool mHasDueDate;
00069 bool mHasStartDate;
00070 bool mHasCompletedDate;
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
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
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
00142
00143
00144
00145
00146
00147 void Todo::setDtDue( const KDateTime &dtDue, bool first )
00148 {
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159 d->mHasDueDate = true;
00160 if ( recurs() && !first ) {
00161 d->mDtRecurrence = dtDue;
00162 } else {
00163 d->mDtDue = dtDue;
00164
00165 recurrence()->setStartDateTime( dtDue );
00166 recurrence()->setAllDay( allDay() );
00167 }
00168
00169 if ( recurs() && dtDue < recurrence()->startDateTime() ) {
00170 setDtStart( dtDue );
00171 }
00172
00173
00174
00175
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" );
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 return d->mDtRecurrence.addDays( dtDue( true ).daysTo( IncidenceBase::dtStart() ) );
00300 } else {
00301 return IncidenceBase::dtStart();
00302 }
00303 }
00304
00305 void Todo::setDtStart( const KDateTime &dtStart )
00306 {
00307
00308 if ( recurs() ) {
00309 recurrence()->setStartDateTime( d->mDtDue );
00310 recurrence()->setAllDay( allDay() );
00311 }
00312 d->mHasStartDate = true;
00313 IncidenceBase::setDtStart( dtStart );
00314 }
00315
00316 QString Todo::dtStartTimeStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const
00317 {
00318 if ( spec.isValid() ) {
00319
00320 QString timeZone;
00321 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00322 timeZone = ' ' + spec.timeZone().name();
00323 }
00324
00325 return KGlobal::locale()->formatTime(
00326 dtStart( first ).toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
00327 } else {
00328 return KGlobal::locale()->formatTime(
00329 dtStart( first ).time(), !shortfmt );
00330 }
00331 }
00332
00333 QString Todo::dtStartTimeStr( bool shortfmt, const KDateTime::Spec &spec ) const
00334 {
00335 return IncidenceFormatter::timeToString( dtStart(), shortfmt, spec );
00336 }
00337
00338 QString Todo::dtStartDateStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const
00339 {
00340 if ( spec.isValid() ) {
00341
00342 QString timeZone;
00343 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00344 timeZone = ' ' + spec.timeZone().name();
00345 }
00346
00347 return KGlobal::locale()->formatDate(
00348 dtStart( first ).toTimeSpec( spec ).date(),
00349 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00350 } else {
00351 return KGlobal::locale()->formatDate(
00352 dtStart( first ).date(),
00353 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00354 }
00355 }
00356
00357 QString Todo::dtStartDateStr( bool shortfmt, const KDateTime::Spec &spec ) const
00358 {
00359 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec );
00360 }
00361
00362 QString Todo::dtStartStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const
00363 {
00364 if ( allDay() ) {
00365 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec );
00366 }
00367
00368 if ( spec.isValid() ) {
00369
00370 QString timeZone;
00371 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00372 timeZone = ' ' + spec.timeZone().name();
00373 }
00374
00375 return KGlobal::locale()->formatDateTime(
00376 dtStart( first ).toTimeSpec( spec ).dateTime(),
00377 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00378 } else {
00379 return KGlobal::locale()->formatDateTime(
00380 dtStart( first ).dateTime(),
00381 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00382 }
00383 }
00384
00385 QString Todo::dtStartStr( bool shortfmt, const KDateTime::Spec &spec ) const
00386 {
00387 if ( allDay() ) {
00388 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec );
00389 }
00390
00391 if ( spec.isValid() ) {
00392
00393 QString timeZone;
00394 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00395 timeZone = ' ' + spec.timeZone().name();
00396 }
00397
00398 return KGlobal::locale()->formatDateTime(
00399 dtStart().toTimeSpec( spec ).dateTime(),
00400 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00401 } else {
00402 return KGlobal::locale()->formatDateTime(
00403 dtStart().dateTime(),
00404 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00405 }
00406 }
00407
00408 bool Todo::isCompleted() const
00409 {
00410 if ( d->mPercentComplete == 100 ) {
00411 return true;
00412 } else {
00413 return false;
00414 }
00415 }
00416
00417 void Todo::setCompleted( bool completed )
00418 {
00419 if ( completed ) {
00420 d->mPercentComplete = 100;
00421 } else {
00422 d->mPercentComplete = 0;
00423 d->mHasCompletedDate = false;
00424 d->mCompleted = KDateTime();
00425 }
00426 updated();
00427 }
00428
00429 KDateTime Todo::completed() const
00430 {
00431 if ( hasCompletedDate() ) {
00432 return d->mCompleted;
00433 } else {
00434 return KDateTime();
00435 }
00436 }
00437
00438 QString Todo::completedStr( bool shortfmt ) const
00439 {
00440 return
00441 KGlobal::locale()->formatDateTime( d->mCompleted.dateTime(),
00442 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00443 }
00444
00445 void Todo::setCompleted( const KDateTime &completed )
00446 {
00447 if ( !d->recurTodo( this ) ) {
00448 d->mHasCompletedDate = true;
00449 d->mPercentComplete = 100;
00450 d->mCompleted = completed.toUtc();
00451 }
00452 updated();
00453 }
00454
00455 bool Todo::hasCompletedDate() const
00456 {
00457 return d->mHasCompletedDate;
00458 }
00459
00460 int Todo::percentComplete() const
00461 {
00462 return d->mPercentComplete;
00463 }
00464
00465 void Todo::setPercentComplete( int percent )
00466 {
00467
00468 d->mPercentComplete = percent;
00469 if ( percent != 100 ) {
00470 d->mHasCompletedDate = false;
00471 }
00472 updated();
00473 }
00474
00475 bool Todo::isInProgress( bool first ) const
00476 {
00477 if ( isOverdue() ) {
00478 return false;
00479 }
00480
00481 if ( d->mPercentComplete > 0 ) {
00482 return true;
00483 }
00484
00485 if ( d->mHasStartDate && d->mHasDueDate ) {
00486 if ( allDay() ) {
00487 QDate currDate = QDate::currentDate();
00488 if ( dtStart( first ).date() <= currDate && currDate < dtDue( first ).date() ) {
00489 return true;
00490 }
00491 } else {
00492 KDateTime currDate = KDateTime::currentUtcDateTime();
00493 if ( dtStart( first ) <= currDate && currDate < dtDue( first ) ) {
00494 return true;
00495 }
00496 }
00497 }
00498
00499 return false;
00500 }
00501
00502 bool Todo::isOpenEnded() const
00503 {
00504 if ( !d->mHasDueDate && !isCompleted() ) {
00505 return true;
00506 }
00507 return false;
00508
00509 }
00510
00511 bool Todo::isNotStarted( bool first ) const
00512 {
00513 if ( d->mPercentComplete > 0 ) {
00514 return false;
00515 }
00516
00517 if ( !d->mHasStartDate ) {
00518 return false;
00519 }
00520
00521 if ( allDay() ) {
00522 if ( dtStart( first ).date() >= QDate::currentDate() ) {
00523 return false;
00524 }
00525 } else {
00526 if ( dtStart( first ) >= KDateTime::currentUtcDateTime() ) {
00527 return false;
00528 }
00529 }
00530 return true;
00531 }
00532
00533 void Todo::shiftTimes( const KDateTime::Spec &oldSpec,
00534 const KDateTime::Spec &newSpec )
00535 {
00536 Incidence::shiftTimes( oldSpec, newSpec );
00537 d->mDtDue = d->mDtDue.toTimeSpec( oldSpec );
00538 d->mDtDue.setTimeSpec( newSpec );
00539 if ( recurs() ) {
00540 d->mDtRecurrence = d->mDtRecurrence.toTimeSpec( oldSpec );
00541 d->mDtRecurrence.setTimeSpec( newSpec );
00542 }
00543 if ( d->mHasCompletedDate ) {
00544 d->mCompleted = d->mCompleted.toTimeSpec( oldSpec );
00545 d->mCompleted.setTimeSpec( newSpec );
00546 }
00547 }
00548
00549 void Todo::setDtRecurrence( const KDateTime &dt )
00550 {
00551 d->mDtRecurrence = dt;
00552 }
00553
00554 KDateTime Todo::dtRecurrence() const
00555 {
00556 return d->mDtRecurrence.isValid() ? d->mDtRecurrence : d->mDtDue;
00557 }
00558
00559 bool Todo::recursOn( const QDate &date, const KDateTime::Spec &timeSpec ) const
00560 {
00561 QDate today = QDate::currentDate();
00562 return
00563 Incidence::recursOn( date, timeSpec ) &&
00564 !( date < today && d->mDtRecurrence.date() < today &&
00565 d->mDtRecurrence > recurrence()->startDateTime() );
00566 }
00567
00568 bool Todo::isOverdue() const
00569 {
00570 if ( !dtDue().isValid() ) {
00571 return false;
00572 }
00573
00574 bool inPast = allDay() ?
00575 dtDue().date() < QDate::currentDate() :
00576 dtDue() < KDateTime::currentUtcDateTime();
00577 return inPast && !isCompleted();
00578 }
00579
00580 KDateTime Todo::endDateRecurrenceBase() const
00581 {
00582 return dtDue();
00583 }
00584
00585
00586 bool Todo::Private::recurTodo( Todo *todo )
00587 {
00588 if ( todo->recurs() ) {
00589 Recurrence *r = todo->recurrence();
00590 KDateTime endDateTime = r->endDateTime();
00591 KDateTime nextDate = r->getNextDateTime( todo->dtDue() );
00592
00593 if ( ( r->duration() == -1 ||
00594 ( nextDate.isValid() && endDateTime.isValid() &&
00595 nextDate <= endDateTime ) ) ) {
00596
00597 while ( !todo->recursAt( nextDate ) ||
00598 nextDate <= KDateTime::currentUtcDateTime() ) {
00599
00600 if ( !nextDate.isValid() ||
00601 ( nextDate > endDateTime && r->duration() != -1 ) ) {
00602
00603 return false;
00604 }
00605
00606 nextDate = r->getNextDateTime( nextDate );
00607 }
00608
00609 todo->setDtDue( nextDate );
00610 todo->setCompleted( false );
00611 todo->setRevision( todo->revision() + 1 );
00612
00613 return true;
00614 }
00615 }
00616
00617 return false;
00618 }
00619