00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00035 #include "incidence.h"
00036 #include "calformat.h"
00037
00038 #include <kglobal.h>
00039 #include <klocale.h>
00040 #include <kdebug.h>
00041
00042 #include <QtCore/QList>
00043
00044 using namespace KCal;
00045
00050
00051 class KCal::Incidence::Private
00052 {
00053 public:
00054 Private()
00055 : mRecurrence( 0 ),
00056 mStatus( StatusNone ),
00057 mSecrecy( SecrecyPublic ),
00058 mPriority( 0 ),
00059 mRelatedTo( 0 )
00060 {}
00061
00062 Private( const Private &p )
00063 : mCreated( p.mCreated ),
00064 mRevision( p.mRevision ),
00065 mDescription( p.mDescription ),
00066 mDescriptionIsRich( p.mDescriptionIsRich ),
00067 mSummary( p.mSummary ),
00068 mSummaryIsRich( p.mSummaryIsRich ),
00069 mLocation( p.mLocation ),
00070 mLocationIsRich( p.mLocationIsRich ),
00071 mCategories( p.mCategories ),
00072 mResources( p.mResources ),
00073 mStatus( p.mStatus ),
00074 mStatusString( p.mStatusString ),
00075 mSecrecy( p.mSecrecy ),
00076 mPriority( p.mPriority ),
00077 mSchedulingID( p.mSchedulingID ),
00078 mRelatedTo( 0 ),
00079 mRelatedToUid( p.mRelatedToUid )
00080
00081
00082
00083 {}
00084
00085 KDateTime mCreated;
00086 int mRevision;
00087
00088 QString mDescription;
00089 bool mDescriptionIsRich;
00090 QString mSummary;
00091 bool mSummaryIsRich;
00092 QString mLocation;
00093 bool mLocationIsRich;
00094 QStringList mCategories;
00095 mutable Recurrence *mRecurrence;
00096 Attachment::List mAttachments;
00097 Alarm::List mAlarms;
00098 QStringList mResources;
00099 Status mStatus;
00100 QString mStatusString;
00101 Secrecy mSecrecy;
00102 int mPriority;
00103 QString mSchedulingID;
00104
00105 Incidence *mRelatedTo;
00106 QString mRelatedToUid;
00107 Incidence::List mRelations;
00108 };
00109
00110
00111 Incidence::Incidence()
00112 : IncidenceBase(), d( new KCal::Incidence::Private )
00113 {
00114 recreate();
00115
00116 d->mAlarms.setAutoDelete( true );
00117 d->mAttachments.setAutoDelete( true );
00118 }
00119
00120 Incidence::Incidence( const Incidence &i )
00121 : IncidenceBase( i ), Recurrence::RecurrenceObserver(),
00122 d( new KCal::Incidence::Private( *i.d ) )
00123 {
00124
00125
00126
00127 Alarm::List::ConstIterator it;
00128 for ( it = i.d->mAlarms.begin(); it != i.d->mAlarms.end(); ++it ) {
00129 Alarm *b = new Alarm( **it );
00130 b->setParent( this );
00131 d->mAlarms.append( b );
00132 }
00133 d->mAlarms.setAutoDelete( true );
00134
00135 Attachment::List::ConstIterator it1;
00136 for ( it1 = i.d->mAttachments.begin(); it1 != i.d->mAttachments.end(); ++it1 ) {
00137 Attachment *a = new Attachment( **it1 );
00138 d->mAttachments.append( a );
00139 }
00140 d->mAttachments.setAutoDelete( true );
00141
00142 if ( i.d->mRecurrence ) {
00143 d->mRecurrence = new Recurrence( *( i.d->mRecurrence ) );
00144 d->mRecurrence->addObserver( this );
00145 } else {
00146 d->mRecurrence = 0;
00147 }
00148 }
00149
00150 Incidence::~Incidence()
00151 {
00152 Incidence::List Relations = d->mRelations;
00153 List::ConstIterator it;
00154 for ( it = Relations.begin(); it != Relations.end(); ++it ) {
00155 if ( (*it)->relatedTo() == this ) {
00156 (*it)->d->mRelatedTo = 0;
00157 }
00158 }
00159 if ( relatedTo() ) {
00160 relatedTo()->removeRelation( this );
00161 }
00162
00163 delete d->mRecurrence;
00164 delete d;
00165 }
00166
00167
00168
00169 static bool stringCompare( const QString &s1, const QString &s2 )
00170 {
00171 return
00172 ( s1.isEmpty() && s2.isEmpty() ) || ( s1 == s2 );
00173 }
00174
00175
00176 bool Incidence::operator==( const Incidence &i2 ) const
00177 {
00178 if ( alarms().count() != i2.alarms().count() ) {
00179 return false;
00180 }
00181
00182 Alarm::List::ConstIterator a1 = alarms().begin();
00183 Alarm::List::ConstIterator a2 = i2.alarms().begin();
00184 for ( ; a1 != alarms().end() && a2 != i2.alarms().end(); ++a1, ++a2 ) {
00185 if ( **a1 == **a2 ) {
00186 continue;
00187 } else {
00188 return false;
00189 }
00190 }
00191
00192 if ( !IncidenceBase::operator==( i2 ) ) {
00193 return false;
00194 }
00195
00196 bool recurrenceEqual = ( d->mRecurrence == 0 && i2.d->mRecurrence == 0 );
00197 if ( !recurrenceEqual ) {
00198 recurrenceEqual = d->mRecurrence != 0 &&
00199 i2.d->mRecurrence != 0 &&
00200 *d->mRecurrence == *i2.d->mRecurrence;
00201 }
00202
00203 return
00204 recurrenceEqual &&
00205 created() == i2.created() &&
00206 stringCompare( description(), i2.description() ) &&
00207 stringCompare( summary(), i2.summary() ) &&
00208 categories() == i2.categories() &&
00209
00210 stringCompare( relatedToUid(), i2.relatedToUid() ) &&
00211 relations() == i2.relations() &&
00212 attachments() == i2.attachments() &&
00213 resources() == i2.resources() &&
00214 d->mStatus == i2.d->mStatus &&
00215 ( d->mStatus == StatusNone ||
00216 stringCompare( d->mStatusString, i2.d->mStatusString ) ) &&
00217 secrecy() == i2.secrecy() &&
00218 priority() == i2.priority() &&
00219 stringCompare( location(), i2.location() ) &&
00220 stringCompare( schedulingID(), i2.schedulingID() );
00221 }
00222
00223 void Incidence::recreate()
00224 {
00225 KDateTime nowUTC = KDateTime::currentUtcDateTime();
00226 setCreated( nowUTC );
00227
00228 setUid( CalFormat::createUniqueId() );
00229 setSchedulingID( QString() );
00230
00231 setRevision( 0 );
00232
00233 setLastModified( nowUTC );
00234 }
00235
00236 void Incidence::setReadOnly( bool readOnly )
00237 {
00238 IncidenceBase::setReadOnly( readOnly );
00239 if ( d->mRecurrence ) {
00240 d->mRecurrence->setRecurReadOnly( readOnly );
00241 }
00242 }
00243
00244 void Incidence::setAllDay( bool allDay )
00245 {
00246 if ( mReadOnly ) {
00247 return;
00248 }
00249 if ( recurrence() ) {
00250 recurrence()->setAllDay( allDay );
00251 }
00252 IncidenceBase::setAllDay( allDay );
00253 }
00254
00255 void Incidence::setCreated( const KDateTime &created )
00256 {
00257 if ( mReadOnly ) {
00258 return;
00259 }
00260
00261 d->mCreated = created.toUtc();
00262
00263
00264
00265 }
00266
00267 KDateTime Incidence::created() const
00268 {
00269 return d->mCreated;
00270 }
00271
00272 void Incidence::setRevision( int rev )
00273 {
00274 if ( mReadOnly ) {
00275 return;
00276 }
00277
00278 d->mRevision = rev;
00279
00280 updated();
00281 }
00282
00283 int Incidence::revision() const
00284 {
00285 return d->mRevision;
00286 }
00287
00288 void Incidence::setDtStart( const KDateTime &dt )
00289 {
00290 if ( d->mRecurrence ) {
00291 d->mRecurrence->setStartDateTime( dt );
00292 d->mRecurrence->setAllDay( allDay() );
00293 }
00294 IncidenceBase::setDtStart( dt );
00295 }
00296
00297 KDateTime Incidence::dtEnd() const
00298 {
00299 return KDateTime();
00300 }
00301
00302 void Incidence::shiftTimes( const KDateTime::Spec &oldSpec,
00303 const KDateTime::Spec &newSpec )
00304 {
00305 IncidenceBase::shiftTimes( oldSpec, newSpec );
00306 if ( d->mRecurrence ) {
00307 d->mRecurrence->shiftTimes( oldSpec, newSpec );
00308 }
00309 for ( int i = 0, end = d->mAlarms.count(); i < end; ++i ) {
00310 d->mAlarms[i]->shiftTimes( oldSpec, newSpec );
00311 }
00312 }
00313
00314 void Incidence::setDescription( const QString &description, bool isRich )
00315 {
00316 if ( mReadOnly ) {
00317 return;
00318 }
00319 d->mDescription = description;
00320 d->mDescriptionIsRich = isRich;
00321 updated();
00322 }
00323
00324 QString Incidence::description() const
00325 {
00326 return d->mDescription;
00327 }
00328
00329 bool Incidence::descriptionIsRich() const
00330 {
00331 return d->mDescriptionIsRich;
00332 }
00333
00334 void Incidence::setSummary( const QString &summary, bool isRich )
00335 {
00336 if ( mReadOnly ) {
00337 return;
00338 }
00339 d->mSummary = summary;
00340 d->mSummaryIsRich = isRich;
00341 updated();
00342 }
00343
00344 QString Incidence::summary() const
00345 {
00346 return d->mSummary;
00347 }
00348
00349 bool Incidence::summaryIsRich() const
00350 {
00351 return d->mSummaryIsRich;
00352 }
00353
00354 void Incidence::setCategories( const QStringList &categories )
00355 {
00356 if ( mReadOnly ) {
00357 return;
00358 }
00359 d->mCategories = categories;
00360 updated();
00361 }
00362
00363 void Incidence::setCategories( const QString &catStr )
00364 {
00365 if ( mReadOnly ) {
00366 return;
00367 }
00368 d->mCategories.clear();
00369
00370 if ( catStr.isEmpty() ) {
00371 return;
00372 }
00373
00374 d->mCategories = catStr.split( "," );
00375
00376 QStringList::Iterator it;
00377 for ( it = d->mCategories.begin();it != d->mCategories.end(); ++it ) {
00378 *it = (*it).trimmed();
00379 }
00380
00381 updated();
00382 }
00383
00384 QStringList Incidence::categories() const
00385 {
00386 return d->mCategories;
00387 }
00388
00389 QString Incidence::categoriesStr() const
00390 {
00391 return d->mCategories.join( "," );
00392 }
00393
00394 void Incidence::setRelatedToUid( const QString &relatedToUid )
00395 {
00396 if ( mReadOnly || d->mRelatedToUid == relatedToUid ) {
00397 return;
00398 }
00399 d->mRelatedToUid = relatedToUid;
00400 updated();
00401 }
00402
00403 QString Incidence::relatedToUid() const
00404 {
00405 return d->mRelatedToUid;
00406 }
00407
00408 void Incidence::setRelatedTo( Incidence *incidence )
00409 {
00410 if ( mReadOnly || d->mRelatedTo == incidence ) {
00411 return;
00412 }
00413 if ( d->mRelatedTo ) {
00414 d->mRelatedTo->removeRelation( this );
00415 }
00416 d->mRelatedTo = incidence;
00417 if ( d->mRelatedTo ) {
00418 d->mRelatedTo->addRelation( this );
00419 if ( d->mRelatedTo->uid() != d->mRelatedToUid ) {
00420 setRelatedToUid( d->mRelatedTo->uid() );
00421 }
00422 } else {
00423 setRelatedToUid( QString() );
00424 }
00425 }
00426
00427 Incidence *Incidence::relatedTo() const
00428 {
00429 return d->mRelatedTo;
00430 }
00431
00432 Incidence::List Incidence::relations() const
00433 {
00434 return d->mRelations;
00435 }
00436
00437 void Incidence::addRelation( Incidence *incidence )
00438 {
00439 if ( !d->mRelations.contains( incidence ) ) {
00440 d->mRelations.append( incidence );
00441 }
00442 }
00443
00444 void Incidence::removeRelation( Incidence *incidence )
00445 {
00446 d->mRelations.removeRef( incidence );
00447 d->mRelatedToUid = QString();
00448
00449 }
00450
00451
00452
00453 Recurrence *Incidence::recurrence() const
00454 {
00455 if ( !d->mRecurrence ) {
00456 d->mRecurrence = new Recurrence();
00457 d->mRecurrence->setStartDateTime( IncidenceBase::dtStart() );
00458 d->mRecurrence->setAllDay( allDay() );
00459 d->mRecurrence->setRecurReadOnly( mReadOnly );
00460 d->mRecurrence->addObserver( const_cast<KCal::Incidence*>( this ) );
00461 }
00462
00463 return d->mRecurrence;
00464 }
00465
00466 void Incidence::clearRecurrence()
00467 {
00468 delete d->mRecurrence;
00469 d->mRecurrence = 0;
00470 }
00471
00472 ushort Incidence::recurrenceType() const
00473 {
00474 if ( d->mRecurrence ) {
00475 return d->mRecurrence->recurrenceType();
00476 } else {
00477 return Recurrence::rNone;
00478 }
00479 }
00480
00481 bool Incidence::recurs() const
00482 {
00483 if ( d->mRecurrence ) {
00484 return d->mRecurrence->recurs();
00485 } else {
00486 return false;
00487 }
00488 }
00489
00490 bool Incidence::recursOn( const QDate &date,
00491 const KDateTime::Spec &timeSpec ) const
00492 {
00493 return d->mRecurrence && d->mRecurrence->recursOn( date, timeSpec );
00494 }
00495
00496 bool Incidence::recursAt( const KDateTime &qdt ) const
00497 {
00498 return d->mRecurrence && d->mRecurrence->recursAt( qdt );
00499 }
00500
00501 QList<KDateTime> Incidence::startDateTimesForDate( const QDate &date,
00502 const KDateTime::Spec &timeSpec ) const
00503 {
00504 KDateTime start = dtStart();
00505 KDateTime end = endDateRecurrenceBase();
00506
00507 QList<KDateTime> result;
00508
00509
00510 if ( !start.isValid() && ! end.isValid() ) {
00511 return result;
00512 }
00513
00514
00515 KDateTime kdate( date, timeSpec );
00516 if ( !recurs() ) {
00517 if ( !( start > kdate || end < kdate ) ) {
00518 result << start;
00519 }
00520 return result;
00521 }
00522
00523 int days = start.daysTo( end );
00524
00525 QDate tmpday( date.addDays( -days - 1 ) );
00526 KDateTime tmp;
00527 while ( tmpday <= date ) {
00528 if ( recurrence()->recursOn( tmpday, timeSpec ) ) {
00529 QList<QTime> times = recurrence()->recurTimesOn( tmpday, timeSpec );
00530 for ( QList<QTime>::ConstIterator it = times.begin(); it != times.end(); ++it ) {
00531 tmp = KDateTime( tmpday, *it, start.timeSpec() );
00532 if ( endDateForStart( tmp ) >= kdate ) {
00533 result << tmp;
00534 }
00535 }
00536 }
00537 tmpday = tmpday.addDays( 1 );
00538 }
00539 return result;
00540 }
00541
00542 QList<KDateTime> Incidence::startDateTimesForDateTime( const KDateTime &datetime ) const
00543 {
00544 KDateTime start = dtStart();
00545 KDateTime end = endDateRecurrenceBase();
00546
00547 QList<KDateTime> result;
00548
00549
00550 if ( !start.isValid() && ! end.isValid() ) {
00551 return result;
00552 }
00553
00554
00555 if ( !recurs() ) {
00556 if ( !( start > datetime || end < datetime ) ) {
00557 result << start;
00558 }
00559 return result;
00560 }
00561
00562 int days = start.daysTo( end );
00563
00564 QDate tmpday( datetime.date().addDays( -days - 1 ) );
00565 KDateTime tmp;
00566 while ( tmpday <= datetime.date() ) {
00567 if ( recurrence()->recursOn( tmpday, datetime.timeSpec() ) ) {
00568
00569 QList<QTime> times = recurrence()->recurTimesOn( tmpday, start.timeSpec() );
00570 for ( QList<QTime>::ConstIterator it = times.begin(); it != times.end(); ++it ) {
00571 tmp = KDateTime( tmpday, *it, start.timeSpec() );
00572 if ( !( tmp > datetime || endDateForStart( tmp ) < datetime ) ) {
00573 result << tmp;
00574 }
00575 }
00576 }
00577 tmpday = tmpday.addDays( 1 );
00578 }
00579 return result;
00580 }
00581
00582 KDateTime Incidence::endDateForStart( const KDateTime &startDt ) const
00583 {
00584 KDateTime start = dtStart();
00585 KDateTime end = endDateRecurrenceBase();
00586 if ( !end.isValid() ) {
00587 return start;
00588 }
00589 if ( !start.isValid() ) {
00590 return end;
00591 }
00592
00593 return startDt.addSecs( start.secsTo( end ) );
00594 }
00595
00596 void Incidence::addAttachment( Attachment *attachment )
00597 {
00598 if ( mReadOnly || !attachment ) {
00599 return;
00600 }
00601
00602 d->mAttachments.append( attachment );
00603 updated();
00604 }
00605
00606 void Incidence::deleteAttachment( Attachment *attachment )
00607 {
00608 d->mAttachments.removeRef( attachment );
00609 }
00610
00611 void Incidence::deleteAttachments( const QString &mime )
00612 {
00613 Attachment::List::Iterator it = d->mAttachments.begin();
00614 while ( it != d->mAttachments.end() ) {
00615 if ( (*it)->mimeType() == mime ) {
00616 d->mAttachments.erase( it );
00617 } else {
00618 ++it;
00619 }
00620 }
00621 }
00622
00623 Attachment::List Incidence::attachments() const
00624 {
00625 return d->mAttachments;
00626 }
00627
00628 Attachment::List Incidence::attachments( const QString &mime ) const
00629 {
00630 Attachment::List attachments;
00631 Attachment::List::ConstIterator it;
00632 for ( it = d->mAttachments.begin(); it != d->mAttachments.end(); ++it ) {
00633 if ( (*it)->mimeType() == mime ) {
00634 attachments.append( *it );
00635 }
00636 }
00637 return attachments;
00638 }
00639
00640 void Incidence::clearAttachments()
00641 {
00642 d->mAttachments.clear();
00643 }
00644
00645 void Incidence::setResources( const QStringList &resources )
00646 {
00647 if ( mReadOnly ) {
00648 return;
00649 }
00650
00651 d->mResources = resources;
00652 updated();
00653 }
00654
00655 QStringList Incidence::resources() const
00656 {
00657 return d->mResources;
00658 }
00659
00660 void Incidence::setPriority( int priority )
00661 {
00662 if ( mReadOnly ) {
00663 return;
00664 }
00665
00666 d->mPriority = priority;
00667 updated();
00668 }
00669
00670 int Incidence::priority() const
00671 {
00672 return d->mPriority;
00673 }
00674
00675 void Incidence::setStatus( Incidence::Status status )
00676 {
00677 if ( mReadOnly || status == StatusX ) {
00678 return;
00679 }
00680
00681 d->mStatus = status;
00682 d->mStatusString.clear();
00683 updated();
00684 }
00685
00686 void Incidence::setCustomStatus( const QString &status )
00687 {
00688 if ( mReadOnly ) {
00689 return;
00690 }
00691
00692 d->mStatus = status.isEmpty() ? StatusNone : StatusX;
00693 d->mStatusString = status;
00694 updated();
00695 }
00696
00697 Incidence::Status Incidence::status() const
00698 {
00699 return d->mStatus;
00700 }
00701
00702 QString Incidence::statusStr() const
00703 {
00704 if ( d->mStatus == StatusX ) {
00705 return d->mStatusString;
00706 }
00707
00708 return statusName( d->mStatus );
00709 }
00710
00711 QString Incidence::statusName( Incidence::Status status )
00712 {
00713 switch ( status ) {
00714 case StatusTentative:
00715 return i18nc( "@item event is tentative", "Tentative" );
00716 case StatusConfirmed:
00717 return i18nc( "@item event is definite", "Confirmed" );
00718 case StatusCompleted:
00719 return i18nc( "@item to-do is complete", "Completed" );
00720 case StatusNeedsAction:
00721 return i18nc( "@item to-do needs action", "Needs-Action" );
00722 case StatusCanceled:
00723 return i18nc( "@item event orto-do is canceled; journal is removed", "Canceled" );
00724 case StatusInProcess:
00725 return i18nc( "@item to-do is in process", "In-Process" );
00726 case StatusDraft:
00727 return i18nc( "@item journal is in draft form", "Draft" );
00728 case StatusFinal:
00729 return i18nc( "@item journal is in final form", "Final" );
00730 case StatusX:
00731 case StatusNone:
00732 default:
00733 return QString();
00734 }
00735 }
00736
00737 void Incidence::setSecrecy( Incidence::Secrecy secrecy )
00738 {
00739 if ( mReadOnly ) {
00740 return;
00741 }
00742
00743 d->mSecrecy = secrecy;
00744 updated();
00745 }
00746
00747 Incidence::Secrecy Incidence::secrecy() const
00748 {
00749 return d->mSecrecy;
00750 }
00751
00752 QString Incidence::secrecyStr() const
00753 {
00754 return secrecyName( d->mSecrecy );
00755 }
00756
00757 QString Incidence::secrecyName( Incidence::Secrecy secrecy )
00758 {
00759 switch ( secrecy ) {
00760 case SecrecyPublic:
00761 return i18nc( "@item incidence access if for everyone", "Public" );
00762 case SecrecyPrivate:
00763 return i18nc( "@item incidence access is by owner only", "Private" );
00764 case SecrecyConfidential:
00765 return i18nc( "@item incidence access is by owner and a controlled group", "Confidential" );
00766 default:
00767 return QString();
00768 }
00769 }
00770
00771 QStringList Incidence::secrecyList()
00772 {
00773 QStringList list;
00774 list << secrecyName( SecrecyPublic );
00775 list << secrecyName( SecrecyPrivate );
00776 list << secrecyName( SecrecyConfidential );
00777
00778 return list;
00779 }
00780
00781 const Alarm::List &Incidence::alarms() const
00782 {
00783 return d->mAlarms;
00784 }
00785
00786 Alarm *Incidence::newAlarm()
00787 {
00788 Alarm *alarm = new Alarm( this );
00789 d->mAlarms.append( alarm );
00790 return alarm;
00791 }
00792
00793 void Incidence::addAlarm( Alarm *alarm )
00794 {
00795 d->mAlarms.append( alarm );
00796 updated();
00797 }
00798
00799 void Incidence::removeAlarm( Alarm *alarm )
00800 {
00801 d->mAlarms.removeRef( alarm );
00802 updated();
00803 }
00804
00805 void Incidence::clearAlarms()
00806 {
00807 d->mAlarms.clear();
00808 updated();
00809 }
00810
00811 bool Incidence::isAlarmEnabled() const
00812 {
00813 Alarm::List::ConstIterator it;
00814 for ( it = d->mAlarms.begin(); it != d->mAlarms.end(); ++it ) {
00815 if ( (*it)->enabled() ) {
00816 return true;
00817 }
00818 }
00819 return false;
00820 }
00821
00822 void Incidence::setLocation( const QString &location, bool isRich )
00823 {
00824 if ( mReadOnly ) {
00825 return;
00826 }
00827
00828 d->mLocation = location;
00829 d->mLocationIsRich = isRich;
00830 updated();
00831 }
00832
00833 QString Incidence::location() const
00834 {
00835 return d->mLocation;
00836 }
00837
00838 bool Incidence::locationIsRich() const
00839 {
00840 return d->mLocationIsRich;
00841 }
00842
00843 void Incidence::setSchedulingID( const QString &sid )
00844 {
00845 d->mSchedulingID = sid;
00846 }
00847
00848 QString Incidence::schedulingID() const
00849 {
00850 if ( d->mSchedulingID.isNull() ) {
00851
00852 return uid();
00853 }
00854 return d->mSchedulingID;
00855 }
00856
00860 void Incidence::recurrenceUpdated( Recurrence *recurrence )
00861 {
00862 if ( recurrence == d->mRecurrence ) {
00863 updated();
00864 }
00865 }