KCal Library
resourcelocaldir.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "resourcelocaldir.h"
00024 #include "resourcelocaldir_p.h"
00025 #include "calendarlocal.h"
00026 #include "incidence.h"
00027 #include "event.h"
00028 #include "todo.h"
00029 #include "journal.h"
00030 #include "freebusy.h"
00031
00032 #include "kresources/configwidget.h"
00033
00034 #include "assignmentvisitor_p.h"
00035 #include "comparisonvisitor_p.h"
00036 #include <kdebug.h>
00037 #include <klocale.h>
00038 #include <kconfig.h>
00039 #include <kstandarddirs.h>
00040 #include <kconfiggroup.h>
00041
00042 #include <QtCore/QString>
00043 #include <QtCore/QDir>
00044 #include <QtCore/QFileInfo>
00045
00046 #include <typeinfo>
00047 #include <stdlib.h>
00048
00049 #include "resourcelocaldir.moc"
00050 #include "resourcelocaldir_p.moc"
00051
00052 using namespace KCal;
00053
00054 ResourceLocalDir::ResourceLocalDir()
00055 : ResourceCached(), d( new KCal::ResourceLocalDir::Private( this ) )
00056 {
00057 d->init();
00058 }
00059
00060 ResourceLocalDir::ResourceLocalDir( const KConfigGroup &group )
00061 : ResourceCached( group ), d( new KCal::ResourceLocalDir::Private( this ) )
00062 {
00063 readConfig( group );
00064 d->init();
00065 }
00066
00067 ResourceLocalDir::ResourceLocalDir( const QString &dirName )
00068 : ResourceCached(), d( new KCal::ResourceLocalDir::Private( dirName, this ) )
00069 {
00070 d->init();
00071 }
00072
00073 void ResourceLocalDir::readConfig( const KConfigGroup &group )
00074 {
00075 QString url = group.readPathEntry( "CalendarURL", QString() );
00076 d->mURL = KUrl( url );
00077 }
00078
00079 void ResourceLocalDir::writeConfig( KConfigGroup &group )
00080 {
00081 kDebug();
00082
00083 ResourceCalendar::writeConfig( group );
00084
00085 group.writePathEntry( "CalendarURL", d->mURL.prettyUrl() );
00086 }
00087
00088 void ResourceLocalDir::Private::init( )
00089 {
00090 mResource->setType( "dir" );
00091
00092 mResource->setSavePolicy( SaveDelayed );
00093
00094 connect( &mDirWatch, SIGNAL( dirty( const QString & ) ),
00095 this, SLOT( updateIncidenceInCalendar( const QString & ) ) );
00096 connect( &mDirWatch, SIGNAL( created( const QString & ) ),
00097 this, SLOT( addIncidenceToCalendar( const QString & ) ) );
00098 connect( &mDirWatch, SIGNAL( deleted( const QString & ) ),
00099 this, SLOT( deleteIncidenceFromCalendar( const QString & ) ) );
00100
00101 connect ( this, SIGNAL(resourceChanged( ResourceCalendar *)),
00102 mResource, SIGNAL(resourceChanged( ResourceCalendar *)) );
00103
00104 mLock = new KABC::Lock( mURL.path() );
00105
00106 mDirWatch.addDir( mURL.path(), KDirWatch::WatchFiles );
00107 mDirWatch.startScan();
00108 }
00109
00110 ResourceLocalDir::~ResourceLocalDir()
00111 {
00112 close();
00113
00114 delete d->mLock;
00115 delete d;
00116 }
00117
00118 bool ResourceLocalDir::doOpen()
00119 {
00120 QFileInfo dirInfo( d->mURL.path() );
00121 return dirInfo.isDir() && dirInfo.isReadable() &&
00122 ( dirInfo.isWritable() || readOnly() );
00123 }
00124
00125 bool ResourceLocalDir::doLoad( bool )
00126 {
00127 kDebug();
00128
00129 calendar()->close();
00130 QString dirName = d->mURL.path();
00131
00132 if ( !( KStandardDirs::exists( dirName ) || KStandardDirs::exists( dirName + '/' ) ) ) {
00133 kDebug() << "Directory '" << dirName << "' doesn't exist yet. Creating it.";
00134
00135
00136
00137
00138 return KStandardDirs::makeDir( dirName, 0775 );
00139 }
00140
00141
00142 kDebug() << dirName;
00143 QFileInfo dirInfo( dirName );
00144 if ( !( dirInfo.isDir() && dirInfo.isReadable() &&
00145 ( dirInfo.isWritable() || readOnly() ) ) ) {
00146 return false;
00147 }
00148
00149 QDir dir( dirName );
00150 const QStringList entries = dir.entryList( QDir::Files | QDir::Readable );
00151
00152 bool success = true;
00153
00154 foreach ( const QString &entry, entries ) {
00155 if ( d->isTempFile( entry ) ) {
00156 continue;
00157 }
00158
00159 const QString fileName = dirName + '/' + entry;
00160 kDebug() << " read '" << fileName << "'";
00161 CalendarLocal cal( calendar()->timeSpec() );
00162 if ( !doFileLoad( cal, fileName ) ) {
00163 success = false;
00164 }
00165 }
00166
00167 return success;
00168 }
00169
00170 bool ResourceLocalDir::doFileLoad( CalendarLocal &cal, const QString &fileName )
00171 {
00172 return d->doFileLoad( cal, fileName, false );
00173 }
00174
00175 bool ResourceLocalDir::doSave( bool syncCache )
00176 {
00177 Q_UNUSED( syncCache );
00178 Incidence::List list;
00179 bool success = true;
00180
00181 list = addedIncidences();
00182 list += changedIncidences();
00183
00184 for ( Incidence::List::iterator it = list.begin(); it != list.end(); ++it ) {
00185 if ( !doSave( *it ) ) {
00186 success = false;
00187 }
00188 }
00189
00190 return success;
00191 }
00192
00193 bool ResourceLocalDir::doSave( bool, Incidence *incidence )
00194 {
00195 if ( d->mDeletedIncidences.contains( incidence ) ) {
00196 d->mDeletedIncidences.removeAll( incidence );
00197 return true;
00198 }
00199
00200 d->mDirWatch.stopScan();
00201
00202 QString fileName = d->mURL.path() + '/' + incidence->uid();
00203 kDebug() << "writing '" << fileName << "'";
00204
00205 CalendarLocal cal( calendar()->timeSpec() );
00206 cal.addIncidence( incidence->clone() );
00207 const bool ret = cal.save( fileName );
00208
00209 d->mDirWatch.startScan();
00210
00211 return ret;
00212 }
00213
00214 KABC::Lock *ResourceLocalDir::lock()
00215 {
00216 return d->mLock;
00217 }
00218
00219 void ResourceLocalDir::reload( const QString &file )
00220 {
00221 Q_UNUSED( file );
00222 }
00223
00224 bool ResourceLocalDir::deleteEvent( Event *event )
00225 {
00226 kDebug();
00227 if ( d->deleteIncidenceFile( event ) ) {
00228 if ( calendar()->deleteEvent( event ) ) {
00229 d->mDeletedIncidences.append( event );
00230 return true;
00231 } else {
00232 return false;
00233 }
00234 } else {
00235 return false;
00236 }
00237 }
00238
00239 void ResourceLocalDir::deleteAllEvents()
00240 {
00241 calendar()->deleteAllEvents();
00242 }
00243
00244 bool ResourceLocalDir::deleteTodo( Todo *todo )
00245 {
00246 if ( d->deleteIncidenceFile( todo ) ) {
00247 if ( calendar()->deleteTodo( todo ) ) {
00248 d->mDeletedIncidences.append( todo );
00249 return true;
00250 } else {
00251 return false;
00252 }
00253 } else {
00254 return false;
00255 }
00256 }
00257
00258 void ResourceLocalDir::deleteAllTodos()
00259 {
00260 calendar()->deleteAllTodos();
00261 }
00262
00263 bool ResourceLocalDir::deleteJournal( Journal *journal )
00264 {
00265 if ( d->deleteIncidenceFile( journal ) ) {
00266 if ( calendar()->deleteJournal( journal ) ) {
00267 d->mDeletedIncidences.append( journal );
00268 return true;
00269 } else {
00270 return false;
00271 }
00272 } else {
00273 return false;
00274 }
00275 }
00276
00277 void ResourceLocalDir::deleteAllJournals()
00278 {
00279 calendar()->deleteAllJournals();
00280 }
00281
00282 void ResourceLocalDir::dump() const
00283 {
00284 ResourceCalendar::dump();
00285 kDebug() << " Url:" << d->mURL.url();
00286 }
00287
00288 bool ResourceLocalDir::Private::deleteIncidenceFile( Incidence *incidence )
00289 {
00290 QFile file( mURL.path() + '/' + incidence->uid() );
00291 if ( !file.exists() ) {
00292 return true;
00293 }
00294
00295 mDirWatch.stopScan();
00296 bool removed = file.remove();
00297 mDirWatch.startScan();
00298 return removed;
00299 }
00300
00301 bool ResourceLocalDir::Private::isTempFile( const QString &fileName ) const
00302 {
00303 return
00304 fileName.contains( QRegExp( "(~|\\.new|\\.tmp)$" ) ) ||
00305 QFileInfo( fileName ).fileName().startsWith( QLatin1String( "qt_temp." ) ) ||
00306 fileName == mURL.path();
00307 }
00308
00309 void ResourceLocalDir::Private::addIncidenceToCalendar( const QString &file )
00310 {
00311
00312 if ( mResource->isOpen() &&
00313 !isTempFile( file ) &&
00314 !mResource->calendar()->incidence( getUidFromFileName( file ) ) ) {
00315
00316 CalendarLocal cal( mResource->calendar()->timeSpec() );
00317 if ( doFileLoad( cal, file, true ) ) {
00318 emit resourceChanged( mResource );
00319 }
00320 }
00321 }
00322
00323 void ResourceLocalDir::Private::updateIncidenceInCalendar( const QString &file )
00324 {
00325 if ( mResource->isOpen() && !isTempFile( file ) ) {
00326 CalendarLocal cal( mResource->calendar()->timeSpec() );
00327 if ( doFileLoad( cal, file, true ) ) {
00328 emit resourceChanged( mResource );
00329 }
00330 }
00331 }
00332
00333 QString ResourceLocalDir::Private::getUidFromFileName( const QString &fileName )
00334 {
00335 return QFileInfo( fileName ).fileName();
00336 }
00337
00338 void ResourceLocalDir::Private::deleteIncidenceFromCalendar( const QString &file )
00339 {
00340
00341 if ( mResource->isOpen() && !isTempFile( file ) ) {
00342 Incidence *inc = mResource->calendar()->incidence( getUidFromFileName( file ) );
00343
00344 if ( inc ) {
00345 mResource->calendar()->deleteIncidence( inc );
00346 emit resourceChanged( mResource );
00347 }
00348 }
00349 }
00350
00351 bool ResourceLocalDir::Private::doFileLoad( CalendarLocal &cal,
00352 const QString &fileName,
00353 const bool replace )
00354 {
00355 if ( !cal.load( fileName ) ) {
00356 return false;
00357 }
00358 Incidence::List incidences = cal.rawIncidences();
00359 Incidence::List::ConstIterator it;
00360 Incidence *inc;
00361 ComparisonVisitor compVisitor;
00362 AssignmentVisitor assVisitor;
00363 for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) {
00364 Incidence *i = *it;
00365 if ( i ) {
00366
00367 if ( replace && ( inc = mResource->calendar()->incidence( i->uid() ) ) ) {
00368 if ( compVisitor.compare( i, inc ) ) {
00369
00370 return false;
00371 } else {
00372 inc->startUpdates();
00373
00374 bool assignResult = assVisitor.assign( inc, i );
00375
00376 if ( assignResult ) {
00377 if ( !inc->relatedToUid().isEmpty() ) {
00378 QString uid = inc->relatedToUid();
00379 inc->setRelatedTo( mResource->calendar()->incidence( uid ) );
00380 }
00381 inc->updated();
00382 inc->endUpdates();
00383 } else {
00384 inc->endUpdates();
00385 kWarning() << "Incidence (uid=" << inc->uid()
00386 << ", summary=" << inc->summary()
00387 << ") changed type. Replacing it.";
00388
00389 mResource->calendar()->deleteIncidence( inc );
00390 delete inc;
00391 mResource->calendar()->addIncidence( i->clone() );
00392 }
00393 }
00394 } else {
00395 mResource->calendar()->addIncidence( i->clone() );
00396 }
00397 }
00398 }
00399 return true;
00400 }
00401
00402 class AssignmentVisitor::Private
00403 {
00404 public:
00405 Private() : mSource( 0 ) {}
00406
00407 public:
00408 const IncidenceBase *mSource;
00409 };
00410
00411 AssignmentVisitor::AssignmentVisitor() : d( new Private() )
00412 {
00413 }
00414
00415 AssignmentVisitor::~AssignmentVisitor()
00416 {
00417 delete d;
00418 }
00419
00420 bool AssignmentVisitor::assign( IncidenceBase *target, const IncidenceBase *source )
00421 {
00422 Q_ASSERT( target != 0 );
00423 Q_ASSERT( source != 0 );
00424
00425 d->mSource = source;
00426
00427 bool result = target->accept( *this );
00428
00429 d->mSource = 0;
00430
00431 return result;
00432 }
00433
00434 bool AssignmentVisitor::visit( Event *event )
00435 {
00436 Q_ASSERT( event != 0 );
00437
00438 const Event *source = dynamic_cast<const Event*>( d->mSource );
00439 if ( source == 0 ) {
00440 kError(5800) << "Type mismatch: source is" << d->mSource->type()
00441 << "target is" << event->type();
00442 return false;
00443 }
00444
00445 *event = *source;
00446 return true;
00447 }
00448
00449 bool AssignmentVisitor::visit( Todo *todo )
00450 {
00451 Q_ASSERT( todo != 0 );
00452
00453 const Todo *source = dynamic_cast<const Todo*>( d->mSource );
00454 if ( source == 0 ) {
00455 kError(5800) << "Type mismatch: source is" << d->mSource->type()
00456 << "target is" << todo->type();
00457 return false;
00458 }
00459
00460 *todo = *source;
00461 return true;
00462 }
00463
00464 bool AssignmentVisitor::visit( Journal *journal )
00465 {
00466 Q_ASSERT( journal != 0 );
00467
00468 const Journal *source = dynamic_cast<const Journal*>( d->mSource );
00469 if ( source == 0 ) {
00470 kError(5800) << "Type mismatch: source is" << d->mSource->type()
00471 << "target is" << journal->type();
00472 return false;
00473 }
00474
00475 *journal = *source;
00476 return true;
00477 }
00478
00479 bool AssignmentVisitor::visit( FreeBusy *freebusy )
00480 {
00481 Q_ASSERT( freebusy != 0 );
00482
00483 const FreeBusy *source = dynamic_cast<const FreeBusy*>( d->mSource );
00484 if ( source == 0 ) {
00485 kError(5800) << "Type mismatch: source is" << d->mSource->type()
00486 << "target is" << freebusy->type();
00487 return false;
00488 }
00489
00490 *freebusy = *source;
00491 return true;
00492 }
00493
00494 class ComparisonVisitor::Private
00495 {
00496 public:
00497 Private() : mReference( 0 ) {}
00498
00499 public:
00500 const IncidenceBase *mReference;
00501 };
00502
00503 ComparisonVisitor::ComparisonVisitor() : d( new Private() )
00504 {
00505 }
00506
00507 ComparisonVisitor::~ComparisonVisitor()
00508 {
00509 delete d;
00510 }
00511
00512 bool ComparisonVisitor::compare( IncidenceBase *incidence, const IncidenceBase *reference )
00513 {
00514 d->mReference = reference;
00515
00516 const bool result = incidence ? incidence->accept( *this ) : reference == 0;
00517
00518 d->mReference = 0;
00519
00520 return result;
00521 }
00522
00523 bool ComparisonVisitor::visit( Event *event )
00524 {
00525 Q_ASSERT( event != 0 );
00526
00527 const Event *refEvent = dynamic_cast<const Event*>( d->mReference );
00528 if ( refEvent ) {
00529 return *event == *refEvent;
00530 } else {
00531
00532 return false;
00533 }
00534 }
00535
00536 bool ComparisonVisitor::visit( Todo *todo )
00537 {
00538 Q_ASSERT( todo != 0 );
00539
00540 const Todo *refTodo = dynamic_cast<const Todo*>( d->mReference );
00541 if ( refTodo ) {
00542 return *todo == *refTodo;
00543 } else {
00544
00545 return false;
00546 }
00547 }
00548
00549 bool ComparisonVisitor::visit( Journal *journal )
00550 {
00551 Q_ASSERT( journal != 0 );
00552
00553 const Journal *refJournal = dynamic_cast<const Journal*>( d->mReference );
00554 if ( refJournal ) {
00555 return *journal == *refJournal;
00556 } else {
00557
00558 return false;
00559 }
00560 }
00561
00562 bool ComparisonVisitor::visit( FreeBusy *freebusy )
00563 {
00564 Q_ASSERT( freebusy != 0 );
00565
00566 const FreeBusy *refFreeBusy = dynamic_cast<const FreeBusy*>( d->mReference );
00567 if ( refFreeBusy ) {
00568 return *freebusy == *refFreeBusy;
00569 } else {
00570
00571 return false;
00572 }
00573 }
00574