1/*
2 This file is part of the kcal library.
3
4 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22/**
23 @file
24 This file is part of the API for handling calendar data and
25 defines the Incidence class.
26
27 @brief
28 Provides the class common to non-FreeBusy (Events, To-dos, Journals)
29 calendar components known as incidences.
30
31 @author Cornelius Schumacher \<schumacher@kde.org\>
32 @author Reinhold Kainhofer \<reinhold@kainhofer.com\>
33*/
34
35#include "incidence.h"
36#include "calformat.h"
37
38#include "kpimutils/kfileio.h"
39
40#include <kglobal.h>
41#include <klocalizedstring.h>
42#include <kdebug.h>
43#include <ktemporaryfile.h>
44#include <kde_file.h>
45
46#include <QtCore/QList>
47#include <QTextDocument> // for Qt::escape() and Qt::mightBeRichText()
48#include <KMimeType>
49
50using namespace KCal;
51
52/**
53 Private class that helps to provide binary compatibility between releases.
54 @internal
55*/
56//@cond PRIVATE
57class KCal::Incidence::Private
58{
59 public:
60 Private()
61 : mDescriptionIsRich( false ),
62 mSummaryIsRich( false ),
63 mLocationIsRich( false ),
64 mRecurrence( 0 ),
65 mStatus( StatusNone ),
66 mSecrecy( SecrecyPublic ),
67 mPriority( 0 ),
68 mRelatedTo( 0 ),
69 mGeoLatitude( 0 ),
70 mGeoLongitude( 0 ),
71 mHasGeo( false )
72 {
73 mAlarms.setAutoDelete( true );
74 mAttachments.setAutoDelete( true );
75 }
76
77 Private( const Private &p )
78 : mCreated( p.mCreated ),
79 mRevision( p.mRevision ),
80 mDescription( p.mDescription ),
81 mDescriptionIsRich( p.mDescriptionIsRich ),
82 mSummary( p.mSummary ),
83 mSummaryIsRich( p.mSummaryIsRich ),
84 mLocation( p.mLocation ),
85 mLocationIsRich( p.mLocationIsRich ),
86 mCategories( p.mCategories ),
87 mRecurrence( p.mRecurrence ),
88 mResources( p.mResources ),
89 mStatus( p.mStatus ),
90 mStatusString( p.mStatusString ),
91 mSecrecy( p.mSecrecy ),
92 mPriority( p.mPriority ),
93 mSchedulingID( p.mSchedulingID ),
94 mRelatedTo( p.mRelatedTo ),
95 mRelatedToUid( p.mRelatedToUid ),
96 mGeoLatitude( p.mGeoLatitude ),
97 mGeoLongitude( p.mGeoLongitude ),
98 mHasGeo( p.mHasGeo )
99 {
100 mAlarms.setAutoDelete( true );
101 mAttachments.setAutoDelete( true );
102 }
103
104 void clear()
105 {
106 mAlarms.clearAll();
107 mAttachments.clearAll();
108 delete mRecurrence;
109 }
110
111 KDateTime mCreated; // creation datetime
112 int mRevision; // revision number
113
114 QString mDescription; // description string
115 bool mDescriptionIsRich; // description string is richtext.
116 QString mSummary; // summary string
117 bool mSummaryIsRich; // summary string is richtext.
118 QString mLocation; // location string
119 bool mLocationIsRich; // location string is richtext.
120 QStringList mCategories; // category list
121 mutable Recurrence *mRecurrence; // recurrence
122 Attachment::List mAttachments; // attachments list
123 Alarm::List mAlarms; // alarms list
124 QStringList mResources; // resources list (not calendar resources)
125 Status mStatus; // status
126 QString mStatusString; // status string, for custom status
127 Secrecy mSecrecy; // secrecy
128 int mPriority; // priority: 1 = highest, 2 = less, etc.
129 QString mSchedulingID; // ID for scheduling mails
130
131 Incidence *mRelatedTo; // incidence this is related to
132 QString mRelatedToUid; // incidence (by Uid) this is related to
133 Incidence::List mRelations; // a list of incidences related to this
134 float mGeoLatitude; // Specifies latitude in decimal degrees
135 float mGeoLongitude; // Specifies longitude in decimal degrees
136 bool mHasGeo; // if incidence has geo data
137 QHash<Attachment *, QString> mTempFiles; // Temporary files for writing attachments to.
138};
139//@endcond
140
141Incidence::Incidence()
142 : IncidenceBase(), d( new KCal::Incidence::Private )
143{
144 recreate();
145}
146
147Incidence::Incidence( const Incidence &i )
148 : IncidenceBase( i ),
149 Recurrence::RecurrenceObserver(),
150 d( new KCal::Incidence::Private( *i.d ) )
151{
152 init( i );
153}
154
155void Incidence::init( const Incidence &i )
156{
157 d->mRevision = i.d->mRevision;
158 d->mCreated = i.d->mCreated;
159 d->mDescription = i.d->mDescription;
160 d->mSummary = i.d->mSummary;
161 d->mCategories = i.d->mCategories;
162 d->mRelatedTo = i.d->mRelatedTo;
163 d->mRelatedToUid = i.d->mRelatedToUid;
164 d->mRelations = i.d->mRelations;
165 d->mResources = i.d->mResources;
166 d->mStatusString = i.d->mStatusString;
167 d->mStatus = i.d->mStatus;
168 d->mSecrecy = i.d->mSecrecy;
169 d->mPriority = i.d->mPriority;
170 d->mLocation = i.d->mLocation;
171 d->mGeoLatitude = i.d->mGeoLatitude;
172 d->mGeoLongitude = i.d->mGeoLongitude;
173 d->mHasGeo = i.d->mHasGeo;
174
175 // Alarms and Attachments are stored in ListBase<...>, which is a QValueList<...*>.
176 // We need to really duplicate the objects stored therein, otherwise deleting
177 // i will also delete all attachments from this object (setAutoDelete...)
178 foreach ( Alarm *alarm, i.d->mAlarms ) {
179 Alarm *b = new Alarm( *alarm );
180 b->setParent( this );
181 d->mAlarms.append( b );
182 }
183
184 foreach ( Attachment *attachment, i.d->mAttachments ) {
185 Attachment *a = new Attachment( *attachment );
186 d->mAttachments.append( a );
187 }
188
189 if ( i.d->mRecurrence ) {
190 d->mRecurrence = new Recurrence( *( i.d->mRecurrence ) );
191 d->mRecurrence->addObserver( this );
192 } else {
193 d->mRecurrence = 0;
194 }
195}
196
197Incidence::~Incidence()
198{
199 Incidence::List relations = d->mRelations;
200 foreach ( Incidence *incidence, relations ) {
201 if ( incidence->relatedTo() == this ) {
202 incidence->setRelatedTo( 0 );
203 }
204 }
205
206 if ( relatedTo() ) {
207 relatedTo()->removeRelation( this );
208 }
209 delete d->mRecurrence;
210 delete d;
211}
212
213//@cond PRIVATE
214// A string comparison that considers that null and empty are the same
215static bool stringCompare( const QString &s1, const QString &s2 )
216{
217 return ( s1.isEmpty() && s2.isEmpty() ) || ( s1 == s2 );
218}
219
220//@endcond
221Incidence &Incidence::operator=( const Incidence &other )
222{
223 // check for self assignment
224 if ( &other == this ) {
225 return *this;
226 }
227
228 d->clear();
229 //TODO: should relations be cleared out, as in destructor???
230 IncidenceBase::operator=( other );
231 init( other );
232 return *this;
233}
234
235bool Incidence::operator==( const Incidence &i2 ) const
236{
237 if ( alarms().count() != i2.alarms().count() ) {
238 return false; // no need to check further
239 }
240
241 Alarm::List::ConstIterator a1 = alarms().constBegin();
242 Alarm::List::ConstIterator a1end = alarms().constEnd();
243 Alarm::List::ConstIterator a2 = i2.alarms().begin();
244 Alarm::List::ConstIterator a2end = i2.alarms().constEnd();
245 for ( ; a1 != a1end && a2 != a2end; ++a1, ++a2 ) {
246 if ( **a1 == **a2 ) {
247 continue;
248 } else {
249 return false;
250 }
251 }
252
253 if ( !IncidenceBase::operator==( i2 ) ) {
254 return false;
255 }
256
257 bool recurrenceEqual = ( d->mRecurrence == 0 && i2.d->mRecurrence == 0 );
258 if ( !recurrenceEqual ) {
259 recurrenceEqual = d->mRecurrence != 0 &&
260 i2.d->mRecurrence != 0 &&
261 *d->mRecurrence == *i2.d->mRecurrence;
262 }
263
264 return
265 recurrenceEqual &&
266 created() == i2.created() &&
267 stringCompare( description(), i2.description() ) &&
268 stringCompare( summary(), i2.summary() ) &&
269 categories() == i2.categories() &&
270 // no need to compare mRelatedTo
271 stringCompare( relatedToUid(), i2.relatedToUid() ) &&
272 relations() == i2.relations() &&
273 attachments() == i2.attachments() &&
274 resources() == i2.resources() &&
275 d->mStatus == i2.d->mStatus &&
276 ( d->mStatus == StatusNone ||
277 stringCompare( d->mStatusString, i2.d->mStatusString ) ) &&
278 secrecy() == i2.secrecy() &&
279 priority() == i2.priority() &&
280 stringCompare( location(), i2.location() ) &&
281 stringCompare( schedulingID(), i2.schedulingID() );
282}
283
284void Incidence::recreate()
285{
286 KDateTime nowUTC = KDateTime::currentUtcDateTime();
287 setCreated( nowUTC );
288
289 setUid( CalFormat::createUniqueId() );
290 setSchedulingID( QString() );
291
292 setRevision( 0 );
293
294 setLastModified( nowUTC );
295}
296
297void Incidence::setReadOnly( bool readOnly )
298{
299 IncidenceBase::setReadOnly( readOnly );
300 if ( d->mRecurrence ) {
301 d->mRecurrence->setRecurReadOnly( readOnly );
302 }
303}
304
305void Incidence::setAllDay( bool allDay )
306{
307 if ( mReadOnly ) {
308 return;
309 }
310 if ( recurrence() ) {
311 recurrence()->setAllDay( allDay );
312 }
313 IncidenceBase::setAllDay( allDay );
314}
315
316void Incidence::setCreated( const KDateTime &created )
317{
318 if ( mReadOnly ) {
319 return;
320 }
321
322 d->mCreated = created.toUtc();
323
324// FIXME: Shouldn't we call updated for the creation date, too?
325// updated();
326}
327
328KDateTime Incidence::created() const
329{
330 return d->mCreated;
331}
332
333void Incidence::setRevision( int rev )
334{
335 if ( mReadOnly ) {
336 return;
337 }
338
339 d->mRevision = rev;
340
341 updated();
342}
343
344int Incidence::revision() const
345{
346 return d->mRevision;
347}
348
349void Incidence::setDtStart( const KDateTime &dt )
350{
351 if ( d->mRecurrence ) {
352 d->mRecurrence->setStartDateTime( dt );
353 d->mRecurrence->setAllDay( allDay() );
354 }
355 IncidenceBase::setDtStart( dt );
356}
357
358KDateTime Incidence::dtEnd() const
359{
360 return KDateTime();
361}
362
363void Incidence::shiftTimes( const KDateTime::Spec &oldSpec,
364 const KDateTime::Spec &newSpec )
365{
366 IncidenceBase::shiftTimes( oldSpec, newSpec );
367 if ( d->mRecurrence ) {
368 d->mRecurrence->shiftTimes( oldSpec, newSpec );
369 }
370 for ( int i = 0, end = d->mAlarms.count(); i < end; ++i ) {
371 d->mAlarms[i]->shiftTimes( oldSpec, newSpec );
372 }
373}
374
375void Incidence::setDescription( const QString &description, bool isRich )
376{
377 if ( mReadOnly ) {
378 return;
379 }
380 d->mDescription = description;
381 d->mDescriptionIsRich = isRich;
382 updated();
383}
384
385void Incidence::setDescription( const QString &description )
386{
387 setDescription( description, Qt::mightBeRichText( description ) );
388}
389
390QString Incidence::description() const
391{
392 return d->mDescription;
393}
394
395QString Incidence::richDescription() const
396{
397 if ( descriptionIsRich() ) {
398 return d->mDescription;
399 } else {
400 return Qt::escape( d->mDescription ).replace( '\n', "<br/>" );
401 }
402}
403
404bool Incidence::descriptionIsRich() const
405{
406 return d->mDescriptionIsRich;
407}
408
409void Incidence::setSummary( const QString &summary, bool isRich )
410{
411 if ( mReadOnly ) {
412 return;
413 }
414 d->mSummary = summary;
415 d->mSummaryIsRich = isRich;
416 updated();
417}
418
419void Incidence::setSummary( const QString &summary )
420{
421 setSummary( summary, Qt::mightBeRichText( summary ) );
422}
423
424QString Incidence::summary() const
425{
426 return d->mSummary;
427}
428
429QString Incidence::richSummary() const
430{
431 if ( summaryIsRich() ) {
432 return d->mSummary;
433 } else {
434 return Qt::escape( d->mSummary ).replace( '\n', "<br/>" );
435 }
436}
437
438bool Incidence::summaryIsRich() const
439{
440 return d->mSummaryIsRich;
441}
442
443void Incidence::setCategories( const QStringList &categories )
444{
445 if ( mReadOnly ) {
446 return;
447 }
448 d->mCategories = categories;
449 updated();
450}
451
452void Incidence::setCategories( const QString &catStr )
453{
454 if ( mReadOnly ) {
455 return;
456 }
457 d->mCategories.clear();
458
459 if ( catStr.isEmpty() ) {
460 return;
461 }
462
463 d->mCategories = catStr.split( ',' );
464
465 QStringList::Iterator it;
466 for ( it = d->mCategories.begin();it != d->mCategories.end(); ++it ) {
467 *it = (*it).trimmed();
468 }
469
470 updated();
471}
472
473QStringList Incidence::categories() const
474{
475 return d->mCategories;
476}
477
478QString Incidence::categoriesStr() const
479{
480 return d->mCategories.join( "," );
481}
482
483void Incidence::setRelatedToUid( const QString &relatedToUid )
484{
485 if ( d->mRelatedToUid == relatedToUid ) {
486 return;
487 }
488 d->mRelatedToUid = relatedToUid;
489 updated();
490}
491
492QString Incidence::relatedToUid() const
493{
494 return d->mRelatedToUid;
495}
496
497void Incidence::setRelatedTo( Incidence *incidence )
498{
499 if ( d->mRelatedTo == incidence ) {
500 return;
501 }
502 if ( d->mRelatedTo ) {
503 d->mRelatedTo->removeRelation( this );
504 }
505 d->mRelatedTo = incidence;
506 if ( d->mRelatedTo ) {
507 d->mRelatedTo->addRelation( this );
508 if ( d->mRelatedTo->uid() != d->mRelatedToUid ) {
509 setRelatedToUid( d->mRelatedTo->uid() );
510 }
511 } else {
512 setRelatedToUid( QString() );
513 }
514}
515
516Incidence *Incidence::relatedTo() const
517{
518 return d->mRelatedTo;
519}
520
521Incidence::List Incidence::relations() const
522{
523 return d->mRelations;
524}
525
526void Incidence::addRelation( Incidence *incidence )
527{
528 if ( !d->mRelations.contains( incidence ) ) {
529 d->mRelations.append( incidence );
530 }
531}
532
533void Incidence::removeRelation( Incidence *incidence )
534{
535 const QString uid = incidence->uid();
536 d->mRelations.removeRef( incidence );
537 if ( d->mRelatedToUid == uid ) {
538 d->mRelatedToUid.clear();
539 }
540// if (incidence->getRelatedTo() == this) incidence->setRelatedTo(0);
541}
542
543// %%%%%%%%%%%% Recurrence-related methods %%%%%%%%%%%%%%%%%%%%
544
545Recurrence *Incidence::recurrence() const
546{
547 if ( !d->mRecurrence ) {
548 d->mRecurrence = new Recurrence();
549 d->mRecurrence->setStartDateTime( IncidenceBase::dtStart() );
550 d->mRecurrence->setAllDay( allDay() );
551 d->mRecurrence->setRecurReadOnly( mReadOnly );
552 d->mRecurrence->addObserver( const_cast<KCal::Incidence*>( this ) );
553 }
554
555 return d->mRecurrence;
556}
557
558void Incidence::clearRecurrence()
559{
560 delete d->mRecurrence;
561 d->mRecurrence = 0;
562}
563
564ushort Incidence::recurrenceType() const
565{
566 if ( d->mRecurrence ) {
567 return d->mRecurrence->recurrenceType();
568 } else {
569 return Recurrence::rNone;
570 }
571}
572
573bool Incidence::recurs() const
574{
575 if ( d->mRecurrence ) {
576 return d->mRecurrence->recurs();
577 } else {
578 return false;
579 }
580}
581
582bool Incidence::recursOn( const QDate &date,
583 const KDateTime::Spec &timeSpec ) const
584{
585 return d->mRecurrence && d->mRecurrence->recursOn( date, timeSpec );
586}
587
588bool Incidence::recursAt( const KDateTime &qdt ) const
589{
590 return d->mRecurrence && d->mRecurrence->recursAt( qdt );
591}
592
593QList<KDateTime> Incidence::startDateTimesForDate( const QDate &date,
594 const KDateTime::Spec &timeSpec ) const
595{
596 KDateTime start = dtStart();
597 KDateTime end = endDateRecurrenceBase();
598
599 QList<KDateTime> result;
600
601 // TODO_Recurrence: Also work if only due date is given...
602 if ( !start.isValid() && ! end.isValid() ) {
603 return result;
604 }
605
606 // if the incidence doesn't recur,
607 KDateTime kdate( date, timeSpec );
608 if ( !recurs() ) {
609 if ( !( start > kdate || end < kdate ) ) {
610 result << start;
611 }
612 return result;
613 }
614
615 int days = start.daysTo( end );
616 // Account for possible recurrences going over midnight, while the original event doesn't
617 QDate tmpday( date.addDays( -days - 1 ) );
618 KDateTime tmp;
619 while ( tmpday <= date ) {
620 if ( recurrence()->recursOn( tmpday, timeSpec ) ) {
621 QList<QTime> times = recurrence()->recurTimesOn( tmpday, timeSpec );
622 foreach ( const QTime &time, times ) {
623 tmp = KDateTime( tmpday, time, start.timeSpec() );
624 if ( endDateForStart( tmp ) >= kdate ) {
625 result << tmp;
626 }
627 }
628 }
629 tmpday = tmpday.addDays( 1 );
630 }
631 return result;
632}
633
634QList<KDateTime> Incidence::startDateTimesForDateTime( const KDateTime &datetime ) const
635{
636 KDateTime start = dtStart();
637 KDateTime end = endDateRecurrenceBase();
638
639 QList<KDateTime> result;
640
641 // TODO_Recurrence: Also work if only due date is given...
642 if ( !start.isValid() && ! end.isValid() ) {
643 return result;
644 }
645
646 // if the incidence doesn't recur,
647 if ( !recurs() ) {
648 if ( !( start > datetime || end < datetime ) ) {
649 result << start;
650 }
651 return result;
652 }
653
654 int days = start.daysTo( end );
655 // Account for possible recurrences going over midnight, while the original event doesn't
656 QDate tmpday( datetime.date().addDays( -days - 1 ) );
657 KDateTime tmp;
658 while ( tmpday <= datetime.date() ) {
659 if ( recurrence()->recursOn( tmpday, datetime.timeSpec() ) ) {
660 // Get the times during the day (in start date's time zone) when recurrences happen
661 QList<QTime> times = recurrence()->recurTimesOn( tmpday, start.timeSpec() );
662 foreach ( const QTime &time, times ) {
663 tmp = KDateTime( tmpday, time, start.timeSpec() );
664 if ( !( tmp > datetime || endDateForStart( tmp ) < datetime ) ) {
665 result << tmp;
666 }
667 }
668 }
669 tmpday = tmpday.addDays( 1 );
670 }
671 return result;
672}
673
674KDateTime Incidence::endDateForStart( const KDateTime &startDt ) const
675{
676 KDateTime start = dtStart();
677 KDateTime end = endDateRecurrenceBase();
678 if ( !end.isValid() ) {
679 return start;
680 }
681 if ( !start.isValid() ) {
682 return end;
683 }
684
685 return startDt.addSecs( start.secsTo( end ) );
686}
687
688void Incidence::addAttachment( Attachment *attachment )
689{
690 if ( mReadOnly || !attachment ) {
691 return;
692 }
693
694 d->mAttachments.append( attachment );
695 updated();
696}
697
698void Incidence::deleteAttachment( Attachment *attachment )
699{
700 d->mAttachments.removeRef( attachment );
701}
702
703void Incidence::deleteAttachments( const QString &mime )
704{
705 Attachment::List::Iterator it = d->mAttachments.begin();
706 while ( it != d->mAttachments.end() ) {
707 if ( (*it)->mimeType() == mime ) {
708 d->mAttachments.removeRef( it );
709 } else {
710 ++it;
711 }
712 }
713}
714
715Attachment::List Incidence::attachments() const
716{
717 return d->mAttachments;
718}
719
720Attachment::List Incidence::attachments( const QString &mime ) const
721{
722 Attachment::List attachments;
723 Attachment::List::ConstIterator it;
724 foreach ( Attachment *attachment, d->mAttachments ) {
725 if ( attachment->mimeType() == mime ) {
726 attachments.append( attachment );
727 }
728 }
729 return attachments;
730}
731
732void Incidence::clearAttachments()
733{
734 d->mAttachments.clearAll();
735}
736
737QString Incidence::writeAttachmentToTempFile( Attachment* attachment ) const
738{
739 if ( d->mTempFiles.contains( attachment ) ) {
740 return d->mTempFiles.value( attachment );
741 }
742 KTemporaryFile *file = new KTemporaryFile();
743
744 QStringList patterns = KMimeType::mimeType( attachment->mimeType() )->patterns();
745
746 if ( !patterns.empty() ) {
747 file->setSuffix( QString( patterns.first() ).remove( '*' ) );
748 }
749 file->setAutoRemove( true );
750 file->open();
751 // read-only not to give the idea that it could be written to
752 file->setPermissions( QFile::ReadUser );
753 file->write( QByteArray::fromBase64( attachment->data() ) );
754 d->mTempFiles.insert( attachment, file->fileName() );
755 file->close();
756 return d->mTempFiles.value( attachment );
757}
758
759void Incidence::clearTempFiles()
760{
761 QHash<Attachment*, QString>::const_iterator it = d->mTempFiles.constBegin();
762 const QHash<Attachment*, QString>::const_iterator end = d->mTempFiles.constEnd();
763 for ( ; it != end; ++it )
764 {
765 QFile::remove( it.value() );
766 }
767 d->mTempFiles.clear();
768}
769
770void Incidence::setResources( const QStringList &resources )
771{
772 if ( mReadOnly ) {
773 return;
774 }
775
776 d->mResources = resources;
777 updated();
778}
779
780QStringList Incidence::resources() const
781{
782 return d->mResources;
783}
784
785void Incidence::setPriority( int priority )
786{
787 if ( mReadOnly ) {
788 return;
789 }
790
791 d->mPriority = priority;
792 updated();
793}
794
795int Incidence::priority() const
796{
797 return d->mPriority;
798}
799
800void Incidence::setStatus( Incidence::Status status )
801{
802 if ( mReadOnly || status == StatusX ) {
803 return;
804 }
805
806 d->mStatus = status;
807 d->mStatusString.clear();
808 updated();
809}
810
811void Incidence::setCustomStatus( const QString &status )
812{
813 if ( mReadOnly ) {
814 return;
815 }
816
817 d->mStatus = status.isEmpty() ? StatusNone : StatusX;
818 d->mStatusString = status;
819 updated();
820}
821
822Incidence::Status Incidence::status() const
823{
824 return d->mStatus;
825}
826
827QString Incidence::statusStr() const
828{
829 if ( d->mStatus == StatusX ) {
830 return d->mStatusString;
831 }
832
833 return statusName( d->mStatus );
834}
835
836QString Incidence::statusName( Incidence::Status status )
837{
838 switch ( status ) {
839 case StatusTentative:
840 return i18nc( "@item event is tentative", "Tentative" );
841 case StatusConfirmed:
842 return i18nc( "@item event is definite", "Confirmed" );
843 case StatusCompleted:
844 return i18nc( "@item to-do is complete", "Completed" );
845 case StatusNeedsAction:
846 return i18nc( "@item to-do needs action", "Needs-Action" );
847 case StatusCanceled:
848 return i18nc( "@item event orto-do is canceled; journal is removed", "Canceled" );
849 case StatusInProcess:
850 return i18nc( "@item to-do is in process", "In-Process" );
851 case StatusDraft:
852 return i18nc( "@item journal is in draft form", "Draft" );
853 case StatusFinal:
854 return i18nc( "@item journal is in final form", "Final" );
855 case StatusX:
856 case StatusNone:
857 default:
858 return QString();
859 }
860}
861
862void Incidence::setSecrecy( Incidence::Secrecy secrecy )
863{
864 if ( mReadOnly ) {
865 return;
866 }
867
868 d->mSecrecy = secrecy;
869 updated();
870}
871
872Incidence::Secrecy Incidence::secrecy() const
873{
874 return d->mSecrecy;
875}
876
877QString Incidence::secrecyStr() const
878{
879 return secrecyName( d->mSecrecy );
880}
881
882QString Incidence::secrecyName( Incidence::Secrecy secrecy )
883{
884 switch ( secrecy ) {
885 case SecrecyPublic:
886 return i18nc( "@item incidence access if for everyone", "Public" );
887 case SecrecyPrivate:
888 return i18nc( "@item incidence access is by owner only", "Private" );
889 case SecrecyConfidential:
890 return i18nc( "@item incidence access is by owner and a controlled group", "Confidential" );
891 default:
892 return QString(); // to make compilers happy
893 }
894}
895
896QStringList Incidence::secrecyList()
897{
898 QStringList list;
899 list << secrecyName( SecrecyPublic );
900 list << secrecyName( SecrecyPrivate );
901 list << secrecyName( SecrecyConfidential );
902
903 return list;
904}
905
906const Alarm::List &Incidence::alarms() const
907{
908 return d->mAlarms;
909}
910
911Alarm *Incidence::newAlarm()
912{
913 Alarm *alarm = new Alarm( this );
914 d->mAlarms.append( alarm );
915 return alarm;
916}
917
918void Incidence::addAlarm( Alarm *alarm )
919{
920 d->mAlarms.append( alarm );
921 updated();
922}
923
924void Incidence::removeAlarm( Alarm *alarm )
925{
926 d->mAlarms.removeRef( alarm );
927 updated();
928}
929
930void Incidence::clearAlarms()
931{
932 d->mAlarms.clearAll();
933 updated();
934}
935
936bool Incidence::isAlarmEnabled() const
937{
938 foreach ( Alarm *alarm, d->mAlarms ) {
939 if ( alarm->enabled() ) {
940 return true;
941 }
942 }
943 return false;
944}
945
946void Incidence::setLocation( const QString &location, bool isRich )
947{
948 if ( mReadOnly ) {
949 return;
950 }
951
952 d->mLocation = location;
953 d->mLocationIsRich = isRich;
954 updated();
955}
956
957void Incidence::setLocation( const QString &location )
958{
959 setLocation( location, Qt::mightBeRichText( location ) );
960}
961
962QString Incidence::location() const
963{
964 return d->mLocation;
965}
966
967QString Incidence::richLocation() const
968{
969 if ( locationIsRich() ) {
970 return d->mLocation;
971 } else {
972 return Qt::escape( d->mLocation ).replace( '\n', "<br/>" );
973 }
974}
975
976bool Incidence::locationIsRich() const
977{
978 return d->mLocationIsRich;
979}
980
981void Incidence::setSchedulingID( const QString &sid )
982{
983 d->mSchedulingID = sid;
984}
985
986QString Incidence::schedulingID() const
987{
988 if ( d->mSchedulingID.isNull() ) {
989 // Nothing set, so use the normal uid
990 return uid();
991 }
992 return d->mSchedulingID;
993}
994
995bool Incidence::hasGeo() const
996{
997 return d->mHasGeo;
998}
999
1000void Incidence::setHasGeo( bool hasGeo )
1001{
1002 if ( mReadOnly ) {
1003 return;
1004 }
1005
1006 d->mHasGeo = hasGeo;
1007 updated();
1008}
1009
1010float &Incidence::geoLatitude() const
1011{
1012 return d->mGeoLatitude;
1013}
1014
1015void Incidence::setGeoLatitude( float geolatitude )
1016{
1017 if ( mReadOnly ) {
1018 return;
1019 }
1020
1021 d->mGeoLatitude = geolatitude;
1022 updated();
1023}
1024
1025float &Incidence::geoLongitude() const
1026{
1027 return d->mGeoLongitude;
1028}
1029
1030void Incidence::setGeoLongitude( float geolongitude )
1031{
1032 if ( mReadOnly ) {
1033 return;
1034 }
1035
1036 d->mGeoLongitude = geolongitude;
1037 updated();
1038}
1039
1040/** Observer interface for the recurrence class. If the recurrence is changed,
1041 this method will be called for the incidence the recurrence object
1042 belongs to. */
1043void Incidence::recurrenceUpdated( Recurrence *recurrence )
1044{
1045 if ( recurrence == d->mRecurrence ) {
1046 updated();
1047 }
1048}
1049