00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00032 #include "todo.h"
00033 #include "incidenceformatter.h"
00034
00035 #include <kglobal.h>
00036 #include <klocale.h>
00037 #include <kdebug.h>
00038 #include <ksystemtimezone.h>
00039
00040 using namespace KCal;
00041
00046
00047 class KCal::Todo::Private
00048 {
00049 public:
00050 Private()
00051 : mPercentComplete( 0 ),
00052 mHasDueDate( false ),
00053 mHasStartDate( false ),
00054 mHasCompletedDate( false )
00055 {}
00056 Private( const KCal::Todo::Private &other )
00057 { init( other ); }
00058
00059 void init( const KCal::Todo::Private &other );
00060
00061 KDateTime mDtDue;
00062
00063 KDateTime mDtRecurrence;
00064 KDateTime mCompleted;
00065 int mPercentComplete;
00066 bool mHasDueDate;
00067 bool mHasStartDate;
00068 bool mHasCompletedDate;
00069
00073 bool recurTodo( Todo *todo );
00074 };
00075
00076 void KCal::Todo::Private::init( const KCal::Todo::Private &other )
00077 {
00078 mDtDue = other.mDtDue;
00079 mDtRecurrence = other.mDtRecurrence;
00080 mCompleted = other.mCompleted;
00081 mPercentComplete = other.mPercentComplete;
00082 mHasDueDate = other.mHasDueDate;
00083 mHasStartDate = other.mHasStartDate;
00084 mHasCompletedDate = other.mHasCompletedDate;
00085 }
00086
00087
00088
00089 Todo::Todo()
00090 : d( new KCal::Todo::Private )
00091 {
00092 }
00093
00094 Todo::Todo( const Todo &other )
00095 : Incidence( other ),
00096 d( new KCal::Todo::Private( *other.d ) )
00097 {
00098 }
00099
00100 Todo::~Todo()
00101 {
00102 delete d;
00103 }
00104
00105 Todo *Todo::clone()
00106 {
00107 return new Todo( *this );
00108 }
00109
00110 Todo &Todo::operator=( const Todo &other )
00111 {
00112
00113 if ( &other == this ) {
00114 return *this;
00115 }
00116
00117 Incidence::operator=( other );
00118 d->init( *other.d );
00119 return *this;
00120 }
00121
00122 bool Todo::operator==( const Todo &todo ) const
00123 {
00124 return
00125 Incidence::operator==( todo ) &&
00126 dtDue() == todo.dtDue() &&
00127 hasDueDate() == todo.hasDueDate() &&
00128 hasStartDate() == todo.hasStartDate() &&
00129 completed() == todo.completed() &&
00130 hasCompletedDate() == todo.hasCompletedDate() &&
00131 percentComplete() == todo.percentComplete();
00132 }
00133
00134 QByteArray Todo::type() const
00135 {
00136 return "Todo";
00137 }
00138
00139 void Todo::setDtDue( const KDateTime &dtDue, bool first )
00140 {
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150 if ( recurs() && !first ) {
00151 d->mDtRecurrence = dtDue;
00152 } else {
00153 d->mDtDue = dtDue;
00154
00155 recurrence()->setStartDateTime( dtDue );
00156 recurrence()->setAllDay( allDay() );
00157 }
00158
00159 if ( recurs() && dtDue < recurrence()->startDateTime() ) {
00160 setDtStart( dtDue );
00161 }
00162
00163
00164
00165
00166
00167 updated();
00168 }
00169
00170 KDateTime Todo::dtDue( bool first ) const
00171 {
00172 if ( !hasDueDate() ) {
00173 return KDateTime();
00174 }
00175 if ( recurs() && !first && d->mDtRecurrence.isValid() ) {
00176 return d->mDtRecurrence;
00177 }
00178
00179 return d->mDtDue;
00180 }
00181
00182 QString Todo::dtDueTimeStr( bool shortfmt, const KDateTime::Spec &spec ) const
00183 {
00184 if ( spec.isValid() ) {
00185
00186 QString timeZone;
00187 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00188 timeZone = ' ' + spec.timeZone().name();
00189 }
00190
00191 return KGlobal::locale()->formatTime(
00192 dtDue( !recurs() ).toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
00193 } else {
00194 return KGlobal::locale()->formatTime(
00195 dtDue( !recurs() ).time(), !shortfmt );
00196 }
00197 }
00198
00199 QString Todo::dtDueDateStr( bool shortfmt, const KDateTime::Spec &spec ) const
00200 {
00201 if ( spec.isValid() ) {
00202
00203 QString timeZone;
00204 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00205 timeZone = ' ' + spec.timeZone().name();
00206 }
00207
00208 return KGlobal::locale()->formatDate(
00209 dtDue( !recurs() ).toTimeSpec( spec ).date(),
00210 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00211 } else {
00212 return KGlobal::locale()->formatDate(
00213 dtDue( !recurs() ).date(),
00214 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00215 }
00216 }
00217
00218 QString Todo::dtDueStr( bool shortfmt, const KDateTime::Spec &spec ) const
00219 {
00220 if ( allDay() ) {
00221 return IncidenceFormatter::dateToString( dtDue(), shortfmt, spec );
00222 }
00223
00224 if ( spec.isValid() ) {
00225
00226 QString timeZone;
00227 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00228 timeZone = ' ' + spec.timeZone().name();
00229 }
00230
00231 return KGlobal::locale()->formatDateTime(
00232 dtDue( !recurs() ).toTimeSpec( spec ).dateTime(),
00233 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00234 } else {
00235 return KGlobal::locale()->formatDateTime(
00236 dtDue( !recurs() ).dateTime(),
00237 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00238 }
00239 }
00240
00241 bool Todo::hasDueDate() const
00242 {
00243 return d->mHasDueDate;
00244 }
00245
00246 void Todo::setHasDueDate( bool f )
00247 {
00248 if ( mReadOnly ) {
00249 return;
00250 }
00251 d->mHasDueDate = f;
00252 updated();
00253 }
00254
00255 bool Todo::hasStartDate() const
00256 {
00257 return d->mHasStartDate;
00258 }
00259
00260 void Todo::setHasStartDate( bool f )
00261 {
00262 if ( mReadOnly ) {
00263 return;
00264 }
00265
00266 if ( recurs() && !f ) {
00267 if ( !comments().filter( "NoStartDate" ).count() ) {
00268 addComment( "NoStartDate" );
00269 }
00270 } else {
00271 QString s( "NoStartDate" );
00272 removeComment( s );
00273 }
00274 d->mHasStartDate = f;
00275 updated();
00276 }
00277
00278 KDateTime Todo::dtStart() const
00279 {
00280 return dtStart( false );
00281 }
00282
00283 KDateTime Todo::dtStart( bool first ) const
00284 {
00285 if ( !hasStartDate() ) {
00286 return KDateTime();
00287 }
00288 if ( recurs() && !first ) {
00289 return d->mDtRecurrence.addDays( dtDue( true ).daysTo( IncidenceBase::dtStart() ) );
00290 } else {
00291 return IncidenceBase::dtStart();
00292 }
00293 }
00294
00295 void Todo::setDtStart( const KDateTime &dtStart )
00296 {
00297
00298 if ( recurs() ) {
00299 recurrence()->setStartDateTime( d->mDtDue );
00300 recurrence()->setAllDay( allDay() );
00301 }
00302 IncidenceBase::setDtStart( dtStart );
00303 }
00304
00305 QString Todo::dtStartTimeStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const
00306 {
00307 if ( spec.isValid() ) {
00308
00309 QString timeZone;
00310 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00311 timeZone = ' ' + spec.timeZone().name();
00312 }
00313
00314 return KGlobal::locale()->formatTime(
00315 dtStart( first ).toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
00316 } else {
00317 return KGlobal::locale()->formatTime(
00318 dtStart( first ).time(), !shortfmt );
00319 }
00320 }
00321
00322 QString Todo::dtStartTimeStr( bool shortfmt, const KDateTime::Spec &spec ) const
00323 {
00324 return IncidenceFormatter::timeToString( dtStart(), shortfmt, spec );
00325 }
00326
00327 QString Todo::dtStartDateStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const
00328 {
00329 if ( spec.isValid() ) {
00330
00331 QString timeZone;
00332 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00333 timeZone = ' ' + spec.timeZone().name();
00334 }
00335
00336 return KGlobal::locale()->formatDate(
00337 dtStart( first ).toTimeSpec( spec ).date(),
00338 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00339 } else {
00340 return KGlobal::locale()->formatDate(
00341 dtStart( first ).date(),
00342 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00343 }
00344 }
00345
00346 QString Todo::dtStartDateStr( bool shortfmt, const KDateTime::Spec &spec ) const
00347 {
00348 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec );
00349 }
00350
00351 QString Todo::dtStartStr( bool shortfmt, bool first, const KDateTime::Spec &spec ) const
00352 {
00353 if ( allDay() ) {
00354 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec );
00355 }
00356
00357 if ( spec.isValid() ) {
00358
00359 QString timeZone;
00360 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00361 timeZone = ' ' + spec.timeZone().name();
00362 }
00363
00364 return KGlobal::locale()->formatDateTime(
00365 dtStart( first ).toTimeSpec( spec ).dateTime(),
00366 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00367 } else {
00368 return KGlobal::locale()->formatDateTime(
00369 dtStart( first ).dateTime(),
00370 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00371 }
00372 }
00373
00374 QString Todo::dtStartStr( bool shortfmt, const KDateTime::Spec &spec ) const
00375 {
00376 if ( allDay() ) {
00377 return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec );
00378 }
00379
00380 if ( spec.isValid() ) {
00381
00382 QString timeZone;
00383 if ( spec.timeZone() != KSystemTimeZones::local() ) {
00384 timeZone = ' ' + spec.timeZone().name();
00385 }
00386
00387 return KGlobal::locale()->formatDateTime(
00388 dtStart().toTimeSpec( spec ).dateTime(),
00389 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
00390 } else {
00391 return KGlobal::locale()->formatDateTime(
00392 dtStart().dateTime(),
00393 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00394 }
00395 }
00396
00397 bool Todo::isCompleted() const
00398 {
00399 if ( d->mPercentComplete == 100 ) {
00400 return true;
00401 } else {
00402 return false;
00403 }
00404 }
00405
00406 void Todo::setCompleted( bool completed )
00407 {
00408 if ( completed ) {
00409 d->mPercentComplete = 100;
00410 } else {
00411 d->mPercentComplete = 0;
00412 d->mHasCompletedDate = false;
00413 d->mCompleted = KDateTime();
00414 }
00415 updated();
00416 }
00417
00418 KDateTime Todo::completed() const
00419 {
00420 if ( hasCompletedDate() ) {
00421 return d->mCompleted;
00422 } else {
00423 return KDateTime();
00424 }
00425 }
00426
00427 QString Todo::completedStr( bool shortfmt ) const
00428 {
00429 return
00430 KGlobal::locale()->formatDateTime( d->mCompleted.dateTime(),
00431 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
00432 }
00433
00434 void Todo::setCompleted( const KDateTime &completed )
00435 {
00436 if ( !d->recurTodo( this ) ) {
00437 d->mHasCompletedDate = true;
00438 d->mPercentComplete = 100;
00439 d->mCompleted = completed.toUtc();
00440 }
00441 updated();
00442 }
00443
00444 bool Todo::hasCompletedDate() const
00445 {
00446 return d->mHasCompletedDate;
00447 }
00448
00449 int Todo::percentComplete() const
00450 {
00451 return d->mPercentComplete;
00452 }
00453
00454 void Todo::setPercentComplete( int percent )
00455 {
00456
00457 d->mPercentComplete = percent;
00458 if ( percent != 100 ) {
00459 d->mHasCompletedDate = false;
00460 }
00461 updated();
00462 }
00463
00464 void Todo::shiftTimes( const KDateTime::Spec &oldSpec,
00465 const KDateTime::Spec &newSpec )
00466 {
00467 Incidence::shiftTimes( oldSpec, newSpec );
00468 d->mDtDue = d->mDtDue.toTimeSpec( oldSpec );
00469 d->mDtDue.setTimeSpec( newSpec );
00470 if ( recurs() ) {
00471 d->mDtRecurrence = d->mDtRecurrence.toTimeSpec( oldSpec );
00472 d->mDtRecurrence.setTimeSpec( newSpec );
00473 }
00474 if ( d->mHasCompletedDate ) {
00475 d->mCompleted = d->mCompleted.toTimeSpec( oldSpec );
00476 d->mCompleted.setTimeSpec( newSpec );
00477 }
00478 }
00479
00480 void Todo::setDtRecurrence( const KDateTime &dt )
00481 {
00482 d->mDtRecurrence = dt;
00483 }
00484
00485 KDateTime Todo::dtRecurrence() const
00486 {
00487 return d->mDtRecurrence.isValid() ? d->mDtRecurrence : d->mDtDue;
00488 }
00489
00490 bool Todo::recursOn( const QDate &date, const KDateTime::Spec &timeSpec ) const
00491 {
00492 QDate today = QDate::currentDate();
00493 return
00494 Incidence::recursOn( date, timeSpec ) &&
00495 !( date < today && d->mDtRecurrence.date() < today &&
00496 d->mDtRecurrence > recurrence()->startDateTime() );
00497 }
00498
00499 bool Todo::isOverdue() const
00500 {
00501 if ( !dtDue().isValid() ) {
00502 return false;
00503 }
00504
00505 bool inPast = allDay() ?
00506 dtDue().date() < QDate::currentDate() :
00507 dtDue() < KDateTime::currentUtcDateTime();
00508 return inPast && !isCompleted();
00509 }
00510
00511 KDateTime Todo::endDateRecurrenceBase() const
00512 {
00513 return dtDue();
00514 }
00515
00516
00517 bool Todo::Private::recurTodo( Todo *todo )
00518 {
00519 if ( todo->recurs() ) {
00520 Recurrence *r = todo->recurrence();
00521 KDateTime endDateTime = r->endDateTime();
00522 KDateTime nextDate = r->getNextDateTime( todo->dtDue() );
00523
00524 if ( ( r->duration() == -1 ||
00525 ( nextDate.isValid() && endDateTime.isValid() &&
00526 nextDate <= endDateTime ) ) ) {
00527
00528 while ( !todo->recursAt( nextDate ) ||
00529 nextDate <= KDateTime::currentUtcDateTime() ) {
00530
00531 if ( !nextDate.isValid() ||
00532 ( nextDate > endDateTime && r->duration() != -1 ) ) {
00533
00534 return false;
00535 }
00536
00537 nextDate = r->getNextDateTime( nextDate );
00538 }
00539
00540 todo->setDtDue( nextDate );
00541 todo->setCompleted( false );
00542 todo->setRevision( todo->revision() + 1 );
00543
00544 return true;
00545 }
00546 }
00547
00548 return false;
00549 }
00550