libkcal

resourcecached.cpp
1 /*
2  This file is part of libkcal.
3 
4  Copyright (c) 2003,2004 Cornelius Schumacher <schumacher@kde.org>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 // TODO [FIXME] IMPORTANT
23 // If a cached resource initiates a reload while an event editor is active, or an event is locked for editing,
24 // a big fat crash will ensue. The reload subroutine must ABORT if ANY korganizer events are locked for editing!!!
25 
26 #include <tqdatastream.h>
27 #include <tqdatetime.h>
28 #include <tqfile.h>
29 #include <tqstring.h>
30 #include <tqptrlist.h>
31 
32 #include <kdebug.h>
33 #include <klocale.h>
34 #include <kurl.h>
35 #include <kstandarddirs.h>
36 
37 #include "event.h"
38 #include "exceptions.h"
39 #include "incidence.h"
40 #include "journal.h"
41 #include "todo.h"
42 #include <unistd.h>
43 
44 
45 #include "resourcecached.h"
46 
47 using namespace KCal;
48 
49 static bool m_editoropen = false;
50 
51 ResourceCached::ResourceCached( const KConfig* config )
52  : ResourceCalendar( config ), mCalendar( TQString::fromLatin1( "UTC" ) ),
53  mReloadPolicy( ReloadNever ), mReloadInterval( 10 ),
54  mReloadTimer( 0, "mReloadTimer" ), mReloaded( false ),
55  mSavePolicy( SaveNever ), mSaveInterval( 10 ),
56  mSaveTimer( 0, "mSaveTimer" ), mIdMapper( "kcal/uidmaps/" )
57 {
58  connect( &mReloadTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotReload() ) );
59  connect( &mSaveTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotSave() ) );
60 }
61 
62 ResourceCached::~ResourceCached()
63 {
64 }
65 
67 {
68  mReloadPolicy = i;
69 
70  setupReloadTimer();
71 }
72 
74 {
75  return mReloadPolicy;
76 }
77 
79 {
80  mReloadInterval = minutes;
81 }
82 
84 {
85  return mReloadInterval;
86 }
87 
89 {
90  mSavePolicy = i;
91 
92  setupSaveTimer();
93 }
94 
96 {
97  return mSavePolicy;
98 }
99 
101 {
102  mSaveInterval = minutes;
103 }
104 
106 {
107  return mSaveInterval;
108 }
109 
110 void ResourceCached::readConfig( const KConfig *config )
111 {
112  mReloadPolicy = config->readNumEntry( "ReloadPolicy", ReloadNever );
113  mReloadInterval = config->readNumEntry( "ReloadInterval", 10 );
114 
115  mSaveInterval = config->readNumEntry( "SaveInterval", 10 );
116  mSavePolicy = config->readNumEntry( "SavePolicy", SaveNever );
117 
118  mLastLoad = config->readDateTimeEntry( "LastLoad" );
119  mLastSave = config->readDateTimeEntry( "LastSave" );
120 
121  setupSaveTimer();
122  setupReloadTimer();
123 }
124 
125 void ResourceCached::setupSaveTimer()
126 {
127  if ( mSavePolicy == SaveInterval ) {
128  kdDebug(5800) << "ResourceCached::setSavePolicy(): start save timer (interval "
129  << mSaveInterval << " minutes)." << endl;
130  mSaveTimer.start( mSaveInterval * 60 * 1000 ); // n minutes
131  } else {
132  mSaveTimer.stop();
133  }
134 }
135 
136 void ResourceCached::setupReloadTimer()
137 {
138  if ( mReloadPolicy == ReloadInterval ) {
139  kdDebug(5800) << "ResourceCached::setSavePolicy(): start reload timer "
140  "(interval " << mReloadInterval << " minutes)" << endl;
141  mReloadTimer.start( mReloadInterval * 60 * 1000 ); // n minutes
142  } else {
143  mReloadTimer.stop();
144  }
145 }
146 
147 void ResourceCached::writeConfig( KConfig *config )
148 {
149  config->writeEntry( "ReloadPolicy", mReloadPolicy );
150  config->writeEntry( "ReloadInterval", mReloadInterval );
151 
152  config->writeEntry( "SavePolicy", mSavePolicy );
153  config->writeEntry( "SaveInterval", mSaveInterval );
154 
155  config->writeEntry( "LastLoad", mLastLoad );
156  config->writeEntry( "LastSave", mLastSave );
157 }
158 
160 {
161  return mCalendar.addEvent( event );
162 }
163 
164 bool ResourceCached::addEvent(Event *event, const TQString &subresource )
165 {
166  Q_UNUSED( subresource ); // CalendarLocal does not support subresources
167  return mCalendar.addEvent( event );
168 }
169 
170 // probably not really efficient, but...it works for now.
172 {
173  kdDebug(5800) << "ResourceCached::deleteEvent" << endl;
174 
175  return mCalendar.deleteEvent( event );
176 }
177 
178 
179 Event *ResourceCached::event( const TQString &uid )
180 {
181  return mCalendar.event( uid );
182 }
183 
185  EventSortField sortField,
186  SortDirection sortDirection )
187 {
188  Event::List list = mCalendar.rawEventsForDate( qd, sortField, sortDirection );
189 
190  return list;
191 }
192 
193 Event::List ResourceCached::rawEvents( const TQDate &start, const TQDate &end,
194  bool inclusive )
195 {
196  return mCalendar.rawEvents( start, end, inclusive );
197 }
198 
200 {
201  return mCalendar.rawEventsForDate( qdt.date() );
202 }
203 
205 {
206  return mCalendar.rawEvents( sortField, sortDirection );
207 }
208 
210 {
211  return mCalendar.addTodo( todo );
212 }
213 
214 bool ResourceCached::addTodo( Todo *todo, const TQString &subresource )
215 {
216  Q_UNUSED( subresource ); // CalendarLocal does not support subresources
217  return mCalendar.addTodo( todo );
218 }
219 
221 {
222  return mCalendar.deleteTodo( todo );
223 }
224 
226 {
227  return mCalendar.deleteJournal( journal );
228 }
229 
230 
232 {
233  return mCalendar.rawTodos( sortField, sortDirection );
234 }
235 
236 Todo *ResourceCached::todo( const TQString &uid )
237 {
238  return mCalendar.todo( uid );
239 }
240 
242 {
243  return mCalendar.rawTodosForDate( date );
244 }
245 
247 {
248  return mCalendar.addJournal( journal );
249 }
250 
251 bool ResourceCached::addJournal( Journal *journal, const TQString &subresource )
252 {
253  Q_UNUSED( subresource ); // CalendarLocal does not support subresources
254  return mCalendar.addJournal( journal );
255 }
256 
257 Journal *ResourceCached::journal( const TQString &uid )
258 {
259  return mCalendar.journal( uid );
260 }
261 
263 {
264  return mCalendar.rawJournals( sortField, sortDirection );
265 }
266 
268 {
269  return mCalendar.rawJournalsForDate( date );
270 }
271 
272 
273 Alarm::List ResourceCached::alarmsTo( const TQDateTime &to )
274 {
275  return mCalendar.alarmsTo( to );
276 }
277 
278 Alarm::List ResourceCached::alarms( const TQDateTime &from, const TQDateTime &to )
279 {
280  // kdDebug(5800) << "ResourceCached::alarms(" << from.toString() << " - " << to.toString() << ")\n";
281  return mCalendar.alarms( from, to );
282 }
283 
284 
285 void ResourceCached::setTimeZoneId( const TQString& tzid )
286 {
287  mCalendar.setTimeZoneId( tzid );
288 }
289 
290 TQString ResourceCached::timeZoneId() const
291 {
292  return mCalendar.timeZoneId();
293 }
294 
295 void ResourceCached::clearChanges()
296 {
297  mAddedIncidences.clear();
298  mChangedIncidences.clear();
299  mDeletedIncidences.clear();
300 }
301 
303 {
304  setIdMapperIdentifier();
305  mIdMapper.load();
306 
307  if ( KStandardDirs::exists( cacheFile() ) ) {
308  mCalendar.load( cacheFile() );
309  if ( readOnly() ) {
310  Incidence::List incidences( rawIncidences() );
311  Incidence::List::Iterator it;
312  for ( it = incidences.begin(); it != incidences.end(); ++it ) {
313  (*it)->setReadOnly( true );
314  }
315  }
316  }
317 }
318 
320 {
321  kdDebug(5800) << "ResourceCached::saveCache(): " << cacheFile() << endl;
322 
323  setIdMapperIdentifier();
324  mIdMapper.save();
325 
326  mCalendar.save( cacheFile() );
327 }
328 
329 void ResourceCached::setIdMapperIdentifier()
330 {
331  mIdMapper.setIdentifier( type() + "_" + identifier() );
332 }
333 
335 {
336  mCalendar.close();
337 }
338 
340 {
341  mCalendar.closeEvents();
342 }
343 
345 {
346  mCalendar.closeTodos();
347 }
348 
350 {
351  mCalendar.closeJournals();
352 }
353 
354 void ResourceCached::cleanUpEventCache( const Event::List &eventList )
355 {
356  CalendarLocal calendar ( TQString::fromLatin1( "UTC" ) );
357 
358  if ( KStandardDirs::exists( cacheFile() ) )
359  calendar.load( cacheFile() );
360  else
361  return;
362 
363  Event::List list = calendar.events();
364  Event::List::ConstIterator cacheIt, it;
365  for ( cacheIt = list.begin(); cacheIt != list.end(); ++cacheIt ) {
366  bool found = false;
367  for ( it = eventList.begin(); it != eventList.end(); ++it ) {
368  if ( (*it)->uid() == (*cacheIt)->uid() )
369  found = true;
370  }
371 
372  if ( !found ) {
373  mIdMapper.removeRemoteId( mIdMapper.remoteId( (*cacheIt)->uid() ) );
374  Event *event = mCalendar.event( (*cacheIt)->uid() );
375  if ( event )
376  mCalendar.deleteEvent( event );
377  }
378  }
379 
380  calendar.close();
381 }
382 
383 void ResourceCached::cleanUpTodoCache( const Todo::List &todoList )
384 {
385  CalendarLocal calendar ( TQString::fromLatin1( "UTC" ) );
386 
387  if ( KStandardDirs::exists( cacheFile() ) )
388  calendar.load( cacheFile() );
389  else
390  return;
391 
392  Todo::List list = calendar.todos();
393  Todo::List::ConstIterator cacheIt, it;
394  for ( cacheIt = list.begin(); cacheIt != list.end(); ++cacheIt ) {
395 
396  bool found = false;
397  for ( it = todoList.begin(); it != todoList.end(); ++it ) {
398  if ( (*it)->uid() == (*cacheIt)->uid() )
399  found = true;
400  }
401 
402  if ( !found ) {
403  mIdMapper.removeRemoteId( mIdMapper.remoteId( (*cacheIt)->uid() ) );
404  Todo *todo = mCalendar.todo( (*cacheIt)->uid() );
405  if ( todo )
406  mCalendar.deleteTodo( todo );
407  }
408  }
409 
410  calendar.close();
411 }
412 
413 KPIM::IdMapper& ResourceCached::idMapper()
414 {
415  return mIdMapper;
416 }
417 
419 {
420  return locateLocal( "cache", "kcal/kresources/" + identifier() );
421 }
422 
423 TQString ResourceCached::changesCacheFile( const TQString &type ) const
424 {
425  return locateLocal( "cache", "kcal/changescache/" + identifier() + "_" + type );
426 }
427 
428 void ResourceCached::saveChangesCache( const TQMap<Incidence*, bool> &map, const TQString &type )
429 {
430  CalendarLocal calendar ( TQString::fromLatin1( "UTC" ) );
431 
432  bool isEmpty = true;
433  TQMap<Incidence *,bool>::ConstIterator it;
434  for ( it = map.begin(); it != map.end(); ++it ) {
435  isEmpty = false;
436  calendar.addIncidence( it.key()->clone() );
437  }
438 
439  if ( !isEmpty ) {
440  calendar.save( changesCacheFile( type ) );
441  } else {
442  TQFile file( changesCacheFile( type ) );
443  file.remove();
444  }
445 
446  calendar.close();
447 }
448 
449 void ResourceCached::saveChangesCache()
450 {
451  saveChangesCache( mAddedIncidences, "added" );
452  saveChangesCache( mDeletedIncidences, "deleted" );
453  saveChangesCache( mChangedIncidences, "changed" );
454 }
455 
456 void ResourceCached::loadChangesCache( TQMap<Incidence*, bool> &map, const TQString &type )
457 {
458  CalendarLocal calendar ( TQString::fromLatin1( "UTC" ) );
459 
460  if ( KStandardDirs::exists( changesCacheFile( type ) ) )
461  calendar.load( changesCacheFile( type ) );
462  else
463  return;
464 
465  const Incidence::List list = calendar.incidences();
466  Incidence::List::ConstIterator it;
467  for ( it = list.begin(); it != list.end(); ++it )
468  map.insert( (*it)->clone(), true );
469 
470  calendar.close();
471 }
472 
473 void ResourceCached::loadChangesCache()
474 {
475  loadChangesCache( mAddedIncidences, "added" );
476  loadChangesCache( mDeletedIncidences, "deleted" );
477  loadChangesCache( mChangedIncidences, "changed" );
478 }
479 
481 {
482 #if 1
483  kdDebug(5800) << "ResourceCached::calendarIncidenceAdded(): "
484  << i->uid() << endl;
485 #endif
486 
487  TQMap<Incidence *,bool>::ConstIterator it;
488  it = mAddedIncidences.find( i );
489  if ( it == mAddedIncidences.end() ) {
490  mAddedIncidences.insert( i, true );
491  }
492 
493  checkForAutomaticSave();
494 }
495 
497 {
498 #if 1
499  kdDebug(5800) << "ResourceCached::calendarIncidenceChanged(): "
500  << i->uid() << endl;
501 #endif
502 
503  TQMap<Incidence *,bool>::ConstIterator it;
504  it = mChangedIncidences.find( i );
505  // FIXME: If you modify an added incidence, there's no need to add it to mChangedIncidences!
506  if ( it == mChangedIncidences.end() ) {
507  mChangedIncidences.insert( i, true );
508  }
509 
510  checkForAutomaticSave();
511 }
512 
514 {
515 #if 1
516  kdDebug(5800) << "ResourceCached::calendarIncidenceDeleted(): "
517  << i->uid() << endl;
518 #endif
519 
520  if (i->hasRecurrenceID()) {
521  // This incidence has a parent; notify the parent of the child's death and do not destroy the parent!
522  // Get the parent
523  IncidenceList il = i->childIncidences();
524  IncidenceListIterator it;
525  it = il.begin();
526  Incidence *parentIncidence;
527  parentIncidence = this->incidence(*it);
528  // Remove the child
529  calendarIncidenceChanged(parentIncidence);
530  }
531  else {
532  TQMap<Incidence *,bool>::ConstIterator it;
533  it = mDeletedIncidences.find( i );
534  if ( it == mDeletedIncidences.end() ) {
535  mDeletedIncidences.insert( i, true );
536  }
537  }
538  checkForAutomaticSave();
539 }
540 
541 Incidence::List ResourceCached::addedIncidences() const
542 {
543  Incidence::List added;
544  TQMap<Incidence *,bool>::ConstIterator it;
545  for( it = mAddedIncidences.begin(); it != mAddedIncidences.end(); ++it ) {
546  added.append( it.key() );
547  }
548  return added;
549 }
550 
551 Incidence::List ResourceCached::changedIncidences() const
552 {
553  Incidence::List changed;
554  TQMap<Incidence *,bool>::ConstIterator it;
555  for( it = mChangedIncidences.begin(); it != mChangedIncidences.end(); ++it ) {
556  changed.append( it.key() );
557  }
558  return changed;
559 }
560 
561 Incidence::List ResourceCached::deletedIncidences() const
562 {
563  Incidence::List deleted;
564  TQMap<Incidence *,bool>::ConstIterator it;
565  for( it = mDeletedIncidences.begin(); it != mDeletedIncidences.end(); ++it ) {
566  deleted.append( it.key() );
567  }
568  return deleted;
569 }
570 
571 Incidence::List ResourceCached::allChanges() const
572 {
573  Incidence::List changes;
574  TQMap<Incidence *,bool>::ConstIterator it;
575  for( it = mAddedIncidences.begin(); it != mAddedIncidences.end(); ++it ) {
576  changes.append( it.key() );
577  }
578  for( it = mChangedIncidences.begin(); it != mChangedIncidences.end(); ++it ) {
579  changes.append( it.key() );
580  }
581  for( it = mDeletedIncidences.begin(); it != mDeletedIncidences.end(); ++it ) {
582  changes.append( it.key() );
583  }
584  return changes;
585 }
586 
587 bool ResourceCached::hasChanges() const
588 {
589  return !( mAddedIncidences.isEmpty() && mChangedIncidences.isEmpty() &&
590  mDeletedIncidences.isEmpty() );
591 }
592 
593 void ResourceCached::clearChange( Incidence *incidence )
594 {
595  clearChange( incidence->uid() );
596 }
597 
598 void ResourceCached::clearChange( const TQString &uid )
599 {
600  TQMap<Incidence*, bool>::Iterator it;
601 
602  for ( it = mAddedIncidences.begin(); it != mAddedIncidences.end(); ++it )
603  if ( it.key()->uid() == uid ) {
604  mAddedIncidences.remove( it );
605  break;
606  }
607 
608  for ( it = mChangedIncidences.begin(); it != mChangedIncidences.end(); ++it )
609  if ( it.key()->uid() == uid ) {
610  mChangedIncidences.remove( it );
611  break;
612  }
613 
614  for ( it = mDeletedIncidences.begin(); it != mDeletedIncidences.end(); ++it )
615  if ( it.key()->uid() == uid ) {
616  mDeletedIncidences.remove( it );
617  break;
618  }
619 }
620 
621 void ResourceCached::enableChangeNotification()
622 {
623  mCalendar.registerObserver( this );
624 }
625 
626 void ResourceCached::disableChangeNotification()
627 {
628  mCalendar.unregisterObserver( this );
629 }
630 
631 bool ResourceCached::editorWindowOpen()
632 {
633  return m_editoropen;
634 }
635 
636 void ResourceCached::setEditorWindowOpen(bool open)
637 {
638  m_editoropen = open;
639 }
640 
641 void ResourceCached::slotReload()
642 {
643  if ( !isActive() ) return;
644 
645  // Make sure no editor windows are open
646  if (editorWindowOpen() == true) return;
647 
648  kdDebug(5800) << "ResourceCached::slotReload()" << endl;
649 
650  load();
651 }
652 
653 void ResourceCached::slotSave()
654 {
655  if ( !isActive() ) return;
656 
657  kdDebug(5800) << "ResourceCached::slotSave()" << endl;
658 
659  save();
660 }
661 
662 void ResourceCached::checkForAutomaticSave()
663 {
664  if ( mSavePolicy == SaveAlways ) {
665  kdDebug(5800) << "ResourceCached::checkForAutomaticSave(): save now" << endl;
666  mSaveTimer.start( 1 * 1000, true ); // 1 second
667  } else if ( mSavePolicy == SaveDelayed ) {
668  kdDebug(5800) << "ResourceCached::checkForAutomaticSave(): save delayed"
669  << endl;
670  mSaveTimer.start( 15 * 1000, true ); // 15 seconds
671  }
672 }
673 
675 {
676  if ( mReloadPolicy == ReloadNever ) return false;
677  if ( mReloadPolicy == ReloadOnStartup ) return !mReloaded;
678  return true;
679 }
680 
682 {
683  if ( mSavePolicy == SaveNever ) return false;
684  return true;
685 }
686 
687 void ResourceCached::addInfoText( TQString &txt ) const
688 {
689  if ( mLastLoad.isValid() ) {
690  txt += "<br>";
691  txt += i18n("Last loaded: %1")
692  .arg( KGlobal::locale()->formatDateTime( mLastLoad ) );
693  }
694  if ( mLastSave.isValid() ) {
695  txt += "<br>";
696  txt += i18n("Last saved: %1")
697  .arg( KGlobal::locale()->formatDateTime( mLastSave ) );
698  }
699 }
700 
702 {
703  mCalendar.close();
704 }
705 
707 {
708  kdDebug(5800) << "Opening resource " << resourceName() << endl;
709  return true;
710 }
711 
713 {
714  mCalendar.setOwner( owner );
715 }
716 
718 {
719  return mCalendar.getOwner();
720 }
721 
722 #include "resourcecached.moc"