KCalCore Library
todo.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the kcalcore 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 "visitor.h" 00036 00037 using namespace KCalCore; 00038 00043 //@cond PRIVATE 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; // to-do due date (if there is one) 00059 // ALSO the first occurrence of a recurring to-do 00060 KDateTime mDtRecurrence; // next occurrence (for recurring to-dos) 00061 KDateTime mCompleted; // to-do completion date (if it has been completed) 00062 int mPercentComplete; // to-do percent complete [0,100] 00063 bool mHasDueDate; // true if the to-do has a due date 00064 bool mHasStartDate; // true if the to-do has a starting date 00065 bool mHasCompletedDate; // true if the to-do has a completion date 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 //@endcond 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 // If they weren't the same type IncidenceBase::equals would had returned false already 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 //int diffsecs = d->mDtDue.secsTo(dtDue); 00149 00150 /*if (mReadOnly) return; 00151 const Alarm::List& alarms = alarms(); 00152 for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) { 00153 if (alarm->enabled()) { 00154 alarm->setTime(alarm->time().addSecs(diffsecs)); 00155 } 00156 }*/ 00157 d->mHasDueDate = dtDue.isValid(); 00158 00159 if ( recurs() && !first ) { 00160 d->mDtRecurrence = dtDue; 00161 } else { 00162 d->mDtDue = dtDue; 00163 // TODO: This doesn't seem right... 00164 recurrence()->setStartDateTime( dtDue ); 00165 recurrence()->setAllDay( allDay() ); 00166 } 00167 00168 if ( recurs() && dtDue < recurrence()->startDateTime() ) { 00169 setDtStart( dtDue ); 00170 } 00171 00172 /*const Alarm::List& alarms = alarms(); 00173 for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) 00174 alarm->setAlarmStart(d->mDtDue);*/ 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" ); //TODO: --> custom flag? 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 // TODO: This doesn't seem right (rfc 2445/6 says, recurrence is calculated from the dtstart...) 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; // if it's never due, it can't be overdue 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 //@cond PRIVATE 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 // We convert to the same timeSpec so we get the correct .date() 00459 const KDateTime rightNow = 00460 KDateTime::currentUtcDateTime().toTimeSpec( nextOccurrenceDateTime.timeSpec() ); 00461 const bool isDateOnly = todo->allDay(); 00462 00463 /* Now we search for the occurrence that's _after_ the currentUtcDateTime, or 00464 * if it's dateOnly, the occurrrence that's _during or after today_. 00465 * The reason we use "<" for date only, but "<=" for ocurrences with time is that 00466 * if it's date only, the user can still complete that ocurrence today, so that's 00467 * the current ocurrence that needs completing. 00468 */ 00469 while ( !todo->recursAt( nextOccurrenceDateTime ) || 00470 ( !isDateOnly && nextOccurrenceDateTime <= rightNow ) || 00471 ( isDateOnly && nextOccurrenceDateTime.date() < rightNow.date() ) ) { 00472 00473 if ( !nextOccurrenceDateTime.isValid() || 00474 ( nextOccurrenceDateTime > recurrenceEndDateTime && r->duration() != -1 ) ) { 00475 return false; 00476 } 00477 nextOccurrenceDateTime = r->getNextDateTime( nextOccurrenceDateTime ); 00478 } 00479 00480 todo->setDtDue( nextOccurrenceDateTime ); 00481 todo->setCompleted( false ); 00482 todo->setRevision( todo->revision() + 1 ); 00483 00484 return true; 00485 } 00486 } 00487 00488 return false; 00489 } 00490 //@endcond 00491 00492 bool Todo::accept( Visitor &v, IncidenceBase::Ptr incidence ) 00493 { 00494 return v.visit( incidence.staticCast<Todo>() ); 00495 } 00496 00497 KDateTime Todo::dateTime( DateTimeRole role ) const 00498 { 00499 switch ( role ) { 00500 case RoleAlarmStartOffset: 00501 return dtStart(); 00502 case RoleAlarmEndOffset: 00503 return dtDue(); 00504 case RoleSort: 00505 // Sorting to-dos first compares dtDue, then dtStart if 00506 // dtDue doesn't exist 00507 return hasDueDate() ? dtDue() : dtStart(); 00508 case RoleCalendarHashing: 00509 return dtDue(); 00510 case RoleStartTimeZone: 00511 return dtStart(); 00512 case RoleEndTimeZone: 00513 return dtDue(); 00514 case RoleEndRecurrenceBase: 00515 return dtDue(); 00516 case RoleDisplayStart: 00517 case RoleDisplayEnd: 00518 return dtDue(); 00519 case RoleAlarm: 00520 if ( alarms().isEmpty() ) { 00521 return KDateTime(); 00522 } else { 00523 Alarm::Ptr alarm = alarms().first(); 00524 if ( alarm->hasStartOffset() && hasStartDate() ) { 00525 return dtStart(); 00526 } else if ( alarm->hasEndOffset() && hasDueDate() ) { 00527 return dtDue(); 00528 } else { 00529 // The application shouldn't add alarms on to-dos without dates. 00530 return KDateTime(); 00531 } 00532 } 00533 case RoleRecurrenceStart: 00534 return dtDue(); 00535 break; 00536 case RoleEnd: 00537 // todos don't have dtEnd 00538 default: 00539 return KDateTime(); 00540 } 00541 } 00542 00543 void Todo::setDateTime( const KDateTime &dateTime, DateTimeRole role ) 00544 { 00545 Q_UNUSED( dateTime ); 00546 Q_UNUSED( role ); 00547 } 00548 00549 void Todo::virtual_hook( int id, void *data ) 00550 { 00551 Q_UNUSED( id ); 00552 Q_UNUSED( data ); 00553 Q_ASSERT( false ); 00554 } 00555 00556 QLatin1String Todo::mimeType() const 00557 { 00558 return Todo::todoMimeType(); 00559 } 00560 00561 QLatin1String Todo::todoMimeType() 00562 { 00563 return QLatin1String( "application/x-vnd.akonadi.calendar.todo" ); 00564 } 00565 00566 QLatin1String Todo::iconName( const KDateTime &recurrenceId ) const 00567 { 00568 KDateTime occurrenceDT = recurrenceId; 00569 00570 if ( recurs() && occurrenceDT.isDateOnly() ) { 00571 occurrenceDT.setTime( QTime( 0, 0 ) ); 00572 } 00573 00574 const bool usesCompletedTaskPixmap = isCompleted() || 00575 ( recurs() && occurrenceDT.isValid() && 00576 occurrenceDT < dtDue( false ) ); 00577 00578 if ( usesCompletedTaskPixmap ) { 00579 return QLatin1String( "task-complete" ); 00580 } else { 00581 return QLatin1String( "view-calendar-tasks" ); 00582 } 00583 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:07:49 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:07:49 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.