1/*
2 This file is part of the kcal library.
3
4 Copyright (c) 1998 Preston Brown <pbrown@kde.org>
5 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
6 Copyright (c) 2003 David Jarvie <software@astrojar.org.uk>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22*/
23/**
24 @file
25 This file is part of the API for handling calendar data and
26 defines the Alarm class.
27
28 @brief
29 Represents an alarm notification.
30
31 @author Cornelius Schumacher \<schumacher@kde.org\>
32*/
33
34#include "alarm.h"
35#include "incidence.h"
36#include "todo.h"
37
38#include <kdebug.h>
39
40using namespace KCal;
41
42/**
43 Private class that helps to provide binary compatibility between releases.
44 @internal
45*/
46//@cond PRIVATE
47class KCal::Alarm::Private
48{
49 public:
50 Private()
51 : mParent( 0 ),
52 mType( Alarm::Invalid ),
53 mAlarmSnoozeTime( 5 ),
54 mAlarmRepeatCount( 0 ),
55 mEndOffset( false ),
56 mHasTime( false ),
57 mAlarmEnabled( false )
58 {}
59 Private( const Private &other )
60 : mParent( other.mParent ),
61 mType( other.mType ),
62 mDescription( other.mDescription ),
63 mFile( other.mFile ),
64 mMailSubject( other.mMailSubject ),
65 mMailAttachFiles( other.mMailAttachFiles ),
66 mMailAddresses( other.mMailAddresses ),
67 mAlarmTime( other.mAlarmTime ),
68 mAlarmSnoozeTime( other.mAlarmSnoozeTime ),
69 mAlarmRepeatCount( other.mAlarmRepeatCount ),
70 mOffset( other.mOffset ),
71 mEndOffset( other.mEndOffset ),
72 mHasTime( other.mHasTime ),
73 mAlarmEnabled( other.mAlarmEnabled )
74 {}
75
76 Incidence *mParent; // the incidence which this alarm belongs to
77
78 Type mType; // type of alarm
79 QString mDescription;// text to display/email body/procedure arguments
80 QString mFile; // program to run/optional audio file to play
81 QString mMailSubject;// subject of email
82 QStringList mMailAttachFiles; // filenames to attach to email
83 QList<Person> mMailAddresses; // who to mail for reminder
84
85 KDateTime mAlarmTime;// time at which to trigger the alarm
86 Duration mAlarmSnoozeTime; // how long after alarm to snooze before
87 // triggering again
88 int mAlarmRepeatCount;// number of times for alarm to repeat
89 // after the initial time
90
91 Duration mOffset; // time relative to incidence DTSTART
92 // to trigger the alarm
93 bool mEndOffset; // if true, mOffset relates to DTEND, not DTSTART
94 bool mHasTime; // use mAlarmTime, not mOffset
95 bool mAlarmEnabled;
96};
97//@endcond
98
99Alarm::Alarm( Incidence *parent ) : d( new KCal::Alarm::Private )
100{
101 d->mParent = parent;
102}
103
104Alarm::Alarm( const Alarm &other ) :
105 CustomProperties( other ), d( new KCal::Alarm::Private( *other.d ) )
106{
107}
108
109Alarm::~Alarm()
110{
111 delete d;
112}
113
114Alarm &Alarm::operator=( const Alarm &a )
115{
116 d->mParent = a.d->mParent;
117 d->mType = a.d->mType;
118 d->mDescription = a.d->mDescription;
119 d->mFile = a.d->mFile;
120 d->mMailAttachFiles = a.d->mMailAttachFiles;
121 d->mMailAddresses = a.d->mMailAddresses;
122 d->mMailSubject = a.d->mMailSubject;
123 d->mAlarmSnoozeTime = a.d->mAlarmSnoozeTime;
124 d->mAlarmRepeatCount = a.d->mAlarmRepeatCount;
125 d->mAlarmTime = a.d->mAlarmTime;
126 d->mOffset = a.d->mOffset;
127 d->mEndOffset = a.d->mEndOffset;
128 d->mHasTime = a.d->mHasTime;
129 d->mAlarmEnabled = a.d->mAlarmEnabled;
130 return *this;
131}
132
133bool Alarm::operator==( const Alarm &rhs ) const
134{
135 if ( d->mType != rhs.d->mType ||
136 d->mAlarmSnoozeTime != rhs.d->mAlarmSnoozeTime ||
137 d->mAlarmRepeatCount != rhs.d->mAlarmRepeatCount ||
138 d->mAlarmEnabled != rhs.d->mAlarmEnabled ||
139 d->mHasTime != rhs.d->mHasTime ) {
140 return false;
141 }
142
143 if ( d->mHasTime ) {
144 if ( d->mAlarmTime != rhs.d->mAlarmTime ) {
145 return false;
146 }
147 } else {
148 if ( d->mOffset != rhs.d->mOffset || d->mEndOffset != rhs.d->mEndOffset ) {
149 return false;
150 }
151 }
152
153 switch ( d->mType ) {
154 case Display:
155 return d->mDescription == rhs.d->mDescription;
156
157 case Email:
158 return d->mDescription == rhs.d->mDescription &&
159 d->mMailAttachFiles == rhs.d->mMailAttachFiles &&
160 d->mMailAddresses == rhs.d->mMailAddresses &&
161 d->mMailSubject == rhs.d->mMailSubject;
162
163 case Procedure:
164 return d->mFile == rhs.d->mFile &&
165 d->mDescription == rhs.d->mDescription;
166
167 case Audio:
168 return d->mFile == rhs.d->mFile;
169
170 case Invalid:
171 break;
172 }
173 return false;
174}
175
176void Alarm::setType( Alarm::Type type )
177{
178 if ( type == d->mType ) {
179 return;
180 }
181
182 switch ( type ) {
183 case Display:
184 d->mDescription = "";
185 break;
186 case Procedure:
187 d->mFile = d->mDescription = "";
188 break;
189 case Audio:
190 d->mFile = "";
191 break;
192 case Email:
193 d->mMailSubject = d->mDescription = "";
194 d->mMailAddresses.clear();
195 d->mMailAttachFiles.clear();
196 break;
197 case Invalid:
198 break;
199 default:
200 return;
201 }
202 d->mType = type;
203 if ( d->mParent ) {
204 d->mParent->updated();
205 }
206}
207
208Alarm::Type Alarm::type() const
209{
210 return d->mType;
211}
212
213void Alarm::setAudioAlarm( const QString &audioFile )
214{
215 d->mType = Audio;
216 d->mFile = audioFile;
217 if ( d->mParent ) {
218 d->mParent->updated();
219 }
220}
221
222void Alarm::setAudioFile( const QString &audioFile )
223{
224 if ( d->mType == Audio ) {
225 d->mFile = audioFile;
226 if ( d->mParent ) {
227 d->mParent->updated();
228 }
229 }
230}
231
232QString Alarm::audioFile() const
233{
234 return ( d->mType == Audio ) ? d->mFile : QString();
235}
236
237void Alarm::setProcedureAlarm( const QString &programFile,
238 const QString &arguments )
239{
240 d->mType = Procedure;
241 d->mFile = programFile;
242 d->mDescription = arguments;
243 if ( d->mParent ) {
244 d->mParent->updated();
245 }
246}
247
248void Alarm::setProgramFile( const QString &programFile )
249{
250 if ( d->mType == Procedure ) {
251 d->mFile = programFile;
252 if ( d->mParent ) {
253 d->mParent->updated();
254 }
255 }
256}
257
258QString Alarm::programFile() const
259{
260 return ( d->mType == Procedure ) ? d->mFile : QString();
261}
262
263void Alarm::setProgramArguments( const QString &arguments )
264{
265 if ( d->mType == Procedure ) {
266 d->mDescription = arguments;
267 if ( d->mParent ) {
268 d->mParent->updated();
269 }
270 }
271}
272
273QString Alarm::programArguments() const
274{
275 return ( d->mType == Procedure ) ? d->mDescription : QString();
276}
277
278void Alarm::setEmailAlarm( const QString &subject, const QString &text,
279 const QList<Person> &addressees,
280 const QStringList &attachments )
281{
282 d->mType = Email;
283 d->mMailSubject = subject;
284 d->mDescription = text;
285 d->mMailAddresses = addressees;
286 d->mMailAttachFiles = attachments;
287 if ( d->mParent ) {
288 d->mParent->updated();
289 }
290}
291
292void Alarm::setMailAddress( const Person &mailAddress )
293{
294 if ( d->mType == Email ) {
295 d->mMailAddresses.clear();
296 d->mMailAddresses += mailAddress;
297 if ( d->mParent ) {
298 d->mParent->updated();
299 }
300 }
301}
302
303void Alarm::setMailAddresses( const QList<Person> &mailAddresses )
304{
305 if ( d->mType == Email ) {
306 d->mMailAddresses = mailAddresses;
307 if ( d->mParent ) {
308 d->mParent->updated();
309 }
310 }
311}
312
313void Alarm::addMailAddress( const Person &mailAddress )
314{
315 if ( d->mType == Email ) {
316 d->mMailAddresses += mailAddress;
317 if ( d->mParent ) {
318 d->mParent->updated();
319 }
320 }
321}
322
323QList<Person> Alarm::mailAddresses() const
324{
325 return ( d->mType == Email ) ? d->mMailAddresses : QList<Person>();
326}
327
328void Alarm::setMailSubject( const QString &mailAlarmSubject )
329{
330 if ( d->mType == Email ) {
331 d->mMailSubject = mailAlarmSubject;
332 if ( d->mParent ) {
333 d->mParent->updated();
334 }
335 }
336}
337
338QString Alarm::mailSubject() const
339{
340 return ( d->mType == Email ) ? d->mMailSubject : QString();
341}
342
343void Alarm::setMailAttachment( const QString &mailAttachFile )
344{
345 if ( d->mType == Email ) {
346 d->mMailAttachFiles.clear();
347 d->mMailAttachFiles += mailAttachFile;
348 if ( d->mParent ) {
349 d->mParent->updated();
350 }
351 }
352}
353
354void Alarm::setMailAttachments( const QStringList &mailAttachFiles )
355{
356 if ( d->mType == Email ) {
357 d->mMailAttachFiles = mailAttachFiles;
358 if ( d->mParent ) {
359 d->mParent->updated();
360 }
361 }
362}
363
364void Alarm::addMailAttachment( const QString &mailAttachFile )
365{
366 if ( d->mType == Email ) {
367 d->mMailAttachFiles += mailAttachFile;
368 if ( d->mParent ) {
369 d->mParent->updated();
370 }
371 }
372}
373
374QStringList Alarm::mailAttachments() const
375{
376 return ( d->mType == Email ) ? d->mMailAttachFiles : QStringList();
377}
378
379void Alarm::setMailText( const QString &text )
380{
381 if ( d->mType == Email ) {
382 d->mDescription = text;
383 if ( d->mParent ) {
384 d->mParent->updated();
385 }
386 }
387}
388
389QString Alarm::mailText() const
390{
391 return ( d->mType == Email ) ? d->mDescription : QString();
392}
393
394void Alarm::setDisplayAlarm( const QString &text )
395{
396 d->mType = Display;
397 if ( !text.isNull() ) {
398 d->mDescription = text;
399 }
400 if ( d->mParent ) {
401 d->mParent->updated();
402 }
403}
404
405void Alarm::setText( const QString &text )
406{
407 if ( d->mType == Display ) {
408 d->mDescription = text;
409 if ( d->mParent ) {
410 d->mParent->updated();
411 }
412 }
413}
414
415QString Alarm::text() const
416{
417 return ( d->mType == Display ) ? d->mDescription : QString();
418}
419
420void Alarm::setTime( const KDateTime &alarmTime )
421{
422 d->mAlarmTime = alarmTime;
423 d->mHasTime = true;
424
425 if ( d->mParent ) {
426 d->mParent->updated();
427 }
428}
429
430KDateTime Alarm::time() const
431{
432 if ( hasTime() ) {
433 return d->mAlarmTime;
434 } else if ( d->mParent ) {
435 if ( d->mEndOffset ) {
436 if ( d->mParent->type() == "Todo" ) {
437 Todo *t = static_cast<Todo*>( d->mParent );
438 return d->mOffset.end( t->dtDue() );
439 } else {
440 return d->mOffset.end( d->mParent->dtEnd() );
441 }
442 } else {
443 return d->mOffset.end( d->mParent->dtStart() );
444 }
445 } else {
446 return KDateTime();
447 }
448}
449
450bool Alarm::hasTime() const
451{
452 return d->mHasTime;
453}
454
455void Alarm::shiftTimes( const KDateTime::Spec &oldSpec,
456 const KDateTime::Spec &newSpec )
457{
458 d->mAlarmTime = d->mAlarmTime.toTimeSpec( oldSpec );
459 d->mAlarmTime.setTimeSpec( newSpec );
460 if ( d->mParent ) {
461 d->mParent->updated();
462 }
463}
464
465void Alarm::setSnoozeTime( const Duration &alarmSnoozeTime )
466{
467 if ( alarmSnoozeTime.value() > 0 ) {
468 d->mAlarmSnoozeTime = alarmSnoozeTime;
469 if ( d->mParent ) {
470 d->mParent->updated();
471 }
472 }
473}
474
475Duration Alarm::snoozeTime() const
476{
477 return d->mAlarmSnoozeTime;
478}
479
480void Alarm::setRepeatCount( int alarmRepeatCount )
481{
482 d->mAlarmRepeatCount = alarmRepeatCount;
483 if ( d->mParent ) {
484 d->mParent->updated();
485 }
486}
487
488int Alarm::repeatCount() const
489{
490 return d->mAlarmRepeatCount;
491}
492
493Duration Alarm::duration() const
494{
495 return Duration( d->mAlarmSnoozeTime.value() * d->mAlarmRepeatCount,
496 d->mAlarmSnoozeTime.type() );
497}
498
499KDateTime Alarm::nextRepetition( const KDateTime &preTime ) const
500{
501 KDateTime at = time();
502 if ( at > preTime ) {
503 return at;
504 }
505 if ( !d->mAlarmRepeatCount ) {
506 // there isn't an occurrence after the specified time
507 return KDateTime();
508 }
509 qint64 repetition;
510 int interval = d->mAlarmSnoozeTime.value();
511 bool daily = d->mAlarmSnoozeTime.isDaily();
512 if ( daily ) {
513 int daysTo = at.daysTo( preTime );
514 if ( !preTime.isDateOnly() && preTime.time() <= at.time() ) {
515 --daysTo;
516 }
517 repetition = daysTo / interval + 1;
518 } else {
519 repetition = at.secsTo_long( preTime ) / interval + 1;
520 }
521 if ( repetition > d->mAlarmRepeatCount ) {
522 // all repetitions have finished before the specified time
523 return KDateTime();
524 }
525 return daily ? at.addDays( int( repetition * interval ) )
526 : at.addSecs( repetition * interval );
527}
528
529KDateTime Alarm::previousRepetition( const KDateTime &afterTime ) const
530{
531 KDateTime at = time();
532 if ( at >= afterTime ) {
533 // alarm's first/only time is at/after the specified time
534 return KDateTime();
535 }
536 if ( !d->mAlarmRepeatCount ) {
537 return at;
538 }
539 qint64 repetition;
540 int interval = d->mAlarmSnoozeTime.value();
541 bool daily = d->mAlarmSnoozeTime.isDaily();
542 if ( daily ) {
543 int daysTo = at.daysTo( afterTime );
544 if ( afterTime.isDateOnly() || afterTime.time() <= at.time() ) {
545 --daysTo;
546 }
547 repetition = daysTo / interval;
548 } else {
549 repetition = ( at.secsTo_long( afterTime ) - 1 ) / interval;
550 }
551 if ( repetition > d->mAlarmRepeatCount ) {
552 repetition = d->mAlarmRepeatCount;
553 }
554 return daily ? at.addDays( int( repetition * interval ) )
555 : at.addSecs( repetition * interval );
556}
557
558KDateTime Alarm::endTime() const
559{
560 if ( !d->mAlarmRepeatCount ) {
561 return time();
562 }
563 if ( d->mAlarmSnoozeTime.isDaily() ) {
564 return time().addDays( d->mAlarmRepeatCount * d->mAlarmSnoozeTime.asDays() );
565 } else {
566 return time().addSecs( d->mAlarmRepeatCount * d->mAlarmSnoozeTime.asSeconds() );
567 }
568}
569
570void Alarm::toggleAlarm()
571{
572 d->mAlarmEnabled = !d->mAlarmEnabled;
573 if ( d->mParent ) {
574 d->mParent->updated();
575 }
576}
577
578void Alarm::setEnabled( bool enable )
579{
580 d->mAlarmEnabled = enable;
581 if ( d->mParent ) {
582 d->mParent->updated();
583 }
584}
585
586bool Alarm::enabled() const
587{
588 return d->mAlarmEnabled;
589}
590
591void Alarm::setStartOffset( const Duration &offset )
592{
593 d->mOffset = offset;
594 d->mEndOffset = false;
595 d->mHasTime = false;
596 if ( d->mParent ) {
597 d->mParent->updated();
598 }
599}
600
601Duration Alarm::startOffset() const
602{
603 return ( d->mHasTime || d->mEndOffset ) ? Duration( 0 ) : d->mOffset;
604}
605
606bool Alarm::hasStartOffset() const
607{
608 return !d->mHasTime && !d->mEndOffset;
609}
610
611bool Alarm::hasEndOffset() const
612{
613 return !d->mHasTime && d->mEndOffset;
614}
615
616void Alarm::setEndOffset( const Duration &offset )
617{
618 d->mOffset = offset;
619 d->mEndOffset = true;
620 d->mHasTime = false;
621 if ( d->mParent ) {
622 d->mParent->updated();
623 }
624}
625
626Duration Alarm::endOffset() const
627{
628 return ( d->mHasTime || !d->mEndOffset ) ? Duration( 0 ) : d->mOffset;
629}
630
631void Alarm::setParent( Incidence *parent )
632{
633 d->mParent = parent;
634}
635
636Incidence *Alarm::parent() const
637{
638 return d->mParent;
639}
640
641void Alarm::customPropertyUpdated()
642{
643 if ( d->mParent ) {
644 d->mParent->updated();
645 }
646}
647