KCalCore Library
icalformat.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the kcalcore library. 00003 00004 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00031 #include "icalformat.h" 00032 #include "icalformat_p.h" 00033 #include "icaltimezones.h" 00034 #include "freebusy.h" 00035 #include "memorycalendar.h" 00036 00037 #include <KDebug> 00038 #include <KSaveFile> 00039 00040 #include <QtCore/QFile> 00041 00042 extern "C" { 00043 #include <libical/ical.h> 00044 #include <libical/icalss.h> 00045 #include <libical/icalparser.h> 00046 #include <libical/icalrestriction.h> 00047 #include <libical/icalmemory.h> 00048 } 00049 00050 using namespace KCalCore; 00051 00052 //@cond PRIVATE 00053 class KCalCore::ICalFormat::Private 00054 { 00055 public: 00056 Private( ICalFormat *parent ) 00057 : mImpl( new ICalFormatImpl( parent ) ), 00058 mTimeSpec( KDateTime::UTC ) 00059 {} 00060 ~Private() { delete mImpl; } 00061 ICalFormatImpl *mImpl; 00062 KDateTime::Spec mTimeSpec; 00063 }; 00064 //@endcond 00065 00066 ICalFormat::ICalFormat() 00067 : d( new Private( this ) ) 00068 { 00069 } 00070 00071 ICalFormat::~ICalFormat() 00072 { 00073 icalmemory_free_ring(); 00074 delete d; 00075 } 00076 00077 bool ICalFormat::load( const Calendar::Ptr &calendar, const QString &fileName ) 00078 { 00079 kDebug() << fileName; 00080 00081 clearException(); 00082 00083 QFile file( fileName ); 00084 if ( !file.open( QIODevice::ReadOnly ) ) { 00085 kDebug() << "load error"; 00086 setException( new Exception( Exception::LoadError ) ); 00087 return false; 00088 } 00089 QTextStream ts( &file ); 00090 ts.setCodec( "UTF-8" ); 00091 QByteArray text = ts.readAll().trimmed().toUtf8(); 00092 file.close(); 00093 00094 if ( text.isEmpty() ) { 00095 // empty files are valid 00096 return true; 00097 } else { 00098 return fromRawString( calendar, text, false, fileName ); 00099 } 00100 } 00101 00102 bool ICalFormat::save( const Calendar::Ptr &calendar, const QString &fileName ) 00103 { 00104 kDebug() << fileName; 00105 00106 clearException(); 00107 00108 QString text = toString( calendar ); 00109 if ( text.isEmpty() ) { 00110 return false; 00111 } 00112 00113 // Write backup file 00114 KSaveFile::backupFile( fileName ); 00115 00116 KSaveFile file( fileName ); 00117 if ( !file.open() ) { 00118 kDebug() << "file open error:" << file.errorString(); 00119 setException( new Exception( Exception::SaveErrorOpenFile, 00120 QStringList( fileName ) ) ); 00121 00122 return false; 00123 } 00124 00125 // Convert to UTF8 and save 00126 QByteArray textUtf8 = text.toUtf8(); 00127 file.write( textUtf8.data(), textUtf8.size() ); 00128 00129 if ( !file.finalize() ) { 00130 kDebug() << "file finalize error:" << file.errorString(); 00131 setException( new Exception( Exception::SaveErrorSaveFile, 00132 QStringList( fileName ) ) ); 00133 00134 return false; 00135 } 00136 00137 return true; 00138 } 00139 00140 bool ICalFormat::fromString( const Calendar::Ptr &cal, const QString &string, 00141 bool deleted, const QString ¬ebook ) 00142 { 00143 return fromRawString( cal, string.toUtf8(), deleted, notebook ); 00144 } 00145 00146 bool ICalFormat::fromRawString( const Calendar::Ptr &cal, const QByteArray &string, 00147 bool deleted, const QString ¬ebook ) 00148 { 00149 Q_UNUSED( notebook ); 00150 // Get first VCALENDAR component. 00151 // TODO: Handle more than one VCALENDAR or non-VCALENDAR top components 00152 icalcomponent *calendar; 00153 00154 // Let's defend const correctness until the very gates of hell^Wlibical 00155 calendar = icalcomponent_new_from_string( const_cast<char*>( ( const char * )string ) ); 00156 if ( !calendar ) { 00157 kDebug() << "parse error"; 00158 setException( new Exception( Exception::ParseErrorIcal ) ); 00159 return false; 00160 } 00161 00162 bool success = true; 00163 00164 if ( icalcomponent_isa( calendar ) == ICAL_XROOT_COMPONENT ) { 00165 icalcomponent *comp; 00166 for ( comp = icalcomponent_get_first_component( calendar, ICAL_VCALENDAR_COMPONENT ); 00167 comp; comp = icalcomponent_get_next_component( calendar, ICAL_VCALENDAR_COMPONENT ) ) { 00168 // put all objects into their proper places 00169 if ( !d->mImpl->populate( cal, comp, deleted ) ) { 00170 kDebug() << "Could not populate calendar"; 00171 if ( !exception() ) { 00172 setException( new Exception( Exception::ParseErrorKcal ) ); 00173 } 00174 success = false; 00175 } else { 00176 setLoadedProductId( d->mImpl->loadedProductId() ); 00177 } 00178 } 00179 } else if ( icalcomponent_isa( calendar ) != ICAL_VCALENDAR_COMPONENT ) { 00180 kDebug() << "No VCALENDAR component found"; 00181 setException( new Exception( Exception::NoCalendar ) ); 00182 success = false; 00183 } else { 00184 // put all objects into their proper places 00185 if ( !d->mImpl->populate( cal, calendar, deleted ) ) { 00186 kDebug() << "Could not populate calendar"; 00187 if ( !exception() ) { 00188 setException( new Exception( Exception::ParseErrorKcal ) ); 00189 } 00190 success = false; 00191 } else { 00192 setLoadedProductId( d->mImpl->loadedProductId() ); 00193 } 00194 } 00195 00196 icalcomponent_free( calendar ); 00197 icalmemory_free_ring(); 00198 00199 return success; 00200 } 00201 00202 Incidence::Ptr ICalFormat::fromString( const QString &string ) 00203 { 00204 MemoryCalendar::Ptr cal( new MemoryCalendar( d->mTimeSpec ) ); 00205 fromString( cal, string ); 00206 00207 Incidence::Ptr ical; 00208 Event::List elist = cal->events(); 00209 if ( elist.count() > 0 ) { 00210 ical = elist.first(); 00211 } else { 00212 Todo::List tlist = cal->todos(); 00213 if ( tlist.count() > 0 ) { 00214 ical = tlist.first(); 00215 } else { 00216 Journal::List jlist = cal->journals(); 00217 if ( jlist.count() > 0 ) { 00218 ical = jlist.first(); 00219 } 00220 } 00221 } 00222 00223 return ical ? Incidence::Ptr( ical->clone() ) : Incidence::Ptr(); 00224 } 00225 00226 QString ICalFormat::toString( const Calendar::Ptr &cal, 00227 const QString ¬ebook, bool deleted ) 00228 { 00229 icalcomponent *calendar = d->mImpl->createCalendarComponent( cal ); 00230 icalcomponent *component; 00231 00232 ICalTimeZones *tzlist = cal->timeZones(); // time zones possibly used in the calendar 00233 ICalTimeZones tzUsedList; // time zones actually used in the calendar 00234 00235 // todos 00236 Todo::List todoList = deleted ? cal->deletedTodos() : cal->rawTodos(); 00237 Todo::List::ConstIterator it; 00238 for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) { 00239 if ( !deleted || !cal->todo( (*it)->uid(), (*it)->recurrenceId() ) ) { 00240 // use existing ones, or really deleted ones 00241 if ( notebook.isEmpty() || 00242 ( !cal->notebook( *it ).isEmpty() && notebook.endsWith( cal->notebook( *it ) ) ) ) { 00243 component = d->mImpl->writeTodo( *it, tzlist, &tzUsedList ); 00244 icalcomponent_add_component( calendar, component ); 00245 } 00246 } 00247 } 00248 // events 00249 Event::List events = deleted ? cal->deletedEvents() : cal->rawEvents(); 00250 Event::List::ConstIterator it2; 00251 for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) { 00252 if ( !deleted || !cal->event( (*it2)->uid(), (*it2)->recurrenceId() ) ) { 00253 // use existing ones, or really deleted ones 00254 if ( notebook.isEmpty() || 00255 ( !cal->notebook( *it2 ).isEmpty() && notebook.endsWith( cal->notebook( *it2 ) ) ) ) { 00256 component = d->mImpl->writeEvent( *it2, tzlist, &tzUsedList ); 00257 icalcomponent_add_component( calendar, component ); 00258 } 00259 } 00260 } 00261 00262 // journals 00263 Journal::List journals = deleted ? cal->deletedJournals() : cal->rawJournals(); 00264 Journal::List::ConstIterator it3; 00265 for ( it3 = journals.constBegin(); it3 != journals.constEnd(); ++it3 ) { 00266 if ( !deleted || !cal->journal( (*it3)->uid(), (*it3)->recurrenceId() ) ) { 00267 // use existing ones, or really deleted ones 00268 if ( notebook.isEmpty() || 00269 ( !cal->notebook( *it3 ).isEmpty() && notebook.endsWith( cal->notebook( *it3 ) ) ) ) { 00270 component = d->mImpl->writeJournal( *it3, tzlist, &tzUsedList ); 00271 icalcomponent_add_component( calendar, component ); 00272 } 00273 } 00274 } 00275 00276 // time zones 00277 ICalTimeZones::ZoneMap zones = tzUsedList.zones(); 00278 if ( todoList.isEmpty() && events.isEmpty() && journals.isEmpty() ) { 00279 // no incidences means no used timezones, use all timezones 00280 // this will export a calendar having only timezone definitions 00281 zones = tzlist->zones(); 00282 } 00283 for ( ICalTimeZones::ZoneMap::ConstIterator it=zones.constBegin(); 00284 it != zones.constEnd(); ++it ) { 00285 icaltimezone *tz = (*it).icalTimezone(); 00286 if ( !tz ) { 00287 kError() << "bad time zone"; 00288 } else { 00289 component = icalcomponent_new_clone( icaltimezone_get_component( tz ) ); 00290 icalcomponent_add_component( calendar, component ); 00291 icaltimezone_free( tz, 1 ); 00292 } 00293 } 00294 00295 char *const componentString = icalcomponent_as_ical_string_r( calendar ); 00296 const QString &text = QString::fromUtf8( componentString ); 00297 free(componentString); 00298 00299 icalcomponent_free( calendar ); 00300 icalmemory_free_ring(); 00301 00302 if ( text.isEmpty() ) { 00303 setException( new Exception( Exception::LibICalError ) ); 00304 } 00305 00306 return text; 00307 } 00308 00309 QString ICalFormat::toICalString( const Incidence::Ptr &incidence ) 00310 { 00311 MemoryCalendar::Ptr cal( new MemoryCalendar( d->mTimeSpec ) ); 00312 cal->addIncidence( Incidence::Ptr( incidence->clone() ) ); 00313 return toString( cal.staticCast<Calendar>() ); 00314 } 00315 00316 QString ICalFormat::toString( const Incidence::Ptr &incidence ) 00317 { 00318 return QString::fromUtf8( toRawString( incidence ) ); 00319 } 00320 00321 QByteArray ICalFormat::toRawString( const Incidence::Ptr &incidence ) 00322 { 00323 icalcomponent *component; 00324 ICalTimeZones tzlist; 00325 ICalTimeZones tzUsedList; 00326 00327 component = d->mImpl->writeIncidence( incidence, iTIPRequest, &tzlist, &tzUsedList ); 00328 00329 QByteArray text = icalcomponent_as_ical_string( component ); 00330 00331 // time zones 00332 ICalTimeZones::ZoneMap zones = tzUsedList.zones(); 00333 for ( ICalTimeZones::ZoneMap::ConstIterator it=zones.constBegin(); 00334 it != zones.constEnd(); ++it ) { 00335 icaltimezone *tz = (*it).icalTimezone(); 00336 if ( !tz ) { 00337 kError() << "bad time zone"; 00338 } else { 00339 icalcomponent *tzcomponent = icaltimezone_get_component( tz ); 00340 icalcomponent_add_component( component, component ); 00341 text.append( icalcomponent_as_ical_string( tzcomponent ) ); 00342 icaltimezone_free( tz, 1 ); 00343 } 00344 } 00345 00346 icalcomponent_free( component ); 00347 00348 return text; 00349 } 00350 00351 QString ICalFormat::toString( RecurrenceRule *recurrence ) 00352 { 00353 icalproperty *property; 00354 property = icalproperty_new_rrule( d->mImpl->writeRecurrenceRule( recurrence ) ); 00355 QString text = QString::fromUtf8( icalproperty_as_ical_string( property ) ); 00356 icalproperty_free( property ); 00357 return text; 00358 } 00359 00360 bool ICalFormat::fromString( RecurrenceRule *recurrence, const QString &rrule ) 00361 { 00362 if ( !recurrence ) { 00363 return false; 00364 } 00365 bool success = true; 00366 icalerror_clear_errno(); 00367 struct icalrecurrencetype recur = icalrecurrencetype_from_string( rrule.toLatin1() ); 00368 if ( icalerrno != ICAL_NO_ERROR ) { 00369 kDebug() << "Recurrence parsing error:" << icalerror_strerror( icalerrno ); 00370 success = false; 00371 } 00372 00373 if ( success ) { 00374 d->mImpl->readRecurrence( recur, recurrence ); 00375 } 00376 00377 return success; 00378 } 00379 00380 QString ICalFormat::createScheduleMessage( const IncidenceBase::Ptr &incidence, 00381 iTIPMethod method ) 00382 { 00383 icalcomponent *message = 0; 00384 00385 if ( incidence->type() == Incidence::TypeEvent || 00386 incidence->type() == Incidence::TypeTodo ) { 00387 00388 Incidence::Ptr i = incidence.staticCast<Incidence>(); 00389 00390 // Recurring events need timezone information to allow proper calculations 00391 // across timezones with different DST. 00392 const bool useUtcTimes = !i->recurs(); 00393 00394 const bool hasSchedulingId = ( i->schedulingID() != i->uid() ); 00395 00396 const bool incidenceNeedChanges = ( useUtcTimes || hasSchedulingId ); 00397 00398 if ( incidenceNeedChanges ) { 00399 // The incidence need changes, so clone it before we continue 00400 i = Incidence::Ptr( i->clone() ); 00401 00402 // Handle conversion to UTC times 00403 if ( useUtcTimes ) { 00404 i->shiftTimes( KDateTime::Spec::UTC(), KDateTime::Spec::UTC() ); 00405 } 00406 00407 // Handle scheduling ID being present 00408 if ( hasSchedulingId ) { 00409 // We have a separation of scheduling ID and UID 00410 i->setSchedulingID( QString(), i->schedulingID() ); 00411 00412 } 00413 00414 // Build the message with the cloned incidence 00415 message = d->mImpl->createScheduleComponent( i, method ); 00416 } 00417 } 00418 00419 if ( message == 0 ) { 00420 message = d->mImpl->createScheduleComponent( incidence, method ); 00421 } 00422 00423 QString messageText = QString::fromUtf8( icalcomponent_as_ical_string( message ) ); 00424 00425 icalcomponent_free( message ); 00426 return messageText; 00427 } 00428 00429 FreeBusy::Ptr ICalFormat::parseFreeBusy( const QString &str ) 00430 { 00431 clearException(); 00432 00433 icalcomponent *message; 00434 message = icalparser_parse_string( str.toUtf8() ); 00435 00436 if ( !message ) { 00437 return FreeBusy::Ptr(); 00438 } 00439 00440 FreeBusy::Ptr freeBusy; 00441 00442 icalcomponent *c; 00443 for ( c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT ); 00444 c != 0; c = icalcomponent_get_next_component( message, ICAL_VFREEBUSY_COMPONENT ) ) { 00445 FreeBusy::Ptr fb = d->mImpl->readFreeBusy( c ); 00446 00447 if ( freeBusy ) { 00448 freeBusy->merge( fb ); 00449 } else { 00450 freeBusy = fb; 00451 } 00452 } 00453 00454 if ( !freeBusy ) { 00455 kDebug() << "object is not a freebusy."; 00456 } 00457 00458 icalcomponent_free( message ); 00459 00460 return freeBusy; 00461 } 00462 00463 ScheduleMessage::Ptr ICalFormat::parseScheduleMessage( const Calendar::Ptr &cal, 00464 const QString &messageText ) 00465 { 00466 setTimeSpec( cal->timeSpec() ); 00467 clearException(); 00468 00469 if ( messageText.isEmpty() ) { 00470 setException( 00471 new Exception( Exception::ParseErrorEmptyMessage ) ); 00472 return ScheduleMessage::Ptr(); 00473 } 00474 00475 icalcomponent *message; 00476 message = icalparser_parse_string( messageText.toUtf8() ); 00477 00478 if ( !message ) { 00479 setException( 00480 new Exception( Exception::ParseErrorUnableToParse ) ); 00481 00482 return ScheduleMessage::Ptr(); 00483 } 00484 00485 icalproperty *m = 00486 icalcomponent_get_first_property( message, ICAL_METHOD_PROPERTY ); 00487 if ( !m ) { 00488 setException( 00489 new Exception( Exception::ParseErrorMethodProperty ) ); 00490 00491 return ScheduleMessage::Ptr(); 00492 } 00493 00494 // Populate the message's time zone collection with all VTIMEZONE components 00495 ICalTimeZones tzlist; 00496 ICalTimeZoneSource tzs; 00497 tzs.parse( message, tzlist ); 00498 00499 icalcomponent *c; 00500 00501 IncidenceBase::Ptr incidence; 00502 c = icalcomponent_get_first_component( message, ICAL_VEVENT_COMPONENT ); 00503 if ( c ) { 00504 incidence = d->mImpl->readEvent( c, &tzlist ).staticCast<IncidenceBase>(); 00505 } 00506 00507 if ( !incidence ) { 00508 c = icalcomponent_get_first_component( message, ICAL_VTODO_COMPONENT ); 00509 if ( c ) { 00510 incidence = d->mImpl->readTodo( c, &tzlist ).staticCast<IncidenceBase>(); 00511 } 00512 } 00513 00514 if ( !incidence ) { 00515 c = icalcomponent_get_first_component( message, ICAL_VJOURNAL_COMPONENT ); 00516 if ( c ) { 00517 incidence = d->mImpl->readJournal( c, &tzlist ).staticCast<IncidenceBase>(); 00518 } 00519 } 00520 00521 if ( !incidence ) { 00522 c = icalcomponent_get_first_component( message, ICAL_VFREEBUSY_COMPONENT ); 00523 if ( c ) { 00524 incidence = d->mImpl->readFreeBusy( c ).staticCast<IncidenceBase>(); 00525 } 00526 } 00527 00528 if ( !incidence ) { 00529 kDebug() << "object is not a freebusy, event, todo or journal"; 00530 setException( new Exception( Exception::ParseErrorNotIncidence ) ); 00531 00532 return ScheduleMessage::Ptr(); 00533 } 00534 00535 icalproperty_method icalmethod = icalproperty_get_method( m ); 00536 iTIPMethod method; 00537 00538 switch ( icalmethod ) { 00539 case ICAL_METHOD_PUBLISH: 00540 method = iTIPPublish; 00541 break; 00542 case ICAL_METHOD_REQUEST: 00543 method = iTIPRequest; 00544 break; 00545 case ICAL_METHOD_REFRESH: 00546 method = iTIPRefresh; 00547 break; 00548 case ICAL_METHOD_CANCEL: 00549 method = iTIPCancel; 00550 break; 00551 case ICAL_METHOD_ADD: 00552 method = iTIPAdd; 00553 break; 00554 case ICAL_METHOD_REPLY: 00555 method = iTIPReply; 00556 break; 00557 case ICAL_METHOD_COUNTER: 00558 method = iTIPCounter; 00559 break; 00560 case ICAL_METHOD_DECLINECOUNTER: 00561 method = iTIPDeclineCounter; 00562 break; 00563 default: 00564 method = iTIPNoMethod; 00565 kDebug() << "Unknown method"; 00566 break; 00567 } 00568 00569 if ( !icalrestriction_check( message ) ) { 00570 kWarning() << endl 00571 << "kcalcore library reported a problem while parsing:"; 00572 kWarning() << ScheduleMessage::methodName( method ) << ":" //krazy:exclude=kdebug 00573 << d->mImpl->extractErrorProperty( c ); 00574 } 00575 00576 Incidence::Ptr existingIncidence = cal->incidence( incidence->uid() ); 00577 00578 icalcomponent *calendarComponent = 0; 00579 if ( existingIncidence ) { 00580 calendarComponent = d->mImpl->createCalendarComponent( cal ); 00581 00582 // TODO: check, if cast is required, or if it can be done by virtual funcs. 00583 // TODO: Use a visitor for this! 00584 if ( existingIncidence->type() == Incidence::TypeTodo ) { 00585 Todo::Ptr todo = existingIncidence.staticCast<Todo>(); 00586 icalcomponent_add_component( calendarComponent, 00587 d->mImpl->writeTodo( todo ) ); 00588 } 00589 if ( existingIncidence->type() == Incidence::TypeEvent ) { 00590 Event::Ptr event = existingIncidence.staticCast<Event>(); 00591 icalcomponent_add_component( calendarComponent, 00592 d->mImpl->writeEvent( event ) ); 00593 } 00594 } else { 00595 icalcomponent_free( message ); 00596 return ScheduleMessage::Ptr( new ScheduleMessage( incidence, method, 00597 ScheduleMessage::Unknown ) ); 00598 } 00599 00600 icalproperty_xlicclass result = 00601 icalclassify( message, calendarComponent, static_cast<const char *>( "" ) ); 00602 00603 ScheduleMessage::Status status; 00604 00605 switch ( result ) { 00606 case ICAL_XLICCLASS_PUBLISHNEW: 00607 status = ScheduleMessage::PublishNew; 00608 break; 00609 case ICAL_XLICCLASS_PUBLISHUPDATE: 00610 status = ScheduleMessage::PublishUpdate; 00611 break; 00612 case ICAL_XLICCLASS_OBSOLETE: 00613 status = ScheduleMessage::Obsolete; 00614 break; 00615 case ICAL_XLICCLASS_REQUESTNEW: 00616 status = ScheduleMessage::RequestNew; 00617 break; 00618 case ICAL_XLICCLASS_REQUESTUPDATE: 00619 status = ScheduleMessage::RequestUpdate; 00620 break; 00621 case ICAL_XLICCLASS_UNKNOWN: 00622 default: 00623 status = ScheduleMessage::Unknown; 00624 break; 00625 } 00626 00627 icalcomponent_free( message ); 00628 icalcomponent_free( calendarComponent ); 00629 00630 return ScheduleMessage::Ptr( new ScheduleMessage( incidence, method, status ) ); 00631 } 00632 00633 void ICalFormat::setTimeSpec( const KDateTime::Spec &timeSpec ) 00634 { 00635 d->mTimeSpec = timeSpec; 00636 } 00637 00638 KDateTime::Spec ICalFormat::timeSpec() const 00639 { 00640 return d->mTimeSpec; 00641 } 00642 00643 QString ICalFormat::timeZoneId() const 00644 { 00645 KTimeZone tz = d->mTimeSpec.timeZone(); 00646 return tz.isValid() ? tz.name() : QString(); 00647 } 00648 00649 void ICalFormat::virtual_hook( int id, void *data ) 00650 { 00651 Q_UNUSED( id ); 00652 Q_UNUSED( data ); 00653 Q_ASSERT( false ); 00654 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Apr 30 2012 21:48:21 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Apr 30 2012 21:48:21 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.