1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> |
5 | Copyright (C) 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 FreeBusy class. |
26 | |
27 | @brief |
28 | Provides information about the free/busy time of a calendar user. |
29 | |
30 | @author Cornelius Schumacher \<schumacher@kde.org\> |
31 | @author Reinhold Kainhofer \<reinhold@kainhofer.com\> |
32 | */ |
33 | |
34 | #include "freebusy.h" |
35 | #include "calendar.h" |
36 | #include "event.h" |
37 | |
38 | #include <kdebug.h> |
39 | #include <klocalizedstring.h> |
40 | |
41 | using namespace KCal; |
42 | |
43 | //@cond PRIVATE |
44 | class KCal::FreeBusy::Private |
45 | { |
46 | private: |
47 | FreeBusy *const q; |
48 | public: |
49 | Private( FreeBusy *qq ) : q( qq ) |
50 | {} |
51 | |
52 | Private( const KCal::FreeBusy::Private &other, FreeBusy *qq ) : q( qq ) |
53 | { init( other ); } |
54 | |
55 | Private( const FreeBusyPeriod::List &busyPeriods, FreeBusy *qq ) |
56 | : q( qq ), mBusyPeriods( busyPeriods ) |
57 | {} |
58 | |
59 | void init( const KCal::FreeBusy::Private &other ); |
60 | void init( const Event::List &events, const KDateTime &start, const KDateTime &end ); |
61 | |
62 | KDateTime mDtEnd; // end datetime |
63 | FreeBusyPeriod::List mBusyPeriods; // list of periods |
64 | |
65 | // This is used for creating a freebusy object for the current user |
66 | bool addLocalPeriod( FreeBusy *fb, const KDateTime &start, const KDateTime &end ); |
67 | }; |
68 | |
69 | void KCal::FreeBusy::Private::init( const KCal::FreeBusy::Private &other ) |
70 | { |
71 | mDtEnd = other.mDtEnd; |
72 | mBusyPeriods = other.mBusyPeriods; |
73 | } |
74 | //@endcond |
75 | |
76 | FreeBusy::FreeBusy() |
77 | : d( new KCal::FreeBusy::Private( this ) ) |
78 | { |
79 | } |
80 | |
81 | FreeBusy::FreeBusy( const FreeBusy &other ) |
82 | : IncidenceBase( other ), |
83 | d( new KCal::FreeBusy::Private( *other.d, this ) ) |
84 | { |
85 | } |
86 | |
87 | FreeBusy::FreeBusy( const KDateTime &start, const KDateTime &end ) |
88 | : d( new KCal::FreeBusy::Private( this ) ) |
89 | { |
90 | setDtStart( start ); |
91 | setDtEnd( end ); |
92 | } |
93 | |
94 | FreeBusy::FreeBusy( const Event::List &events, const KDateTime &start, const KDateTime &end ) |
95 | : d( new KCal::FreeBusy::Private( this ) ) |
96 | { |
97 | setDtStart( start ); |
98 | setDtEnd( end ); |
99 | |
100 | d->init( events, start, end ); |
101 | } |
102 | |
103 | void FreeBusy::Private::init( const Event::List &eventList, |
104 | const KDateTime &start, const KDateTime &end ) |
105 | { |
106 | int , i, x, duration; |
107 | duration = start.daysTo( end ); |
108 | QDate day; |
109 | KDateTime tmpStart; |
110 | KDateTime tmpEnd; |
111 | |
112 | // Loops through every event in the calendar |
113 | Event::List::ConstIterator it; |
114 | for ( it = eventList.constBegin(); it != eventList.constEnd(); ++it ) { |
115 | Event *event = *it; |
116 | |
117 | // If this event is transparent it shouldn't be in the freebusy list. |
118 | if ( event->transparency() == Event::Transparent ) { |
119 | continue; |
120 | } |
121 | |
122 | // The code below can not handle all-day events. Fixing this resulted |
123 | // in a lot of duplicated code. Instead, make a copy of the event and |
124 | // set the period to the full day(s). This trick works for recurring, |
125 | // multiday, and single day all-day events. |
126 | Event *allDayEvent = 0; |
127 | if ( event->allDay() ) { |
128 | // addDay event. Do the hack |
129 | kDebug() << "All-day event" ; |
130 | allDayEvent = new Event( *event ); |
131 | |
132 | // Set the start and end times to be on midnight |
133 | KDateTime st = allDayEvent->dtStart(); |
134 | st.setTime( QTime( 0, 0 ) ); |
135 | KDateTime nd = allDayEvent->dtEnd(); |
136 | nd.setTime( QTime( 23, 59, 59, 999 ) ); |
137 | allDayEvent->setAllDay( false ); |
138 | allDayEvent->setDtStart( st ); |
139 | allDayEvent->setDtEnd( nd ); |
140 | |
141 | kDebug() << "Use:" << st.toString() << "to" << nd.toString(); |
142 | // Finally, use this event for the setting below |
143 | event = allDayEvent; |
144 | } |
145 | |
146 | // This whole for loop is for recurring events, it loops through |
147 | // each of the days of the freebusy request |
148 | |
149 | for ( i = 0; i <= duration; ++i ) { |
150 | day = start.addDays(i).date(); |
151 | tmpStart.setDate( day ); |
152 | tmpEnd.setDate( day ); |
153 | |
154 | if ( event->recurs() ) { |
155 | if ( event->isMultiDay() ) { |
156 | // FIXME: This doesn't work for sub-daily recurrences or recurrences with |
157 | // a different time than the original event. |
158 | extraDays = event->dtStart().daysTo( event->dtEnd() ); |
159 | for ( x = 0; x <= extraDays; ++x ) { |
160 | if ( event->recursOn( day.addDays(-x), start.timeSpec() ) ) { |
161 | tmpStart.setDate( day.addDays(-x) ); |
162 | tmpStart.setTime( event->dtStart().time() ); |
163 | tmpEnd = event->duration().end( tmpStart ); |
164 | |
165 | addLocalPeriod( q, tmpStart, tmpEnd ); |
166 | break; |
167 | } |
168 | } |
169 | } else { |
170 | if ( event->recursOn( day, start.timeSpec() ) ) { |
171 | tmpStart.setTime( event->dtStart().time() ); |
172 | tmpEnd.setTime( event->dtEnd().time() ); |
173 | |
174 | addLocalPeriod ( q, tmpStart, tmpEnd ); |
175 | } |
176 | } |
177 | } |
178 | |
179 | } |
180 | // Non-recurring events |
181 | addLocalPeriod( q, event->dtStart(), event->dtEnd() ); |
182 | |
183 | // Clean up |
184 | delete allDayEvent; |
185 | } |
186 | |
187 | q->sortList(); |
188 | } |
189 | |
190 | FreeBusy::FreeBusy( Calendar *calendar, const KDateTime &start, const KDateTime &end ) |
191 | : d( new KCal::FreeBusy::Private( this ) ) |
192 | { |
193 | kDebug(); |
194 | |
195 | setDtStart( start ); |
196 | setDtEnd( end ); |
197 | |
198 | d->init( calendar ? calendar->rawEvents( start.date(), end.date() ) : Event::List(), start, end ); |
199 | } |
200 | |
201 | FreeBusy::FreeBusy( const Period::List &busyPeriods ) |
202 | : d( new KCal::FreeBusy::Private( this ) ) |
203 | { |
204 | addPeriods(busyPeriods); |
205 | } |
206 | |
207 | FreeBusy::FreeBusy( const FreeBusyPeriod::List &busyPeriods ) |
208 | : d( new KCal::FreeBusy::Private( busyPeriods, this ) ) |
209 | { |
210 | } |
211 | |
212 | FreeBusy::~FreeBusy() |
213 | { |
214 | delete d; |
215 | } |
216 | |
217 | QByteArray FreeBusy::type() const |
218 | { |
219 | return "FreeBusy" ; |
220 | } |
221 | |
222 | //KDE5: |
223 | //QString FreeBusy::typeStr() const |
224 | //{ |
225 | // return i18nc( "incidence type is freebusy", "free/busy" ); |
226 | //} |
227 | |
228 | void FreeBusy::setDtStart( const KDateTime &start ) |
229 | { |
230 | IncidenceBase::setDtStart( start.toUtc() ); |
231 | updated(); |
232 | } |
233 | |
234 | void FreeBusy::setDtEnd( const KDateTime &end ) |
235 | { |
236 | d->mDtEnd = end; |
237 | } |
238 | |
239 | KDateTime FreeBusy::dtEnd() const |
240 | { |
241 | return d->mDtEnd; |
242 | } |
243 | |
244 | Period::List FreeBusy::busyPeriods() const |
245 | { |
246 | Period::List res; |
247 | |
248 | foreach ( const FreeBusyPeriod &p, d->mBusyPeriods ) { |
249 | res << p; |
250 | } |
251 | |
252 | return res; |
253 | } |
254 | |
255 | FreeBusyPeriod::List FreeBusy::fullBusyPeriods() const |
256 | { |
257 | return d->mBusyPeriods; |
258 | } |
259 | |
260 | void FreeBusy::sortList() |
261 | { |
262 | qSort( d->mBusyPeriods ); |
263 | return; |
264 | } |
265 | |
266 | void FreeBusy::addPeriods( const Period::List &list ) |
267 | { |
268 | foreach ( const Period &p, list ) { |
269 | d->mBusyPeriods << FreeBusyPeriod( p ); |
270 | } |
271 | sortList(); |
272 | } |
273 | |
274 | void FreeBusy::addPeriods( const FreeBusyPeriod::List &list ) |
275 | { |
276 | d->mBusyPeriods += list; |
277 | sortList(); |
278 | } |
279 | |
280 | void FreeBusy::addPeriod( const KDateTime &start, const KDateTime &end ) |
281 | { |
282 | d->mBusyPeriods.append( FreeBusyPeriod( start, end ) ); |
283 | sortList(); |
284 | } |
285 | |
286 | void FreeBusy::addPeriod( const KDateTime &start, const Duration &duration ) |
287 | { |
288 | d->mBusyPeriods.append( FreeBusyPeriod( start, duration ) ); |
289 | sortList(); |
290 | } |
291 | |
292 | void FreeBusy::merge( FreeBusy *freeBusy ) |
293 | { |
294 | if ( freeBusy->dtStart() < dtStart() ) { |
295 | setDtStart( freeBusy->dtStart() ); |
296 | } |
297 | |
298 | if ( freeBusy->dtEnd() > dtEnd() ) { |
299 | setDtEnd( freeBusy->dtEnd() ); |
300 | } |
301 | |
302 | Period::List periods = freeBusy->busyPeriods(); |
303 | Period::List::ConstIterator it; |
304 | for ( it = periods.constBegin(); it != periods.constEnd(); ++it ) { |
305 | d->mBusyPeriods.append( FreeBusyPeriod( (*it).start(), (*it).end() ) ); |
306 | } |
307 | sortList(); |
308 | } |
309 | |
310 | void FreeBusy::shiftTimes( const KDateTime::Spec &oldSpec, |
311 | const KDateTime::Spec &newSpec ) |
312 | { |
313 | if ( oldSpec.isValid() && newSpec.isValid() && oldSpec != newSpec ) { |
314 | IncidenceBase::shiftTimes( oldSpec, newSpec ); |
315 | d->mDtEnd = d->mDtEnd.toTimeSpec( oldSpec ); |
316 | d->mDtEnd.setTimeSpec( newSpec ); |
317 | foreach ( FreeBusyPeriod p, d->mBusyPeriods ) { //krazy:exclude=foreach |
318 | p.shiftTimes( oldSpec, newSpec ); |
319 | } |
320 | } |
321 | } |
322 | |
323 | FreeBusy &FreeBusy::operator=( const FreeBusy &other ) |
324 | { |
325 | // check for self assignment |
326 | if ( &other == this ) { |
327 | return *this; |
328 | } |
329 | |
330 | IncidenceBase::operator=( other ); |
331 | d->init( *other.d ); |
332 | return *this; |
333 | } |
334 | |
335 | bool FreeBusy::operator==( const FreeBusy &freebusy ) const |
336 | { |
337 | return |
338 | IncidenceBase::operator==( freebusy ) && |
339 | dtEnd() == freebusy.dtEnd() && |
340 | d->mBusyPeriods == freebusy.d->mBusyPeriods; |
341 | } |
342 | |
343 | //@cond PRIVATE |
344 | bool FreeBusy::Private::addLocalPeriod( FreeBusy *fb, |
345 | const KDateTime &eventStart, |
346 | const KDateTime &eventEnd ) |
347 | { |
348 | KDateTime tmpStart; |
349 | KDateTime tmpEnd; |
350 | |
351 | //Check to see if the start *or* end of the event is |
352 | //between the start and end of the freebusy dates. |
353 | KDateTime start = fb->dtStart(); |
354 | if ( !( ( ( start.secsTo(eventStart) >= 0 ) && |
355 | ( eventStart.secsTo(mDtEnd) >= 0 ) ) || |
356 | ( ( start.secsTo(eventEnd) >= 0 ) && |
357 | ( eventEnd.secsTo(mDtEnd) >= 0 ) ) ) ) { |
358 | return false; |
359 | } |
360 | |
361 | if ( eventStart.secsTo( start ) >= 0 ) { |
362 | tmpStart = start; |
363 | } else { |
364 | tmpStart = eventStart; |
365 | } |
366 | |
367 | if ( eventEnd.secsTo( mDtEnd ) <= 0 ) { |
368 | tmpEnd = mDtEnd; |
369 | } else { |
370 | tmpEnd = eventEnd; |
371 | } |
372 | |
373 | FreeBusyPeriod p( tmpStart, tmpEnd ); |
374 | mBusyPeriods.append( p ); |
375 | |
376 | return true; |
377 | } |
378 | //@endcond |
379 | |