summaryrefslogtreecommitdiff
path: root/library/datebookdb.cpp
Unidiff
Diffstat (limited to 'library/datebookdb.cpp') (more/less context) (show whitespace changes)
-rw-r--r--library/datebookdb.cpp1121
1 files changed, 1121 insertions, 0 deletions
diff --git a/library/datebookdb.cpp b/library/datebookdb.cpp
new file mode 100644
index 0000000..bf7fd94
--- a/dev/null
+++ b/library/datebookdb.cpp
@@ -0,0 +1,1121 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <qasciidict.h>
22#include <qfile.h>
23#include <qmessagebox.h>
24#include <qstring.h>
25#include <qtextcodec.h>
26#include <qtextstream.h>
27#include <qtl.h>
28
29#include <qpe/alarmserver.h>
30#include <qpe/global.h>
31#include "datebookdb.h"
32#include <qpe/stringutil.h>
33#include <qpe/timeconversion.h>
34
35#include <errno.h>
36#include <stdlib.h>
37
38
39class DateBookDBPrivate
40{
41public:
42 bool clean; // indcate whether we need to write to disk...
43};
44
45
46// Helper functions
47
48static QString dateBookJournalFile()
49{
50 QString str = getenv("HOME");
51 return QString( str +"/.caljournal" );
52}
53
54static QString dateBookFilename()
55{
56 return Global::applicationFileName("datebook","datebook.xml");
57}
58
59/* Calculating the next event of a recuring event is actually
60 computationally inexpensive, esp. compared to checking each day
61 individually. There are bad worse cases for say the 29th of
62 february or the 31st of some other months. However
63 these are still bounded */
64bool nextOccurance(const Event &e, const QDate &from, QDateTime &next)
65{
66 // easy checks, first are we too far in the future or too far in the past?
67 QDate tmpDate;
68 int freq = e.repeatPattern().frequency;
69 int diff, diff2, a;
70 int iday, imonth, iyear;
71 int dayOfWeek = 0;
72 int firstOfWeek = 0;
73 int weekOfMonth;
74
75
76 if (e.repeatPattern().hasEndDate && e.repeatPattern().endDate() < from)
77 return FALSE;
78
79 if (e.start() >= from) {
80 next = e.start();
81 return TRUE;
82 }
83
84 switch ( e.repeatPattern().type ) {
85 case Event::Weekly:
86 /* weekly is just daily by 7 */
87 /* first convert the repeatPattern.Days() mask to the next
88 day of week valid after from */
89 dayOfWeek = from.dayOfWeek();
90 dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */
91
92 /* this is done in case freq > 1 and from in week not
93 for this round */
94 // firstOfWeek = 0; this is already done at decl.
95 while(!((1 << firstOfWeek) & e.repeatPattern().days))
96 firstOfWeek++;
97
98 /* there is at least one 'day', or there would be no event */
99 while(!((1 << (dayOfWeek % 7)) & e.repeatPattern().days))
100 dayOfWeek++;
101
102 dayOfWeek = dayOfWeek % 7; /* the actual day of week */
103 dayOfWeek -= e.start().date().dayOfWeek() -1;
104
105 firstOfWeek = firstOfWeek % 7; /* the actual first of week */
106 firstOfWeek -= e.start().date().dayOfWeek() -1;
107
108 // dayOfWeek may be negitive now
109 // day of week is number of days to add to start day
110
111 freq *= 7;
112 // FALL-THROUGH !!!!!
113 case Event::Daily:
114 // the add is for the possible fall through from weekly */
115 if(e.start().date().addDays(dayOfWeek) > from) {
116 /* first week exception */
117 next = QDateTime(e.start().date().addDays(dayOfWeek),
118 e.start().time());
119 if ((next.date() > e.repeatPattern().endDate())
120 && e.repeatPattern().hasEndDate)
121 return FALSE;
122 return TRUE;
123 }
124 /* if from is middle of a non-week */
125
126 diff = e.start().date().addDays(dayOfWeek).daysTo(from) % freq;
127 diff2 = e.start().date().addDays(firstOfWeek).daysTo(from) % freq;
128
129 if(diff != 0)
130 diff = freq - diff;
131 if(diff2 != 0)
132 diff2 = freq - diff2;
133 diff = QMIN(diff, diff2);
134
135 next = QDateTime(from.addDays(diff), e.start().time());
136 if ( (next.date() > e.repeatPattern().endDate())
137 && e.repeatPattern().hasEndDate )
138 return FALSE;
139 return TRUE;
140 case Event::MonthlyDay:
141 iday = from.day();
142 iyear = from.year();
143 imonth = from.month();
144 /* find equivelent day of month for this month */
145 dayOfWeek = e.start().date().dayOfWeek();
146 weekOfMonth = (e.start().date().day() - 1) / 7;
147
148 /* work out when the next valid month is */
149 a = from.year() - e.start().date().year();
150 a *= 12;
151 a = a + (imonth - e.start().date().month());
152 /* a is e.start()monthsFrom(from); */
153 if(a % freq) {
154 a = freq - (a % freq);
155 imonth = from.month() + a;
156 if (imonth > 12) {
157 imonth--;
158 iyear += imonth / 12;
159 imonth = imonth % 12;
160 imonth++;
161 }
162 }
163 /* imonth is now the first month after or on
164 from that matches the frequency given */
165
166 /* find for this month */
167 tmpDate = QDate( iyear, imonth, 1 );
168
169 iday = 1;
170 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
171 iday += 7 * weekOfMonth;
172 while (iday > tmpDate.daysInMonth()) {
173 imonth += freq;
174 if (imonth > 12) {
175 imonth--;
176 iyear += imonth / 12;
177 imonth = imonth % 12;
178 imonth++;
179 }
180 tmpDate = QDate( iyear, imonth, 1 );
181 /* these loops could go for a while, check end case now */
182 if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
183 return FALSE;
184 iday = 1;
185 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
186 iday += 7 * weekOfMonth;
187 }
188 tmpDate = QDate(iyear, imonth, iday);
189
190 if (tmpDate >= from) {
191 next = QDateTime(tmpDate, e.start().time());
192 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
193 return FALSE;
194 return TRUE;
195 }
196
197 /* need to find the next iteration */
198 do {
199 imonth += freq;
200 if (imonth > 12) {
201 imonth--;
202 iyear += imonth / 12;
203 imonth = imonth % 12;
204 imonth++;
205 }
206 tmpDate = QDate( iyear, imonth, 1 );
207 /* these loops could go for a while, check end case now */
208 if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
209 return FALSE;
210 iday = 1;
211 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
212 iday += 7 * weekOfMonth;
213 } while (iday > tmpDate.daysInMonth());
214 tmpDate = QDate(iyear, imonth, iday);
215
216 next = QDateTime(tmpDate, e.start().time());
217 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
218 return FALSE;
219 return TRUE;
220 case Event::MonthlyDate:
221 iday = e.start().date().day();
222 iyear = from.year();
223 imonth = from.month();
224
225 a = from.year() - e.start().date().year();
226 a *= 12;
227 a = a + (imonth - e.start().date().month());
228 /* a is e.start()monthsFrom(from); */
229 if(a % freq) {
230 a = freq - (a % freq);
231 imonth = from.month() + a;
232 if (imonth > 12) {
233 imonth--;
234 iyear += imonth / 12;
235 imonth = imonth % 12;
236 imonth++;
237 }
238 }
239 /* imonth is now the first month after or on
240 from that matches the frequencey given */
241
242 /* this could go for a while, worse case, 4*12 iterations, probably */
243 while(!QDate::isValid(iyear, imonth, iday) ) {
244 imonth += freq;
245 if (imonth > 12) {
246 imonth--;
247 iyear += imonth / 12;
248 imonth = imonth % 12;
249 imonth++;
250 }
251 /* these loops could go for a while, check end case now */
252 if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
253 return FALSE;
254 }
255
256 if(QDate(iyear, imonth, iday) >= from) {
257 /* done */
258 next = QDateTime(QDate(iyear, imonth, iday),
259 e.start().time());
260 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
261 return FALSE;
262 return TRUE;
263 }
264
265 /* ok, need to cycle */
266 imonth += freq;
267 imonth--;
268 iyear += imonth / 12;
269 imonth = imonth % 12;
270 imonth++;
271
272 while(!QDate::isValid(iyear, imonth, iday) ) {
273 imonth += freq;
274 imonth--;
275 iyear += imonth / 12;
276 imonth = imonth % 12;
277 imonth++;
278 if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
279 return FALSE;
280 }
281
282 next = QDateTime(QDate(iyear, imonth, iday), e.start().time());
283 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
284 return FALSE;
285 return TRUE;
286 case Event::Yearly:
287 iday = e.start().date().day();
288 imonth = e.start().date().month();
289 iyear = from.year(); // after all, we want to start in this year
290
291 diff = 1;
292 if(imonth == 2 && iday > 28) {
293 /* leap year, and it counts, calculate actual frequency */
294 if(freq % 4)
295 if (freq % 2)
296 freq = freq * 4;
297 else
298 freq = freq * 2;
299 /* else divides by 4 already, leave freq alone */
300 diff = 4;
301 }
302
303 a = from.year() - e.start().date().year();
304 if(a % freq) {
305 a = freq - (a % freq);
306 iyear = iyear + a;
307 }
308
309 /* under the assumption we won't hit one of the special not-leap years twice */
310 if(!QDate::isValid(iyear, imonth, iday)) {
311 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
312 iyear += freq;
313 }
314
315 if(QDate(iyear, imonth, iday) >= from) {
316 next = QDateTime(QDate(iyear, imonth, iday),
317 e.start().time());
318 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
319 return FALSE;
320 return TRUE;
321 }
322 /* iyear == from.year(), need to advance again */
323 iyear += freq;
324 /* under the assumption we won't hit one of the special not-leap years twice */
325 if(!QDate::isValid(iyear, imonth, iday)) {
326 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
327 iyear += freq;
328 }
329
330 next = QDateTime(QDate(iyear, imonth, iday), e.start().time());
331 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
332 return FALSE;
333 return TRUE;
334 default:
335 return FALSE;
336 }
337}
338
339static bool nextAlarm( const Event &ev, QDateTime& when, int& warn)
340{
341 QDateTime now = QDateTime::currentDateTime();
342 if ( ev.hasRepeat() ) {
343 QDateTime ralarm;
344 if (nextOccurance(ev, now.date(), ralarm)) {
345 ralarm = ralarm.addSecs(-ev.alarmTime()*60);
346 if ( ralarm > now ) {
347 when = ralarm;
348 warn = ev.alarmTime();
349 } else if ( nextOccurance(ev, now.date().addDays(1), ralarm) ) {
350 ralarm = ralarm.addSecs( -ev.alarmTime()*60 );
351 if ( ralarm > now ) {
352 when = ralarm;
353 warn = ev.alarmTime();
354 }
355 }
356 }
357 } else {
358 warn = ev.alarmTime();
359 when = ev.start().addSecs( -ev.alarmTime()*60 );
360 }
361 return when > now;
362}
363
364static void addEventAlarm( const Event &ev )
365{
366 QDateTime when;
367 int warn;
368 if ( nextAlarm(ev,when,warn) )
369 AlarmServer::addAlarm( when,
370 "QPE/Application/datebook",
371 "alarm(QDateTime,int)", warn );
372}
373
374static void delEventAlarm( const Event &ev )
375{
376 QDateTime when;
377 int warn;
378 if ( nextAlarm(ev,when,warn) )
379 AlarmServer::deleteAlarm( when,
380 "QPE/Application/datebook",
381 "alarm(QDateTime,int)", warn );
382}
383
384
385DateBookDB::DateBookDB()
386{
387 init();
388}
389
390DateBookDB::~DateBookDB()
391{
392 save();
393 eventList.clear();
394 repeatEvents.clear();
395}
396
397
398//#### Why is this code duplicated in getEffectiveEvents ?????
399//#### Addendum. Don't use this function, lets faze it out if we can.
400QValueList<Event> DateBookDB::getEvents( const QDate &from, const QDate &to )
401{
402 QValueList<Event> tmpList;
403 tmpList = getNonRepeatingEvents( from, to );
404
405 // check for repeating events...
406 for (QValueList<Event>::ConstIterator it = repeatEvents.begin();
407 it != repeatEvents.end(); ++it) {
408 QDate itDate = from;
409 QDateTime due;
410
411 /* create a false end date, to short circuit on hard
412 MonthlyDay recurences */
413 Event dummy_event = *it;
414 Event::RepeatPattern r = dummy_event.repeatPattern();
415 if ( !r.hasEndDate || r.endDate() > to ) {
416 r.setEndDate( to );
417 r.hasEndDate = TRUE;
418 }
419 dummy_event.setRepeat(TRUE, r);
420
421 while (nextOccurance(dummy_event, itDate, due)) {
422 if (due.date() > to)
423 break;
424 Event newEvent = *it;
425 newEvent.setStart(due);
426 newEvent.setEnd(due.addSecs((*it).start().secsTo((*it).end())));
427
428 tmpList.append(newEvent);
429 itDate = due.date().addDays(1); /* the next event */
430 }
431 }
432 qHeapSort(tmpList);
433 return tmpList;
434}
435
436QValueList<Event> DateBookDB::getEvents( const QDateTime &start )
437{
438 QValueList<Event> day = getEvents(start.date(),start.date());
439
440 QValueListConstIterator<Event> it;
441 QDateTime dtTmp;
442 QValueList<Event> tmpList;
443 for (it = day.begin(); it != day.end(); ++it ) {
444 dtTmp = (*it).start(TRUE);
445 if ( dtTmp == start )
446 tmpList.append( *it );
447 }
448 return tmpList;
449}
450
451//#### Why is this code duplicated in getEvents ?????
452
453QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDate &from,
454 const QDate &to )
455{
456 QValueList<EffectiveEvent> tmpList;
457 QValueListIterator<Event> it;
458
459 EffectiveEvent effEv;
460 QDateTime dtTmp,
461 dtEnd;
462
463 for (it = eventList.begin(); it != eventList.end(); ++it ) {
464 dtTmp = (*it).start(TRUE);
465 dtEnd = (*it).end(TRUE);
466
467 if ( dtTmp.date() >= from && dtTmp.date() <= to ) {
468 Event tmpEv = *it;
469 effEv.setEvent(tmpEv);
470 effEv.setDate( dtTmp.date() );
471 effEv.setStart( dtTmp.time() );
472 if ( dtTmp.date() != dtEnd.date() )
473 effEv.setEnd( QTime(23, 59, 0) );
474 else
475 effEv.setEnd( dtEnd.time() );
476 tmpList.append( effEv );
477 }
478 // we must also check for end date information...
479 if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) {
480 QDateTime dt = dtTmp.addDays( 1 );
481 dt.setTime( QTime(0, 0, 0) );
482 QDateTime dtStop;
483 if ( dtEnd > to ) {
484 dtStop = to;
485 } else
486 dtStop = dtEnd;
487 while ( dt <= dtStop ) {
488 Event tmpEv = *it;
489 effEv.setEvent( tmpEv );
490 effEv.setDate( dt.date() );
491 if ( dt >= from ) {
492 effEv.setStart( QTime(0, 0, 0) );
493 if ( dt.date() == dtEnd.date() )
494 effEv.setEnd( dtEnd.time() );
495 else
496 effEv.setEnd( QTime(23, 59, 59) );
497 tmpList.append( effEv );
498 }
499 dt = dt.addDays( 1 );
500 }
501 }
502 }
503 // check for repeating events...
504 QDateTime repeat;
505 for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
506
507 /* create a false end date, to short circuit on hard
508 MonthlyDay recurences */
509 Event dummy_event = *it;
510 int duration = (*it).start().date().daysTo( (*it).end().date() );
511 QDate itDate = from.addDays(-duration);
512
513 Event::RepeatPattern r = dummy_event.repeatPattern();
514 if ( !r.hasEndDate || r.endDate() > to ) {
515 r.setEndDate( to );
516 r.hasEndDate = TRUE;
517 }
518 dummy_event.setRepeat(TRUE, r);
519
520 while (nextOccurance(dummy_event, itDate, repeat)) {
521 if(repeat.date() > to)
522 break;
523 effEv.setDate( repeat.date() );
524 if ((*it).type() == Event::AllDay) {
525 effEv.setStart( QTime(0,0,0) );
526 effEv.setEnd( QTime(23,59,59) );
527 } else {
528 /* we only occur by days, not hours/minutes/seconds. Hence
529 the actual end and start times will be the same for
530 every repeated event. For multi day events this is
531 fixed up later if on wronge day span */
532 effEv.setStart( (*it).start().time() );
533 effEv.setEnd( (*it).end().time() );
534 }
535 if ( duration != 0 ) {
536 // multi-day repeating events
537 QDate sub_it = QMAX( repeat.date(), from );
538 QDate startDate = repeat.date();
539 QDate endDate = startDate.addDays( duration );
540
541 while ( sub_it <= endDate && sub_it <= to ) {
542 EffectiveEvent tmpEffEv = effEv;
543 Event tmpEv = *it;
544 tmpEffEv.setEvent( tmpEv );
545
546 if ( sub_it != startDate )
547 tmpEffEv.setStart( QTime(0,0,0) );
548 if ( sub_it != endDate )
549 tmpEffEv.setEnd( QTime(23,59,59) );
550 tmpEffEv.setDate( sub_it );
551 tmpEffEv.setEffectiveDates( startDate, endDate );
552 tmpList.append( tmpEffEv );
553 sub_it = sub_it.addDays( 1 );
554 }
555 itDate = endDate;
556 } else {
557 Event tmpEv = *it;
558 effEv.setEvent( tmpEv );
559 tmpList.append( effEv );
560 itDate = repeat.date().addDays( 1 );
561 }
562 }
563 }
564
565 qHeapSort( tmpList );
566 return tmpList;
567}
568
569QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDateTime &dt)
570{
571 QValueList<EffectiveEvent> day = getEffectiveEvents(dt.date(), dt.date());
572 QValueListConstIterator<EffectiveEvent> it;
573 QValueList<EffectiveEvent> tmpList;
574 QDateTime dtTmp;
575
576 for (it = day.begin(); it != day.end(); ++it ) {
577 dtTmp = QDateTime( (*it).date(), (*it).start() );
578 // at the moment we don't have second granularity, be nice about that..
579 if ( QABS(dt.secsTo(dtTmp)) < 60 )
580 tmpList.append( *it );
581 }
582 return tmpList;
583}
584
585
586void DateBookDB::addEvent( const Event &ev, bool doalarm )
587{
588 // write to the journal...
589 saveJournalEntry( ev, ACTION_ADD, -1, false );
590 addJFEvent( ev, doalarm );
591 d->clean = false;
592}
593
594void DateBookDB::addJFEvent( const Event &ev, bool doalarm )
595{
596 if ( doalarm && ev.hasAlarm() )
597 addEventAlarm( ev );
598 if ( ev.hasRepeat() )
599 repeatEvents.append( ev );
600 else
601 eventList.append( ev );
602}
603
604void DateBookDB::editEvent( const Event &old, Event &editedEv )
605{
606 int oldIndex=0;
607 bool oldHadRepeat = old.hasRepeat();
608 Event orig;
609
610 // write to the journal...
611 if ( oldHadRepeat ) {
612 if ( origRepeat( old, orig ) ) // should work always...
613 oldIndex = repeatEvents.findIndex( orig );
614 } else
615 oldIndex = eventList.findIndex( old );
616 saveJournalEntry( editedEv, ACTION_REPLACE, oldIndex, oldHadRepeat );
617
618 if ( old.hasAlarm() )
619 delEventAlarm( old );
620 if ( oldHadRepeat ) {
621 if ( oldHadRepeat && editedEv.hasRepeat() ) {
622 // assumption, when someone edits a repeating event, they
623 // want to change them all, maybe not perfect, but it works
624 // for the moment...
625 repeatEvents.remove( orig );
626 } else
627 removeRepeat( old );
628 } else {
629 QValueList<Event>::Iterator it = eventList.find( old );
630 if ( it != eventList.end() )
631 eventList.remove( it );
632 }
633 if ( editedEv.hasAlarm() )
634 addEventAlarm( editedEv );
635 if ( editedEv.hasRepeat() )
636 repeatEvents.append( editedEv );
637 else
638 eventList.append( editedEv );
639 d->clean = false;
640}
641
642void DateBookDB::removeEvent( const Event &ev )
643{
644 // write to the journal...
645 saveJournalEntry( ev, ACTION_REMOVE, -1, false );
646 removeJFEvent( ev );
647 d->clean = false;
648}
649
650void DateBookDB::removeJFEvent( const Event&ev )
651{
652 if ( ev.hasAlarm() )
653 delEventAlarm( ev );
654 if ( ev.hasRepeat() ) {
655 removeRepeat( ev );
656 } else {
657 QValueList<Event>::Iterator it = eventList.find( ev );
658 if ( it != eventList.end() )
659 eventList.remove( it );
660 }
661}
662
663// also handles journaling...
664void DateBookDB::loadFile( const QString &strFile )
665{
666
667 QFile f( strFile );
668 if ( !f.open( IO_ReadOnly ) )
669 return;
670
671 enum Attribute {
672 FDescription = 0,
673 FLocation,
674 FCategories,
675 FUid,
676 FType,
677 FAlarm,
678 FSound,
679 FRType,
680 FRWeekdays,
681 FRPosition,
682 FRFreq,
683 FRHasEndDate,
684 FREndDate,
685 FRStart,
686 FREnd,
687 FNote,
688 FCreated,
689 FAction,
690 FActionKey,
691 FJournalOrigHadRepeat
692 };
693
694 QAsciiDict<int> dict( 97 );
695 dict.setAutoDelete( TRUE );
696 dict.insert( "description", new int(FDescription) );
697 dict.insert( "location", new int(FLocation) );
698 dict.insert( "categories", new int(FCategories) );
699 dict.insert( "uid", new int(FUid) );
700 dict.insert( "type", new int(FType) );
701 dict.insert( "alarm", new int(FAlarm) );
702 dict.insert( "sound", new int(FSound) );
703 dict.insert( "rtype", new int(FRType) );
704 dict.insert( "rweekdays", new int(FRWeekdays) );
705 dict.insert( "rposition", new int(FRPosition) );
706 dict.insert( "rfreq", new int(FRFreq) );
707 dict.insert( "rhasenddate", new int(FRHasEndDate) );
708 dict.insert( "enddt", new int(FREndDate) );
709 dict.insert( "start", new int(FRStart) );
710 dict.insert( "end", new int(FREnd) );
711 dict.insert( "note", new int(FNote) );
712 dict.insert( "created", new int(FCreated) );
713 dict.insert( "action", new int(FAction) );
714 dict.insert( "actionkey", new int(FActionKey) );
715 dict.insert( "actionorig", new int (FJournalOrigHadRepeat) );
716
717
718 QByteArray ba = f.readAll();
719 char* dt = ba.data();
720 int len = ba.size();
721 int currentAction,
722 journalKey,
723 origHadRepeat; // should be bool, but we need tri-state(not being used)
724
725 int i = 0;
726 char *point;
727 while ( ( point = strstr( dt+i, "<event " ) ) != 0 ) {
728 i = point - dt;
729 // if we are reading in events in the general case,
730 // we are just adding them, so let the actions represent that...
731 currentAction = ACTION_ADD;
732 journalKey = -1;
733 origHadRepeat = -1;
734 // some temporary variables for dates and times ...
735 //int startY = 0, startM = 0, startD = 0, starth = 0, startm = 0, starts = 0;
736 //int endY = 0, endM = 0, endD = 0, endh = 0, endm = 0, ends = 0;
737 //int enddtY = 0, enddtM = 0, enddtD = 0;
738
739 // ... for the alarm settings ...
740 int alarmTime = -1; Event::SoundTypeChoice alarmSound = Event::Silent;
741 // ... and for the recurrence
742 Event::RepeatPattern rp;
743 Event e;
744
745 i += 7;
746
747 while( 1 ) {
748 while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
749 ++i;
750 if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
751 break;
752 // we have another attribute, read it.
753 int j = i;
754 while ( j < len && dt[j] != '=' )
755 ++j;
756 char *attr = dt+i;
757 dt[j] = '\0';
758 i = ++j; // skip =
759 while ( i < len && dt[i] != '"' )
760 ++i;
761 j = ++i;
762 bool haveAmp = FALSE;
763 bool haveUtf = FALSE;
764 while ( j < len && dt[j] != '"' ) {
765 if ( dt[j] == '&' )
766 haveAmp = TRUE;
767 if ( ((unsigned char)dt[j]) > 0x7f )
768 haveUtf = TRUE;
769 ++j;
770 }
771
772 if ( i == j ) {
773 // leave out empty attributes
774 i = j + 1;
775 continue;
776 }
777
778 QString value = haveUtf ? QString::fromUtf8( dt+i, j-i )
779 : QString::fromLatin1( dt+i, j-i );
780 if ( haveAmp )
781 value = Qtopia::plainString( value );
782 i = j + 1;
783
784 //qDebug("attr='%s' value='%s'", attr.data(), value.latin1() );
785 int * find = dict[ attr ];
786#if 1
787 if ( !find ) {
788 // custom field
789 e.setCustomField(attr, value);
790 continue;
791 }
792
793 switch( *find ) {
794 case FDescription:
795 e.setDescription( value );
796 break;
797 case FLocation:
798 e.setLocation( value );
799 break;
800 case FCategories:
801 e.setCategories( Qtopia::Record::idsFromString( value ) );
802 break;
803 case FUid:
804 e.setUid( value.toInt() );
805 break;
806 case FType:
807 if ( value == "AllDay" )
808 e.setType( Event::AllDay );
809 else
810 e.setType( Event::Normal );
811 break;
812 case FAlarm:
813 alarmTime = value.toInt();
814 break;
815 case FSound:
816 alarmSound = value == "loud" ? Event::Loud : Event::Silent;
817 break;
818 // recurrence stuff
819 case FRType:
820 if ( value == "Daily" )
821 rp.type = Event::Daily;
822 else if ( value == "Weekly" )
823 rp.type = Event::Weekly;
824 else if ( value == "MonthlyDay" )
825 rp.type = Event::MonthlyDay;
826 else if ( value == "MonthlyDate" )
827 rp.type = Event::MonthlyDate;
828 else if ( value == "Yearly" )
829 rp.type = Event::Yearly;
830 else
831 rp.type = Event::NoRepeat;
832 break;
833 case FRWeekdays:
834 rp.days = value.toInt();
835 break;
836 case FRPosition:
837 rp.position = value.toInt();
838 break;
839 case FRFreq:
840 rp.frequency = value.toInt();
841 break;
842 case FRHasEndDate:
843 rp.hasEndDate = value.toInt();
844 break;
845 case FREndDate: {
846 rp.endDateUTC = (time_t) value.toLong();
847 break;
848 }
849 case FRStart: {
850 e.setStart( (time_t) value.toLong() );
851 break;
852 }
853 case FREnd: {
854 e.setEnd( (time_t) value.toLong() );
855 break;
856 }
857 case FNote:
858 e.setNotes( value );
859 break;
860 case FCreated:
861 rp.createTime = value.toInt();
862 break;
863 case FAction:
864 currentAction = value.toInt();
865 break;
866 case FActionKey:
867 journalKey = value.toInt();
868 break;
869 case FJournalOrigHadRepeat:
870 origHadRepeat = value.toInt();
871 break;
872 default:
873 qDebug( "huh??? missing enum? -- attr.: %s", attr );
874 break;
875 }
876#endif
877 }
878 // "post processing" (dates, times, alarm, recurrence)
879 // start date/time
880 e.setRepeat( rp.type != Event::NoRepeat, rp );
881
882 if ( alarmTime != -1 )
883 e.setAlarm( TRUE, alarmTime, alarmSound );
884
885 // now do our action based on the current action...
886 switch ( currentAction ) {
887 case ACTION_ADD:
888 addJFEvent( e );
889 break;
890 case ACTION_REMOVE:
891 removeJFEvent( e );
892 break;
893 case ACTION_REPLACE:
894 // be a little bit careful,
895 // in case of a messed up journal...
896 if ( journalKey > -1 && origHadRepeat > -1 ) {
897 // get the original from proper list...
898 if ( origHadRepeat )
899 removeJFEvent( *(repeatEvents.at(journalKey)) );
900 else
901 removeJFEvent( *(eventList.at(journalKey)) );
902 addJFEvent( e );
903 }
904 break;
905 default:
906 break;
907 }
908 }
909 f.close();
910}
911
912void DateBookDB::init()
913{
914 d = new DateBookDBPrivate;
915 d->clean = false;
916 QString str = dateBookFilename();
917 if ( str.isNull() ) {
918 QMessageBox::warning( 0, QObject::tr("Out of Space"),
919 QObject::tr("Unable to create start up files\n"
920 "Please free up some space\n"
921 "before entering data") );
922 }
923 // continuing along, we call this datebook filename again,
924 // because they may fix it before continuing, though it seems
925 // pretty unlikely...
926 loadFile( dateBookFilename() );
927
928 if ( QFile::exists( dateBookJournalFile() ) ) {
929 // merge the journal
930 loadFile( dateBookJournalFile() );
931 // save in our changes and remove the journal...
932 save();
933 }
934 d->clean = true;
935}
936
937bool DateBookDB::save()
938{
939 if ( d->clean == true )
940 return true;
941 QValueListIterator<Event> it;
942 int total_written;
943 QString strFileNew = dateBookFilename() + ".new";
944
945 QFile f( strFileNew );
946 if ( !f.open( IO_WriteOnly|IO_Raw ) )
947 return FALSE;
948
949 QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
950 buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
951 buf += "<events>\n";
952 QCString str = buf.utf8();
953 total_written = f.writeBlock( str.data(), str.length() );
954 if ( total_written != int(str.length()) ) {
955 f.close();
956 QFile::remove( strFileNew );
957 return false;
958 }
959
960 for ( it = eventList.begin(); it != eventList.end(); ++it ) {
961 buf = "<event";
962 (*it).save( buf );
963 buf += " />\n";
964 str = buf.utf8();
965 total_written = f.writeBlock( str.data(), str.length() );
966 if ( total_written != int(str.length()) ) {
967 f.close();
968 QFile::remove( strFileNew );
969 return false;
970 }
971 }
972 for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
973 buf = "<event";
974 (*it).save( buf );
975 buf += " />\n";
976 str = buf.utf8();
977 total_written = f.writeBlock( str.data(), str.length() );
978 if ( total_written != int(str.length()) ) {
979 f.close();
980 QFile::remove( strFileNew );
981 return false;
982 }
983 }
984 buf = "</events>\n</DATEBOOK>\n";
985 str = buf.utf8();
986 total_written = f.writeBlock( str.data(), str.length() );
987 if ( total_written != int(str.length()) ) {
988 f.close();
989 QFile::remove( strFileNew );
990 return false;
991 }
992 f.close();
993
994 // now rename... I like to use the systemcall
995 if ( ::rename( strFileNew, dateBookFilename() ) < 0 ) {
996 qWarning( "problem renaming file %s to %s errno %d",
997 strFileNew.latin1(), dateBookFilename().latin1(), errno );
998 // remove the file, otherwise it will just stick around...
999 QFile::remove( strFileNew );
1000 }
1001
1002 // may as well remove the journal file...
1003 QFile::remove( dateBookJournalFile() );
1004 d->clean = true;
1005 return true;
1006}
1007
1008void DateBookDB::reload()
1009{
1010 QValueList<Event>::Iterator it = eventList.begin();
1011 for ( ; it != eventList.end(); ++it ) {
1012 if ( (*it).hasAlarm() )
1013 delEventAlarm( *it );
1014 if ( (*it).hasRepeat() )
1015 removeRepeat( *it );
1016 }
1017 eventList.clear();
1018 repeatEvents.clear(); // should be a NOP
1019 init();
1020}
1021
1022bool DateBookDB::removeRepeat( const Event &ev )
1023{
1024 time_t removeMe = ev.repeatPattern().createTime;
1025 QValueListIterator<Event> it;
1026 for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
1027 if ( removeMe == (*it).repeatPattern().createTime ) {
1028 repeatEvents.remove( *it );
1029 // best break, or we are going into undefined territory!
1030 return TRUE;
1031 }
1032 }
1033 return FALSE;
1034}
1035
1036bool DateBookDB::origRepeat( const Event &ev, Event &orig ) const
1037{
1038 time_t removeMe = ev.repeatPattern().createTime;
1039 QValueListConstIterator<Event> it;
1040 for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
1041 if ( removeMe == (*it).repeatPattern().createTime ) {
1042 orig = (*it);
1043 return TRUE;
1044 }
1045 }
1046 return FALSE;
1047}
1048
1049void DateBookDB::saveJournalEntry( const Event &ev, journal_action action )
1050{
1051 saveJournalEntry( ev, action, -1, false );
1052}
1053
1054bool DateBookDB::saveJournalEntry( const Event &evOld, journal_action action,
1055 int key, bool origHadRepeat )
1056{
1057 bool status = false;
1058 Event ev = evOld;
1059 // write our log based on the action
1060 QFile f( dateBookJournalFile() );
1061 if ( !f.open( IO_WriteOnly|IO_Append ) )
1062 return false;
1063 QString buf = "<event";
1064 ev.save( buf );
1065 buf += " action=";
1066 buf += "\"" + QString::number(action) + "\"";
1067 buf += " actionkey=\"" + QString::number(key) + "\"";
1068 buf += " actionorig=\"" + QString::number(origHadRepeat) +"\"";
1069 buf += " />\n";
1070 QString str = buf.utf8();
1071 status = ( f.writeBlock( str.data(), str.length() ) == int(str.length()) );
1072 f.close();
1073 return status;
1074}
1075
1076QValueList<Event> DateBookDB::getRawRepeats() const
1077{
1078 return repeatEvents;
1079}
1080
1081QValueList<Event> DateBookDB::getNonRepeatingEvents( const QDate &from,
1082 const QDate &to ) const
1083{
1084 QValueListConstIterator<Event> it;
1085 QDateTime dtTmp, dtEnd;
1086 QValueList<Event> tmpList;
1087 for (it = eventList.begin(); it != eventList.end(); ++it ) {
1088 dtTmp = (*it).start(TRUE);
1089 dtEnd = (*it).end(TRUE);
1090
1091 if ( dtTmp.date() >= from && dtTmp.date() <= to ) {
1092 Event e = *it;
1093 if ( dtTmp.date() != dtEnd.date() )
1094 e.setEnd( QDateTime(dtTmp.date(), QTime(23, 59, 0)) );
1095 tmpList.append( e );
1096 }
1097 // we must also check for end date information...
1098 if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) {
1099 QDateTime dt = dtTmp.addDays( 1 );
1100 dt.setTime( QTime(0, 0, 0) );
1101 QDateTime dtStop;
1102 if ( dtEnd > to ) {
1103 dtStop = to;
1104 } else
1105 dtStop = dtEnd;
1106 while ( dt <= dtStop ) {
1107 Event ev = *it;
1108 if ( dt >= from ) {
1109 ev.setStart( QDateTime(dt.date(), QTime(0, 0, 0)) );
1110 if ( dt.date() == dtEnd.date() )
1111 ev.setEnd( QDateTime(dt.date(), dtEnd.time()) );
1112 else
1113 ev.setEnd( QDateTime(dt.date(), QTime(23, 59, 0)) );
1114 tmpList.append( ev );
1115 }
1116 dt = dt.addDays( 1 );
1117 }
1118 }
1119 }
1120 return tmpList;
1121}