1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 2001,2004 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 IncidenceBase class. |
26 | |
27 | @brief |
28 | An abstract base class that provides a common base for all calendar incidence |
29 | classes. |
30 | |
31 | @author Cornelius Schumacher \<schumacher@kde.org\> |
32 | @author Reinhold Kainhofer \<reinhold@kainhofer.com\> |
33 | */ |
34 | |
35 | #include "incidencebase.h" |
36 | #include "calformat.h" |
37 | #include "incidenceformatter.h" |
38 | |
39 | #include <kglobal.h> |
40 | #include <klocale.h> |
41 | #include <klocalizedstring.h> |
42 | #include <kdebug.h> |
43 | #include <kurl.h> |
44 | #include <ksystemtimezone.h> |
45 | |
46 | #include <QtCore/QList> |
47 | |
48 | using namespace KCal; |
49 | |
50 | /** |
51 | Private class that helps to provide binary compatibility between releases. |
52 | @internal |
53 | */ |
54 | //@cond PRIVATE |
55 | class KCal::IncidenceBase::Private |
56 | { |
57 | public: |
58 | Private() |
59 | : mUpdateGroupLevel( 0 ), |
60 | mUpdatedPending( false ), |
61 | mAllDay( true ), |
62 | mHasDuration( false ) |
63 | { mAttendees.setAutoDelete( true ); } |
64 | |
65 | Private( const Private &other ) |
66 | : mUpdateGroupLevel( 0 ), |
67 | mUpdatedPending( false ), |
68 | mAllDay( true ), |
69 | mHasDuration( false ) |
70 | { |
71 | mAttendees.setAutoDelete( true ); |
72 | init( other ); |
73 | } |
74 | |
75 | void init( const Private &other ); |
76 | |
77 | KDateTime mLastModified; // incidence last modified date |
78 | KDateTime mDtStart; // incidence start time |
79 | Person mOrganizer; // incidence person (owner) |
80 | QString mUid; // incidence unique id |
81 | Duration mDuration; // incidence duration |
82 | int mUpdateGroupLevel; // if non-zero, suppresses update() calls |
83 | bool mUpdatedPending; // true if an update has occurred since startUpdates() |
84 | bool mAllDay; // true if the incidence is all-day |
85 | bool mHasDuration; // true if the incidence has a duration |
86 | |
87 | Attendee::List mAttendees; // list of incidence attendees |
88 | QStringList ; // list of incidence comments |
89 | QList<IncidenceObserver*> mObservers; // list of incidence observers |
90 | }; |
91 | |
92 | void IncidenceBase::Private::init( const Private &other ) |
93 | { |
94 | mLastModified = other.mLastModified; |
95 | mDtStart = other.mDtStart; |
96 | mOrganizer = other.mOrganizer; |
97 | mUid = other.mUid; |
98 | mDuration = other.mDuration; |
99 | mAllDay = other.mAllDay; |
100 | mHasDuration = other.mHasDuration; |
101 | mComments = other.mComments; |
102 | |
103 | mAttendees.clearAll(); |
104 | Attendee::List::ConstIterator it; |
105 | for ( it = other.mAttendees.constBegin(); it != other.mAttendees.constEnd(); ++it ) { |
106 | mAttendees.append( new Attendee( *(*it) ) ); |
107 | } |
108 | } |
109 | //@endcond |
110 | |
111 | IncidenceBase::IncidenceBase() |
112 | : d( new KCal::IncidenceBase::Private ) |
113 | { |
114 | mReadOnly = false; |
115 | |
116 | setUid( CalFormat::createUniqueId() ); |
117 | } |
118 | |
119 | IncidenceBase::IncidenceBase( const IncidenceBase &i ) |
120 | : CustomProperties( i ), |
121 | d( new KCal::IncidenceBase::Private( *i.d ) ) |
122 | { |
123 | mReadOnly = i.mReadOnly; |
124 | } |
125 | |
126 | IncidenceBase::~IncidenceBase() |
127 | { |
128 | delete d; |
129 | } |
130 | |
131 | IncidenceBase &IncidenceBase::operator=( const IncidenceBase &other ) |
132 | { |
133 | // check for self assignment |
134 | if ( &other == this ) { |
135 | return *this; |
136 | } |
137 | |
138 | CustomProperties::operator=( other ); |
139 | d->init( *other.d ); |
140 | mReadOnly = other.mReadOnly; |
141 | return *this; |
142 | } |
143 | |
144 | bool IncidenceBase::operator==( const IncidenceBase &i2 ) const |
145 | { |
146 | if ( attendees().count() != i2.attendees().count() ) { |
147 | return false; // no need to check further |
148 | } |
149 | |
150 | Attendee::List al1 = attendees(); |
151 | Attendee::List al2 = i2.attendees(); |
152 | Attendee::List::ConstIterator a1 = al1.constBegin(); |
153 | Attendee::List::ConstIterator a2 = al2.constBegin(); |
154 | //TODO Does the order of attendees in the list really matter? |
155 | //Please delete this comment if you know it's ok, kthx |
156 | for ( ; a1 != al1.constEnd() && a2 != al2.constEnd(); ++a1, ++a2 ) { |
157 | if ( !( **a1 == **a2 ) ) { |
158 | return false; |
159 | } |
160 | } |
161 | |
162 | if ( !CustomProperties::operator == (i2) ) { |
163 | return false; |
164 | } |
165 | |
166 | return |
167 | dtStart() == i2.dtStart() && |
168 | organizer() == i2.organizer() && |
169 | uid() == i2.uid() && |
170 | // Don't compare lastModified, otherwise the operator is not |
171 | // of much use. We are not comparing for identity, after all. |
172 | allDay() == i2.allDay() && |
173 | duration() == i2.duration() && |
174 | hasDuration() == i2.hasDuration(); |
175 | // no need to compare mObserver |
176 | } |
177 | |
178 | void IncidenceBase::setUid( const QString &uid ) |
179 | { |
180 | d->mUid = uid; |
181 | updated(); |
182 | } |
183 | |
184 | QString IncidenceBase::uid() const |
185 | { |
186 | return d->mUid; |
187 | } |
188 | |
189 | void IncidenceBase::setLastModified( const KDateTime &lm ) |
190 | { |
191 | // DON'T! updated() because we call this from |
192 | // Calendar::updateEvent(). |
193 | |
194 | // Convert to UTC and remove milliseconds part. |
195 | KDateTime current = lm.toUtc(); |
196 | QTime t = current.time(); |
197 | t.setHMS( t.hour(), t.minute(), t.second(), 0 ); |
198 | current.setTime( t ); |
199 | |
200 | d->mLastModified = current; |
201 | } |
202 | |
203 | KDateTime IncidenceBase::lastModified() const |
204 | { |
205 | return d->mLastModified; |
206 | } |
207 | |
208 | void IncidenceBase::setOrganizer( const Person &o ) |
209 | { |
210 | // we don't check for readonly here, because it is |
211 | // possible that by setting the organizer we are changing |
212 | // the event's readonly status... |
213 | d->mOrganizer = o; |
214 | |
215 | updated(); |
216 | } |
217 | |
218 | void IncidenceBase::setOrganizer( const QString &o ) |
219 | { |
220 | QString mail( o ); |
221 | if ( mail.startsWith( QLatin1String( "MAILTO:" ), Qt::CaseInsensitive ) ) { |
222 | mail = mail.remove( 0, 7 ); |
223 | } |
224 | |
225 | // split the string into full name plus email. |
226 | const Person organizer = Person::fromFullName( mail ); |
227 | setOrganizer( organizer ); |
228 | } |
229 | |
230 | Person IncidenceBase::organizer() const |
231 | { |
232 | return d->mOrganizer; |
233 | } |
234 | |
235 | void IncidenceBase::setReadOnly( bool readOnly ) |
236 | { |
237 | mReadOnly = readOnly; |
238 | } |
239 | |
240 | void IncidenceBase::setDtStart( const KDateTime &dtStart ) |
241 | { |
242 | // if ( mReadOnly ) return; |
243 | d->mDtStart = dtStart; |
244 | d->mAllDay = dtStart.isDateOnly(); |
245 | updated(); |
246 | } |
247 | |
248 | KDateTime IncidenceBase::dtStart() const |
249 | { |
250 | return d->mDtStart; |
251 | } |
252 | |
253 | QString IncidenceBase::dtStartTimeStr( bool shortfmt, const KDateTime::Spec &spec ) const |
254 | { |
255 | if ( spec.isValid() ) { |
256 | |
257 | QString timeZone; |
258 | if ( spec.timeZone() != KSystemTimeZones::local() ) { |
259 | timeZone = ' ' + spec.timeZone().name(); |
260 | } |
261 | |
262 | return KGlobal::locale()->formatTime( |
263 | dtStart().toTimeSpec( spec ).time(), !shortfmt ) + timeZone; |
264 | } else { |
265 | return KGlobal::locale()->formatTime( dtStart().time(), !shortfmt ); |
266 | } |
267 | } |
268 | |
269 | QString IncidenceBase::dtStartDateStr( bool shortfmt, const KDateTime::Spec &spec ) const |
270 | { |
271 | if ( spec.isValid() ) { |
272 | |
273 | QString timeZone; |
274 | if ( spec.timeZone() != KSystemTimeZones::local() ) { |
275 | timeZone = ' ' + spec.timeZone().name(); |
276 | } |
277 | |
278 | return KGlobal::locale()->formatDate( |
279 | dtStart().toTimeSpec( spec ).date(), |
280 | ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; |
281 | } else { |
282 | return KGlobal::locale()->formatDate( |
283 | dtStart().date(), ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); |
284 | } |
285 | } |
286 | |
287 | QString IncidenceBase::dtStartStr( bool shortfmt, const KDateTime::Spec &spec ) const |
288 | { |
289 | if ( allDay() ) { |
290 | return IncidenceFormatter::dateToString( dtStart(), shortfmt, spec ); |
291 | } |
292 | |
293 | if ( spec.isValid() ) { |
294 | |
295 | QString timeZone; |
296 | if ( spec.timeZone() != KSystemTimeZones::local() ) { |
297 | timeZone = ' ' + spec.timeZone().name(); |
298 | } |
299 | |
300 | return KGlobal::locale()->formatDateTime( |
301 | dtStart().toTimeSpec( spec ).dateTime(), |
302 | ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; |
303 | } else { |
304 | return KGlobal::locale()->formatDateTime( |
305 | dtStart().dateTime(), |
306 | ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); |
307 | } |
308 | } |
309 | |
310 | bool IncidenceBase::allDay() const |
311 | { |
312 | return d->mAllDay; |
313 | } |
314 | |
315 | void IncidenceBase::setAllDay( bool f ) |
316 | { |
317 | if ( mReadOnly || f == d->mAllDay ) { |
318 | return; |
319 | } |
320 | d->mAllDay = f; |
321 | updated(); |
322 | } |
323 | |
324 | void IncidenceBase::shiftTimes( const KDateTime::Spec &oldSpec, |
325 | const KDateTime::Spec &newSpec ) |
326 | { |
327 | d->mDtStart = d->mDtStart.toTimeSpec( oldSpec ); |
328 | d->mDtStart.setTimeSpec( newSpec ); |
329 | updated(); |
330 | } |
331 | |
332 | void IncidenceBase::( const QString & ) |
333 | { |
334 | d->mComments += comment; |
335 | } |
336 | |
337 | bool IncidenceBase::( const QString & ) |
338 | { |
339 | bool found = false; |
340 | QStringList::Iterator i; |
341 | |
342 | for ( i = d->mComments.begin(); !found && i != d->mComments.end(); ++i ) { |
343 | if ( (*i) == comment ) { |
344 | found = true; |
345 | d->mComments.erase( i ); |
346 | } |
347 | } |
348 | |
349 | return found; |
350 | } |
351 | |
352 | void IncidenceBase::() |
353 | { |
354 | d->mComments.clear(); |
355 | } |
356 | |
357 | QStringList IncidenceBase::() const |
358 | { |
359 | return d->mComments; |
360 | } |
361 | |
362 | void IncidenceBase::addAttendee( Attendee *a, bool doupdate ) |
363 | { |
364 | if ( !a || mReadOnly ) { |
365 | return; |
366 | } |
367 | |
368 | if ( a->name().left(7).toUpper() == "MAILTO:" ) { |
369 | a->setName( a->name().remove( 0, 7 ) ); |
370 | } |
371 | |
372 | d->mAttendees.append( a ); |
373 | if ( doupdate ) { |
374 | updated(); |
375 | } |
376 | } |
377 | |
378 | const Attendee::List &IncidenceBase::attendees() const |
379 | { |
380 | return d->mAttendees; |
381 | } |
382 | |
383 | int IncidenceBase::attendeeCount() const |
384 | { |
385 | return d->mAttendees.count(); |
386 | } |
387 | |
388 | void IncidenceBase::clearAttendees() |
389 | { |
390 | if ( mReadOnly ) { |
391 | return; |
392 | } |
393 | qDeleteAll( d->mAttendees ); |
394 | d->mAttendees.clear(); |
395 | } |
396 | |
397 | Attendee *IncidenceBase::attendeeByMail( const QString &email ) const |
398 | { |
399 | Attendee::List::ConstIterator it; |
400 | for ( it = d->mAttendees.constBegin(); it != d->mAttendees.constEnd(); ++it ) { |
401 | if ( (*it)->email() == email ) { |
402 | return *it; |
403 | } |
404 | } |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | Attendee *IncidenceBase::attendeeByMails( const QStringList &emails, |
410 | const QString &email ) const |
411 | { |
412 | QStringList mails = emails; |
413 | if ( !email.isEmpty() ) { |
414 | mails.append( email ); |
415 | } |
416 | |
417 | Attendee::List::ConstIterator itA; |
418 | for ( itA = d->mAttendees.constBegin(); itA != d->mAttendees.constEnd(); ++itA ) { |
419 | for ( QStringList::const_iterator it = mails.constBegin(); it != mails.constEnd(); ++it ) { |
420 | if ( (*itA)->email() == (*it) ) { |
421 | return *itA; |
422 | } |
423 | } |
424 | } |
425 | |
426 | return 0; |
427 | } |
428 | |
429 | Attendee *IncidenceBase::attendeeByUid( const QString &uid ) const |
430 | { |
431 | Attendee::List::ConstIterator it; |
432 | for ( it = d->mAttendees.constBegin(); it != d->mAttendees.constEnd(); ++it ) { |
433 | if ( (*it)->uid() == uid ) { |
434 | return *it; |
435 | } |
436 | } |
437 | |
438 | return 0; |
439 | } |
440 | |
441 | void IncidenceBase::setDuration( const Duration &duration ) |
442 | { |
443 | d->mDuration = duration; |
444 | setHasDuration( true ); |
445 | updated(); |
446 | } |
447 | |
448 | Duration IncidenceBase::duration() const |
449 | { |
450 | return d->mDuration; |
451 | } |
452 | |
453 | void IncidenceBase::setHasDuration( bool hasDuration ) |
454 | { |
455 | d->mHasDuration = hasDuration; |
456 | } |
457 | |
458 | bool IncidenceBase::hasDuration() const |
459 | { |
460 | return d->mHasDuration; |
461 | } |
462 | |
463 | void IncidenceBase::registerObserver( IncidenceBase::IncidenceObserver *observer ) |
464 | { |
465 | if ( !d->mObservers.contains( observer ) ) { |
466 | d->mObservers.append( observer ); |
467 | } |
468 | } |
469 | |
470 | void IncidenceBase::unRegisterObserver( IncidenceBase::IncidenceObserver *observer ) |
471 | { |
472 | d->mObservers.removeAll( observer ); |
473 | } |
474 | |
475 | void IncidenceBase::updated() |
476 | { |
477 | if ( d->mUpdateGroupLevel ) { |
478 | d->mUpdatedPending = true; |
479 | } else { |
480 | foreach ( IncidenceObserver *o, d->mObservers ) { |
481 | if ( o ) { |
482 | o->incidenceUpdated( this ); |
483 | } |
484 | } |
485 | } |
486 | } |
487 | |
488 | void IncidenceBase::startUpdates() |
489 | { |
490 | ++d->mUpdateGroupLevel; |
491 | } |
492 | |
493 | void IncidenceBase::endUpdates() |
494 | { |
495 | if ( d->mUpdateGroupLevel > 0 ) { |
496 | if ( --d->mUpdateGroupLevel == 0 && d->mUpdatedPending ) { |
497 | d->mUpdatedPending = false; |
498 | updated(); |
499 | } |
500 | } |
501 | } |
502 | |
503 | void IncidenceBase::customPropertyUpdated() |
504 | { |
505 | updated(); |
506 | } |
507 | |
508 | KUrl IncidenceBase::uri() const |
509 | { |
510 | return KUrl( QString( "urn:x-ical:" ) + uid() ); |
511 | } |
512 | |
513 | bool IncidenceBase::Visitor::visit( Event *event ) |
514 | { |
515 | Q_UNUSED( event ); |
516 | return false; |
517 | } |
518 | |
519 | bool IncidenceBase::Visitor::visit( Todo *todo ) |
520 | { |
521 | Q_UNUSED( todo ); |
522 | return false; |
523 | } |
524 | |
525 | bool IncidenceBase::Visitor::visit( Journal *journal ) |
526 | { |
527 | Q_UNUSED( journal ); |
528 | return false; |
529 | } |
530 | |
531 | bool IncidenceBase::Visitor::visit( FreeBusy *freebusy ) |
532 | { |
533 | Q_UNUSED( freebusy ); |
534 | return false; |
535 | } |
536 | |