KCalCore Library
todo.cpp
Go to the documentation of this file.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 "visitor.h"
00036
00037 using namespace KCalCore;
00038
00043
00044 class KCalCore::Todo::Private
00045 {
00046 public:
00047 Private()
00048 : mPercentComplete( 0 ),
00049 mHasDueDate( false ),
00050 mHasStartDate( false ),
00051 mHasCompletedDate( false )
00052 {}
00053 Private( const KCalCore::Todo::Private &other )
00054 { init( other ); }
00055
00056 void init( const KCalCore::Todo::Private &other );
00057
00058 KDateTime mDtDue;
00059
00060 KDateTime mDtRecurrence;
00061 KDateTime mCompleted;
00062 int mPercentComplete;
00063 bool mHasDueDate;
00064 bool mHasStartDate;
00065 bool mHasCompletedDate;
00066
00070 bool recurTodo( Todo *todo );
00071 };
00072
00073 void KCalCore::Todo::Private::init( const KCalCore::Todo::Private &other )
00074 {
00075 mDtDue = other.mDtDue;
00076 mDtRecurrence = other.mDtRecurrence;
00077 mCompleted = other.mCompleted;
00078 mPercentComplete = other.mPercentComplete;
00079 mHasDueDate = other.mHasDueDate;
00080 mHasStartDate = other.mHasStartDate;
00081 mHasCompletedDate = other.mHasCompletedDate;
00082 }
00083
00084
00085
00086 Todo::Todo()
00087 : d( new KCalCore::Todo::Private )
00088 {
00089 }
00090
00091 Todo::Todo( const Todo &other )
00092 : Incidence( other ),
00093 d( new KCalCore::Todo::Private( *other.d ) )
00094 {
00095 }
00096
00097 Todo::~Todo()
00098 {
00099 delete d;
00100 }
00101
00102 Todo *Todo::clone() const
00103 {
00104 return new Todo( *this );
00105 }
00106
00107 IncidenceBase &Todo::assign( const IncidenceBase &other )
00108 {
00109 if ( &other != this ) {
00110 Incidence::assign( other );
00111 const Todo *t = static_cast<const Todo*>( &other );
00112 d->init( *( t->d ) );
00113 }
00114 return *this;
00115 }
00116
00117 bool Todo::equals( const IncidenceBase &todo ) const
00118 {
00119 if ( !Incidence::equals( todo ) ) {
00120 return false;
00121 } else {
00122
00123 const Todo *t = static_cast<const Todo*>( &todo );
00124 return ( ( dtDue() == t->dtDue() ) ||
00125 ( !dtDue().isValid() && !t->dtDue().isValid() ) ) &&
00126 hasDueDate() == t->hasDueDate() &&
00127 hasStartDate() == t->hasStartDate() &&
00128 ( ( completed() == t->completed() ) ||
00129 ( !completed().isValid() && !t->completed().isValid() ) ) &&
00130 hasCompletedDate() == t->hasCompletedDate() &&
00131 percentComplete() == t->percentComplete();
00132 }
00133 }
00134
00135 Incidence::IncidenceType Todo::type() const
00136 {
00137 return TypeTodo;
00138 }
00139
00140 QByteArray Todo::typeStr() const
00141 {
00142 return "Todo";
00143 }
00144 void Todo::setDtDue( const KDateTime &dtDue, bool first )
00145 {
00146 startUpdates();
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 d->mHasDueDate = dtDue.isValid();
00158
00159 if ( recurs() && !first ) {
00160 d->mDtRecurrence = dtDue;
00161 } else {
00162 d->mDtDue = dtDue;
00163
00164 recurrence()->setStartDateTime( dtDue );
00165 recurrence()->setAllDay( allDay() );
00166 }
00167
00168 if ( recurs() && dtDue < recurrence()->startDateTime() ) {
00169 setDtStart( dtDue );
00170 }
00171
00172
00173
00174
00175 setFieldDirty( FieldDtDue );
00176 endUpdates();
00177 }
00178
00179 KDateTime Todo::dtDue( bool first ) const
00180 {
00181 if ( !hasDueDate() ) {
00182 return KDateTime();
00183 }
00184 if ( recurs() && !first && d->mDtRecurrence.isValid() ) {
00185 return d->mDtRecurrence;
00186 }
00187
00188 return d->mDtDue;
00189 }
00190
00191 bool Todo::hasDueDate() const
00192 {
00193 return d->mHasDueDate;
00194 }
00195
00196 void Todo::setHasDueDate( bool f )
00197 {
00198 if ( mReadOnly ) {
00199 return;
00200 }
00201 update();
00202 d->mHasDueDate = f;
00203 setFieldDirty( FieldDtDue );
00204 updated();
00205 }
00206
00207 bool Todo::hasStartDate() const
00208 {
00209 return d->mHasStartDate;
00210 }
00211
00212 void Todo::setHasStartDate( bool f )
00213 {
00214 if ( mReadOnly ) {
00215 return;
00216 }
00217
00218 update();
00219 if ( recurs() && !f ) {
00220 if ( !comments().filter( "NoStartDate" ).count() ) {
00221 addComment( "NoStartDate" );
00222 }
00223 } else {
00224 QString s( "NoStartDate" );
00225 removeComment( s );
00226 }
00227 d->mHasStartDate = f;
00228 setFieldDirty( FieldDtStart );
00229 updated();
00230 }
00231
00232 KDateTime Todo::dtStart() const
00233 {
00234 return dtStart( false );
00235 }
00236
00237 KDateTime Todo::dtStart( bool first ) const
00238 {
00239 if ( !hasStartDate() ) {
00240 return KDateTime();
00241 }
00242 if ( recurs() && !first ) {
00243 KDateTime dt = d->mDtRecurrence.addDays( dtDue( true ).daysTo( IncidenceBase::dtStart() ) );
00244 dt.setTime( IncidenceBase::dtStart().time() );
00245 return dt;
00246 } else {
00247 return IncidenceBase::dtStart();
00248 }
00249 }
00250
00251 void Todo::setDtStart( const KDateTime &dtStart )
00252 {
00253
00254
00255 d->mHasStartDate = dtStart.isValid();
00256
00257 if ( recurs() ) {
00258 recurrence()->setStartDateTime( d->mDtDue );
00259 recurrence()->setAllDay( allDay() );
00260 }
00261 IncidenceBase::setDtStart( dtStart );
00262 }
00263
00264 bool Todo::isCompleted() const
00265 {
00266 return d->mPercentComplete == 100;
00267 }
00268
00269 void Todo::setCompleted( bool completed )
00270 {
00271 update();
00272 if ( completed ) {
00273 d->mPercentComplete = 100;
00274 } else {
00275 d->mPercentComplete = 0;
00276 d->mHasCompletedDate = false;
00277 d->mCompleted = KDateTime();
00278 }
00279 setFieldDirty( FieldCompleted );
00280 updated();
00281 }
00282
00283 KDateTime Todo::completed() const
00284 {
00285 if ( hasCompletedDate() ) {
00286 return d->mCompleted;
00287 } else {
00288 return KDateTime();
00289 }
00290 }
00291
00292 void Todo::setCompleted( const KDateTime &completed )
00293 {
00294 update();
00295 if ( !d->recurTodo( this ) ) {
00296 d->mHasCompletedDate = true;
00297 d->mPercentComplete = 100;
00298 d->mCompleted = completed.toUtc();
00299 setFieldDirty( FieldCompleted );
00300 }
00301 updated();
00302 }
00303
00304 bool Todo::hasCompletedDate() const
00305 {
00306 return d->mHasCompletedDate;
00307 }
00308
00309 int Todo::percentComplete() const
00310 {
00311 return d->mPercentComplete;
00312 }
00313
00314 void Todo::setPercentComplete( int percent )
00315 {
00316 if ( percent > 100 ) {
00317 percent = 100;
00318 } else if ( percent < 0 ) {
00319 percent = 0;
00320 }
00321
00322 update();
00323 d->mPercentComplete = percent;
00324 if ( percent != 100 ) {
00325 d->mHasCompletedDate = false;
00326 }
00327 setFieldDirty( FieldPercentComplete );
00328 updated();
00329 }
00330
00331 bool Todo::isInProgress( bool first ) const
00332 {
00333 if ( isOverdue() ) {
00334 return false;
00335 }
00336
00337 if ( d->mPercentComplete > 0 ) {
00338 return true;
00339 }
00340
00341 if ( d->mHasStartDate && d->mHasDueDate ) {
00342 if ( allDay() ) {
00343 QDate currDate = QDate::currentDate();
00344 if ( dtStart( first ).date() <= currDate && currDate < dtDue( first ).date() ) {
00345 return true;
00346 }
00347 } else {
00348 KDateTime currDate = KDateTime::currentUtcDateTime();
00349 if ( dtStart( first ) <= currDate && currDate < dtDue( first ) ) {
00350 return true;
00351 }
00352 }
00353 }
00354
00355 return false;
00356 }
00357
00358 bool Todo::isOpenEnded() const
00359 {
00360 if ( !d->mHasDueDate && !isCompleted() ) {
00361 return true;
00362 }
00363 return false;
00364
00365 }
00366
00367 bool Todo::isNotStarted( bool first ) const
00368 {
00369 if ( d->mPercentComplete > 0 ) {
00370 return false;
00371 }
00372
00373 if ( !d->mHasStartDate ) {
00374 return false;
00375 }
00376
00377 if ( allDay() ) {
00378 if ( dtStart( first ).date() >= QDate::currentDate() ) {
00379 return false;
00380 }
00381 } else {
00382 if ( dtStart( first ) >= KDateTime::currentUtcDateTime() ) {
00383 return false;
00384 }
00385 }
00386 return true;
00387 }
00388
00389 void Todo::shiftTimes( const KDateTime::Spec &oldSpec,
00390 const KDateTime::Spec &newSpec )
00391 {
00392 Incidence::shiftTimes( oldSpec, newSpec );
00393 d->mDtDue = d->mDtDue.toTimeSpec( oldSpec );
00394 d->mDtDue.setTimeSpec( newSpec );
00395 if ( recurs() ) {
00396 d->mDtRecurrence = d->mDtRecurrence.toTimeSpec( oldSpec );
00397 d->mDtRecurrence.setTimeSpec( newSpec );
00398 }
00399 if ( d->mHasCompletedDate ) {
00400 d->mCompleted = d->mCompleted.toTimeSpec( oldSpec );
00401 d->mCompleted.setTimeSpec( newSpec );
00402 }
00403 }
00404
00405 void Todo::setDtRecurrence( const KDateTime &dt )
00406 {
00407 d->mDtRecurrence = dt;
00408 setFieldDirty( FieldRecurrence );
00409 }
00410
00411 KDateTime Todo::dtRecurrence() const
00412 {
00413 return d->mDtRecurrence.isValid() ? d->mDtRecurrence : d->mDtDue;
00414 }
00415
00416 bool Todo::recursOn( const QDate &date, const KDateTime::Spec &timeSpec ) const
00417 {
00418 QDate today = QDate::currentDate();
00419 return
00420 Incidence::recursOn( date, timeSpec ) &&
00421 !( date < today && d->mDtRecurrence.date() < today &&
00422 d->mDtRecurrence > recurrence()->startDateTime() );
00423 }
00424
00425 bool Todo::isOverdue() const
00426 {
00427 if ( !dtDue().isValid() ) {
00428 return false;
00429 }
00430
00431 const bool inPast = allDay() ?
00432 dtDue().date() < QDate::currentDate() :
00433 dtDue() < KDateTime::currentUtcDateTime();
00434 return inPast && !isCompleted();
00435 }
00436
00437 void Todo::setAllDay( bool allday )
00438 {
00439 if ( allday != allDay() && !mReadOnly ) {
00440 if ( hasDueDate() && dtDue().isValid() ) {
00441 setFieldDirty( FieldDtDue );
00442 }
00443 Incidence::setAllDay( allday );
00444 }
00445 }
00446
00447
00448 bool Todo::Private::recurTodo( Todo *todo )
00449 {
00450 if ( todo && todo->recurs() ) {
00451 Recurrence *r = todo->recurrence();
00452 const KDateTime recurrenceEndDateTime = r->endDateTime();
00453 KDateTime nextOccurrenceDateTime = r->getNextDateTime( todo->dtDue() );
00454
00455 if ( ( r->duration() == -1 ||
00456 ( nextOccurrenceDateTime.isValid() && recurrenceEndDateTime.isValid() &&
00457 nextOccurrenceDateTime <= recurrenceEndDateTime ) ) ) {
00458
00459 const KDateTime rightNow = KDateTime::currentUtcDateTime().toTimeSpec( nextOccurrenceDateTime.timeSpec() );
00460 const bool isDateOnly = todo->allDay();
00461
00462
00463
00464
00465
00466
00467
00468 while ( !todo->recursAt( nextOccurrenceDateTime ) ||
00469 ( !isDateOnly && nextOccurrenceDateTime <= rightNow ) ||
00470 ( isDateOnly && nextOccurrenceDateTime.date() < rightNow.date() ) ) {
00471
00472 if ( !nextOccurrenceDateTime.isValid() ||
00473 ( nextOccurrenceDateTime > recurrenceEndDateTime && r->duration() != -1 ) ) {
00474 return false;
00475 }
00476 nextOccurrenceDateTime = r->getNextDateTime( nextOccurrenceDateTime );
00477 }
00478
00479 todo->setDtDue( nextOccurrenceDateTime );
00480 todo->setCompleted( false );
00481 todo->setRevision( todo->revision() + 1 );
00482
00483 return true;
00484 }
00485 }
00486
00487 return false;
00488 }
00489
00490
00491 bool Todo::accept( Visitor &v, IncidenceBase::Ptr incidence )
00492 {
00493 return v.visit( incidence.staticCast<Todo>() );
00494 }
00495
00496 KDateTime Todo::dateTime( DateTimeRole role ) const
00497 {
00498 switch ( role ) {
00499 case RoleAlarmStartOffset:
00500 return dtStart();
00501 case RoleAlarmEndOffset:
00502 return dtDue();
00503 case RoleSort:
00504
00505
00506 return hasDueDate() ? dtDue() : dtStart();
00507 case RoleCalendarHashing:
00508 return dtDue();
00509 case RoleStartTimeZone:
00510 return dtStart();
00511 case RoleEndTimeZone:
00512 return dtDue();
00513 case RoleEndRecurrenceBase:
00514 return dtDue();
00515 case RoleDisplayEnd:
00516 return dtDue();
00517 case RoleAlarm:
00518 if ( alarms().isEmpty() ) {
00519 return KDateTime();
00520 } else {
00521 Alarm::Ptr alarm = alarms().first();
00522 if ( alarm->hasStartOffset() && hasStartDate() ) {
00523 return dtStart();
00524 } else if ( alarm->hasEndOffset() && hasDueDate() ) {
00525 return dtDue();
00526 } else {
00527
00528 return KDateTime();
00529 }
00530 }
00531 case RoleRecurrenceStart:
00532 return dtDue();
00533 break;
00534 case RoleEnd:
00535
00536 default:
00537 return KDateTime();
00538 }
00539 }
00540
00541 void Todo::setDateTime( const KDateTime &dateTime, DateTimeRole role )
00542 {
00543 Q_UNUSED( dateTime );
00544 Q_UNUSED( role );
00545 }
00546
00547 void Todo::virtual_hook( int id, void *data )
00548 {
00549 Q_UNUSED( id );
00550 Q_UNUSED( data );
00551 Q_ASSERT( false );
00552 }
00553
00554 QLatin1String Todo::mimeType() const
00555 {
00556 return Todo::todoMimeType();
00557 }
00558
00559 QLatin1String Todo::todoMimeType()
00560 {
00561 return QLatin1String( "application/x-vnd.akonadi.calendar.todo" );
00562 }
00563
00564 QLatin1String Todo::iconName( const KDateTime &recurrenceId ) const
00565 {
00566 KDateTime occurrenceDT = recurrenceId;
00567
00568 if ( recurs() && occurrenceDT.isDateOnly() ) {
00569 occurrenceDT.setTime( QTime( 0, 0 ) );
00570 }
00571
00572 const bool usesCompletedTaskPixmap = isCompleted() ||
00573 ( recurs() && occurrenceDT.isValid() &&
00574 occurrenceDT < dtDue( false ) );
00575
00576 if ( usesCompletedTaskPixmap ) {
00577 return QLatin1String( "task-complete" );
00578 } else {
00579 return QLatin1String( "view-calendar-tasks" );
00580 }
00581 }