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 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
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
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;
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
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