libkcal Library API Documentation

htmlexport.cpp

00001 /* 00002 This file is part of libkcal. 00003 00004 Copyright (c) 2000, 2001 Cornelius Schumacher <schumacher@kde.org> 00005 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 00011 This program 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 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00019 00020 As a special exception, permission is given to link this program 00021 with any edition of Qt, and distribute the resulting executable, 00022 without including the source code for Qt in the source distribution. 00023 */ 00024 00025 #include <qapplication.h> 00026 #include <qfile.h> 00027 #include <qtextstream.h> 00028 #include <qtextcodec.h> 00029 #include <qregexp.h> 00030 00031 #include <kglobal.h> 00032 #include <klocale.h> 00033 #include <kdebug.h> 00034 #include <kcalendarsystem.h> 00035 00036 #include <libkcal/calendar.h> 00037 #include <libkcal/event.h> 00038 #include <libkcal/todo.h> 00039 00040 #ifndef KORG_NOKABC 00041 #include <kabc/stdaddressbook.h> 00042 #endif 00043 #include "htmlexport.h" 00044 00045 using namespace KCal; 00046 00047 HtmlExport::HtmlExport( Calendar *calendar ) : 00048 mCalendar( calendar ), 00049 mMonthViewEnabled( true ), mEventsEnabled( false ), mTodosEnabled( true ), 00050 mCategoriesTodoEnabled( false ), mAttendeesTodoEnabled( false ), 00051 mCategoriesEventEnabled( false ), mAttendeesEventEnabled( false ), 00052 mDueDateEnabled( false ), 00053 mExcludePrivateTodoEnabled( false ), 00054 mExcludeConfidentialTodoEnabled( false ), 00055 mExcludePrivateEventEnabled( false ), 00056 mExcludeConfidentialEventEnabled( false ) 00057 { 00058 mTitle = i18n("KOrganizer Calendar"); 00059 mTitleTodo = i18n("KOrganizer To-Do List"); 00060 mCreditName = "KOrganizer"; 00061 mCreditURL = "http://korganizer.kde.org"; 00062 } 00063 00064 bool HtmlExport::save(const QString &fileName) 00065 { 00066 QFile f(fileName); 00067 if (!f.open(IO_WriteOnly)) { 00068 return false; 00069 } 00070 QTextStream ts(&f); 00071 bool success = save(&ts); 00072 f.close(); 00073 return success; 00074 } 00075 00076 bool HtmlExport::save(QTextStream *ts) 00077 { 00078 ts->setEncoding(QTextStream::UnicodeUTF8); 00079 00080 // Write HTML header 00081 *ts << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "; 00082 *ts << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 00083 00084 *ts << "<html><head>" << endl; 00085 *ts << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset="; 00086 *ts << "UTF-8\" />\n"; 00087 if (!mTitle.isEmpty()) 00088 *ts << " <title>" << mTitle << "</title>\n"; 00089 *ts << " <style type=\"text/css\">\n"; 00090 *ts << styleSheet(); 00091 *ts << " </style>\n"; 00092 *ts << "</head><body>\n"; 00093 00094 // TO DO: Write header 00095 // (Heading, Calendar-Owner, Calendar-Date, ...) 00096 00097 if (eventsEnabled() || monthViewEnabled()) { 00098 if (!mTitle.isEmpty()) 00099 *ts << "<h1>" << mTitle << "</h1>\n"; 00100 } 00101 00102 // Write Month View 00103 if (monthViewEnabled()) { 00104 createHtmlMonthView(ts); 00105 } 00106 00107 // Write Event List 00108 if (eventsEnabled()) { 00109 // Write HTML page content 00110 createHtmlEventList(ts); 00111 } 00112 00113 // Write Todo List 00114 if (todosEnabled()) { 00115 if (!mTitleTodo.isEmpty()) 00116 *ts << "<h1>" << mTitleTodo << "</h1>\n"; 00117 00118 // Write HTML page content 00119 createHtmlTodoList(ts); 00120 } 00121 00122 // Write trailer 00123 QString trailer = i18n("This page was created "); 00124 00125 if (!mEmail.isEmpty()) { 00126 if (!mName.isEmpty()) 00127 trailer += i18n("by <a href=\"mailto:%1\">%2</a> ").arg( mEmail ).arg( mName ); 00128 else 00129 trailer += i18n("by <a href=\"mailto:%1\">%2</a> ").arg( mEmail ).arg( mEmail ); 00130 } else { 00131 if (!mName.isEmpty()) 00132 trailer += i18n("by %1 ").arg( mName ); 00133 } 00134 if (!mCreditName.isEmpty()) { 00135 if (!mCreditURL.isEmpty()) 00136 trailer += i18n("with <a href=\"%1\">%2</a>").arg( mCreditURL ).arg( mCreditName ); 00137 else 00138 trailer += i18n("with %1").arg( mCreditName ); 00139 } 00140 *ts << "<p>" << trailer << "</p>\n"; 00141 00142 // Write HTML trailer 00143 *ts << "</body></html>\n"; 00144 00145 return true; 00146 } 00147 00148 void HtmlExport::createHtmlMonthView(QTextStream *ts) 00149 { 00150 QDate start = fromDate(); 00151 start.setYMD(start.year(),start.month(),1); // go back to first day in month 00152 00153 QDate end(start.year(),start.month(),start.daysInMonth()); 00154 00155 int startmonth = start.month(); 00156 int startyear = start.year(); 00157 00158 while ( start < toDate() ) { 00159 // Write header 00160 *ts << "<h2>" << (i18n("month_year","%1 %2").arg(KGlobal::locale()->calendar()->monthName(start)) 00161 .arg(start.year())) << "</h2>\n"; 00162 if ( KGlobal::locale()->weekStartDay() == 1 ) { 00163 start = start.addDays(1 - start.dayOfWeek()); 00164 } else { 00165 if (start.dayOfWeek() != 7) { 00166 start = start.addDays(-start.dayOfWeek()); 00167 } 00168 } 00169 *ts << "<table border=\"1\">\n"; 00170 00171 // Write table header 00172 *ts << " <tr>"; 00173 for(int i=0; i<7; ++i) { 00174 *ts << "<th>" << KGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << "</th>"; 00175 } 00176 *ts << "</tr>\n"; 00177 00178 // Write days 00179 while (start <= end) { 00180 *ts << " <tr>\n"; 00181 for(int i=0;i<7;++i) { 00182 *ts << " <td valign=\"top\"><table border=\"0\">"; 00183 00184 *ts << "<tr><td "; 00185 if (mHolidayMap.contains(start) || start.dayOfWeek() == 7) { 00186 *ts << "class=\"dateholiday\""; 00187 } else { 00188 *ts << "class=\"date\""; 00189 } 00190 *ts << ">" << QString::number(start.day()); 00191 00192 if (mHolidayMap.contains(start)) { 00193 *ts << " <em>" << mHolidayMap[start] << "</em>"; 00194 } 00195 00196 *ts << "</td></tr><tr><td valign=\"top\">"; 00197 00198 Event::List events = mCalendar->events(start,true); 00199 if (events.count()) { 00200 *ts << "<table>"; 00201 Event::List::ConstIterator it; 00202 for( it = events.begin(); it != events.end(); ++it ) { 00203 if ( checkSecrecy( *it ) ) { 00204 createHtmlEvent( ts, *it, start, false ); 00205 } 00206 } 00207 *ts << "</table>"; 00208 } else { 00209 *ts << "&nbsp;"; 00210 } 00211 00212 *ts << "</td></tr></table></td>\n"; 00213 start = start.addDays(1); 00214 } 00215 *ts << " </tr>\n"; 00216 } 00217 *ts << "</table>\n"; 00218 startmonth += 1; 00219 if ( startmonth > 12 ) { 00220 startyear += 1; 00221 startmonth = 1; 00222 } 00223 start.setYMD( startyear, startmonth, 1 ); 00224 end.setYMD(start.year(),start.month(),start.daysInMonth()); 00225 } 00226 } 00227 00228 void HtmlExport::createHtmlEventList (QTextStream *ts) 00229 { 00230 *ts << "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">\n"; 00231 *ts << " <tr>\n"; 00232 *ts << " <th class=\"sum\">" << i18n("Start Time") << "</th>\n"; 00233 *ts << " <th>" << i18n("End Time") << "</th>\n"; 00234 *ts << " <th>" << i18n("Event") << "</th>\n"; 00235 if (categoriesEventEnabled()) { 00236 *ts << " <th>" << i18n("Categories") << "</th>\n"; 00237 } 00238 if (attendeesEventEnabled()) { 00239 *ts << " <th>" << i18n("Attendees") << "</th>\n"; 00240 } 00241 00242 *ts << " </tr>\n"; 00243 00244 int columns = 3; 00245 if (categoriesEventEnabled()) ++columns; 00246 if (attendeesEventEnabled()) ++columns; 00247 00248 for (QDate dt = fromDate(); dt <= toDate(); dt = dt.addDays(1)) { 00249 kdDebug(5850) << "Getting events for " << dt.toString() << endl; 00250 Event::List events = mCalendar->events(dt,true); 00251 if (events.count()) { 00252 *ts << " <tr><td colspan=\"" << QString::number(columns) 00253 << "\" class=\"datehead\"><i>" 00254 << KGlobal::locale()->formatDate(dt) 00255 << "</i></td></tr>\n"; 00256 00257 Event::List::ConstIterator it; 00258 for( it = events.begin(); it != events.end(); ++it ) { 00259 if ( checkSecrecy( *it ) ) { 00260 createHtmlEvent( ts, *it, dt ); 00261 } 00262 } 00263 } 00264 } 00265 00266 *ts << "</table>\n"; 00267 } 00268 00269 void HtmlExport::createHtmlEvent (QTextStream *ts, Event *event, 00270 QDate date,bool withDescription) 00271 { 00272 kdDebug(5850) << "HtmlExport::createHtmlEvent(): " << event->summary() << endl; 00273 *ts << " <tr>\n"; 00274 00275 if (!event->doesFloat()) { 00276 if (event->isMultiDay() && (event->dtStart().date() != date)) { 00277 *ts << " <td>&nbsp;</td>\n"; 00278 } else { 00279 *ts << " <td valign=\"top\">" << event->dtStartTimeStr() << "</td>\n"; 00280 } 00281 if (event->isMultiDay() && (event->dtEnd().date() != date)) { 00282 *ts << " <td>&nbsp;</td>\n"; 00283 } else { 00284 *ts << " <td valign=\"top\">" << event->dtEndTimeStr() << "</td>\n"; 00285 } 00286 } else { 00287 *ts << " <td>&nbsp;</td><td>&nbsp;</td>\n"; 00288 } 00289 00290 *ts << " <td class=\"sum\">\n"; 00291 *ts << " <b>" << cleanChars(event->summary()) << "</b>\n"; 00292 if (withDescription && !event->description().isEmpty()) { 00293 *ts << " <p>" << breakString(cleanChars(event->description())) << "</p>\n"; 00294 } 00295 *ts << " </td>\n"; 00296 00297 if (categoriesEventEnabled()) { 00298 *ts << " <td>\n"; 00299 formatHtmlCategories(ts,event); 00300 *ts << " </td>\n"; 00301 } 00302 00303 if (attendeesEventEnabled()) { 00304 *ts << " <td>\n"; 00305 formatHtmlAttendees(ts,event); 00306 *ts << " </td>\n"; 00307 } 00308 00309 *ts << " </tr>\n"; 00310 } 00311 00312 void HtmlExport::createHtmlTodoList ( QTextStream *ts ) 00313 { 00314 Todo::List rawTodoList = mCalendar->todos(); 00315 00316 Todo::List::Iterator it = rawTodoList.begin(); 00317 while ( it != rawTodoList.end() ) { 00318 Todo *ev = *it; 00319 Todo *subev = ev; 00320 if ( ev->relatedTo() ) { 00321 if ( ev->relatedTo()->type()=="Todo" ) { 00322 if ( rawTodoList.find( static_cast<Todo *>( ev->relatedTo() ) ) == 00323 rawTodoList.end() ) { 00324 rawTodoList.append( static_cast<Todo *>( ev->relatedTo() ) ); 00325 } 00326 } 00327 } 00328 it = rawTodoList.find( subev ); 00329 ++it; 00330 } 00331 00332 // Sort list by priorities. This is brute force and should be 00333 // replaced by a real sorting algorithm. 00334 Todo::List todoList; 00335 for ( int i = 1; i <= 5; ++i ) { 00336 for( it = rawTodoList.begin(); it != rawTodoList.end(); ++it ) { 00337 if ( (*it)->priority() == i && checkSecrecy( *it ) ) { 00338 todoList.append( *it ); 00339 } 00340 } 00341 } 00342 00343 *ts << "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">\n"; 00344 *ts << " <tr>\n"; 00345 *ts << " <th class=\"sum\">" << i18n("Task") << "</th>\n"; 00346 *ts << " <th>" << i18n("Priority") << "</th>\n"; 00347 *ts << " <th>" << i18n("Completed") << "</th>\n"; 00348 if (dueDateEnabled()) { 00349 *ts << " <th>" << i18n("Due Date") << "</th>\n"; 00350 } 00351 if (categoriesTodoEnabled()) { 00352 *ts << " <th>" << i18n("Categories") << "</th>\n"; 00353 } 00354 if (attendeesTodoEnabled()) { 00355 *ts << " <th>" << i18n("Attendees") << "</th>\n"; 00356 } 00357 *ts << " </tr>\n"; 00358 00359 // Create top-level list. 00360 for( it = todoList.begin(); it != todoList.end(); ++it ) { 00361 if ( !(*it)->relatedTo() ) createHtmlTodo( ts, *it ); 00362 } 00363 00364 // Create sub-level lists 00365 for( it = todoList.begin(); it != todoList.end(); ++it ) { 00366 Incidence::List relations = (*it)->relations(); 00367 if (relations.count()) { 00368 // Generate sub-task list of event ev 00369 *ts << " <tr>\n"; 00370 *ts << " <td class=\"subhead\" colspan="; 00371 int columns = 3; 00372 if (dueDateEnabled()) ++columns; 00373 if (categoriesTodoEnabled()) ++columns; 00374 if (attendeesTodoEnabled()) ++columns; 00375 *ts << "\"" << QString::number(columns) << "\""; 00376 *ts << "><a name=\"sub" << (*it)->uid() << "\"></a>" 00377 << i18n("Sub-Tasks of: ") << "<a href=\"#" 00378 << (*it)->uid() << "\"><b>" << cleanChars( (*it)->summary()) 00379 << "</b></a></td>\n"; 00380 *ts << " </tr>\n"; 00381 00382 Todo::List sortedList; 00383 // Sort list by priorities. This is brute force and should be 00384 // replaced by a real sorting algorithm. 00385 for ( int i = 1; i <= 5; ++i ) { 00386 Incidence::List::ConstIterator it2; 00387 for( it2 = relations.begin(); it2 != relations.end(); ++it2 ) { 00388 Todo *ev3 = dynamic_cast<Todo *>( *it2 ); 00389 if ( ev3 && ev3->priority() == i ) sortedList.append( ev3 ); 00390 } 00391 } 00392 00393 Todo::List::ConstIterator it3; 00394 for( it3 = sortedList.begin(); it3 != sortedList.end(); ++it3 ) { 00395 createHtmlTodo( ts, *it3 ); 00396 } 00397 } 00398 } 00399 00400 *ts << "</table>\n"; 00401 } 00402 00403 void HtmlExport::createHtmlTodo (QTextStream *ts,Todo *todo) 00404 { 00405 kdDebug(5850) << "HtmlExport::createHtmlTodo()" << endl; 00406 00407 bool completed = todo->isCompleted(); 00408 Incidence::List relations = todo->relations(); 00409 00410 *ts << "<tr>\n"; 00411 00412 *ts << " <td class=\"sum\""; 00413 if (completed) *ts << "done"; 00414 *ts << ">\n"; 00415 *ts << " <a name=\"" << todo->uid() << "\"></a>\n"; 00416 *ts << " <b>" << cleanChars(todo->summary()) << "</b>\n"; 00417 if (!todo->description().isEmpty()) { 00418 *ts << " <p>" << breakString(cleanChars(todo->description())) << "</p>\n"; 00419 } 00420 if (relations.count()) { 00421 *ts << " <div align=\"right\"><a href=\"#sub" << todo->uid() 00422 << "\">" << i18n("Sub-Tasks") << "</a></div>\n"; 00423 } 00424 00425 *ts << " </td"; 00426 if (completed) *ts << " class=\"done\""; 00427 *ts << ">\n"; 00428 00429 *ts << " <td"; 00430 if (completed) *ts << " class=\"done\""; 00431 *ts << ">\n"; 00432 *ts << " " << todo->priority() << "\n"; 00433 *ts << " </td>\n"; 00434 00435 *ts << " <td"; 00436 if (completed) *ts << " class=\"done\""; 00437 *ts << ">\n"; 00438 *ts << " " << i18n("%1 %").arg(todo->percentComplete()) << "\n"; 00439 *ts << " </td>\n"; 00440 00441 if (dueDateEnabled()) { 00442 *ts << " <td"; 00443 if (completed) *ts << " class=\"done\""; 00444 *ts << ">\n"; 00445 if (todo->hasDueDate()) { 00446 *ts << " " << todo->dtDueDateStr() << "\n"; 00447 } else { 00448 *ts << " &nbsp;\n"; 00449 } 00450 *ts << " </td>\n"; 00451 } 00452 00453 if (categoriesTodoEnabled()) { 00454 *ts << " <td"; 00455 if (completed) *ts << " class=\"done\""; 00456 *ts << ">\n"; 00457 formatHtmlCategories(ts,todo); 00458 *ts << " </td>\n"; 00459 } 00460 00461 if (attendeesTodoEnabled()) { 00462 *ts << " <td"; 00463 if (completed) *ts << " class=\"done\""; 00464 *ts << ">\n"; 00465 formatHtmlAttendees(ts,todo); 00466 *ts << " </td>\n"; 00467 } 00468 00469 *ts << "</tr>\n"; 00470 } 00471 00472 bool HtmlExport::checkSecrecy( Incidence *incidence ) 00473 { 00474 int secrecy = incidence->secrecy(); 00475 if ( secrecy == Incidence::SecrecyPublic ) { 00476 return true; 00477 } 00478 if ( secrecy == Incidence::SecrecyPrivate && !excludePrivateEventEnabled() ) { 00479 return true; 00480 } 00481 if ( secrecy == Incidence::SecrecyConfidential && 00482 !excludeConfidentialEventEnabled() ) { 00483 return true; 00484 } 00485 return false; 00486 } 00487 00488 void HtmlExport::formatHtmlCategories (QTextStream *ts,Incidence *event) 00489 { 00490 if (!event->categoriesStr().isEmpty()) { 00491 *ts << " " << cleanChars(event->categoriesStr()) << "\n"; 00492 } else { 00493 *ts << " &nbsp;\n"; 00494 } 00495 } 00496 00497 void HtmlExport::formatHtmlAttendees (QTextStream *ts,Incidence *event) 00498 { 00499 Attendee::List attendees = event->attendees(); 00500 if (attendees.count()) { 00501 *ts << "<em>"; 00502 #ifndef KORG_NOKABC 00503 KABC::AddressBook *add_book = KABC::StdAddressBook::self(); 00504 KABC::Addressee::List addressList; 00505 addressList = add_book->findByEmail(event->organizer()); 00506 KABC::Addressee o = addressList.first(); 00507 if (!o.isEmpty() && addressList.size()<2) { 00508 *ts << "<a href=\"mailto:" << event->organizer() << "\">"; 00509 *ts << cleanChars(o.formattedName()) << "</a>\n"; 00510 } 00511 else *ts << event->organizer(); 00512 #else 00513 *ts << event->organizer(); 00514 #endif 00515 *ts << "</em><br />"; 00516 Attendee::List::ConstIterator it; 00517 for( it = attendees.begin(); it != attendees.end(); ++it ) { 00518 Attendee *a = *it; 00519 if (!a->email().isEmpty()) { 00520 *ts << "<a href=\"mailto:" << a->email(); 00521 *ts << "\">" << cleanChars(a->name()) << "</a>"; 00522 } 00523 else { 00524 *ts << " " << cleanChars(a->name()); 00525 } 00526 *ts << "<br />" << "\n"; 00527 } 00528 } else { 00529 *ts << " &nbsp;\n"; 00530 } 00531 } 00532 00533 QString HtmlExport::breakString(const QString &text) 00534 { 00535 int number = text.contains("\n"); 00536 if(number < 0) { 00537 return text; 00538 } else { 00539 QString out; 00540 QString tmpText = text; 00541 int pos = 0; 00542 QString tmp; 00543 for(int i=0;i<=number;i++) { 00544 pos = tmpText.find("\n"); 00545 tmp = tmpText.left(pos); 00546 tmpText = tmpText.right(tmpText.length() - pos - 1); 00547 out += tmp + "<br />"; 00548 } 00549 return out; 00550 } 00551 } 00552 00553 QString HtmlExport::cleanChars(const QString &text) 00554 { 00555 QString txt = text; 00556 txt = txt.replace( "&", "&amp;" ); 00557 txt = txt.replace( "<", "&lt;" ); 00558 txt = txt.replace( ">", "&gt;" ); 00559 txt = txt.replace( "\"", "&quot;" ); 00560 txt = txt.replace( "ä", "&auml;" ); 00561 txt = txt.replace( "Ä", "&Auml;" ); 00562 txt = txt.replace( "ö", "&ouml;" ); 00563 txt = txt.replace( "Ö", "&Ouml;" ); 00564 txt = txt.replace( "ü", "&uuml;" ); 00565 txt = txt.replace( "Ü", "&Uuml;" ); 00566 txt = txt.replace( "ß", "&szlig;" ); 00567 txt = txt.replace( "¤", "&euro;" ); 00568 txt = txt.replace( "é", "&eacute;" ); 00569 00570 return txt; 00571 } 00572 00573 void HtmlExport::setStyleSheet( const QString &styleSheet ) 00574 { 00575 mStyleSheet = styleSheet; 00576 } 00577 00578 QString HtmlExport::styleSheet() 00579 { 00580 if ( !mStyleSheet.isEmpty() ) return mStyleSheet; 00581 00582 QString css; 00583 00584 if ( QApplication::reverseLayout() ) { 00585 css += " body { background-color:white; color:black; direction: rtl }\n"; 00586 css += " td { text-align:center; background-color:#eee }\n"; 00587 css += " th { text-align:center; background-color:#228; color:white }\n"; 00588 css += " td.sumdone { background-color:#ccc }\n"; 00589 css += " td.done { background-color:#ccc }\n"; 00590 css += " td.subhead { text-align:center; background-color:#ccf }\n"; 00591 css += " td.datehead { text-align:center; background-color:#ccf }\n"; 00592 css += " td.space { background-color:white }\n"; 00593 css += " td.dateholiday { color:red }\n"; 00594 } else { 00595 css += " body { background-color:white; color:black }\n"; 00596 css += " td { text-align:center; background-color:#eee }\n"; 00597 css += " th { text-align:center; background-color:#228; color:white }\n"; 00598 css += " td.sum { text-align:left }\n"; 00599 css += " td.sumdone { text-align:left; background-color:#ccc }\n"; 00600 css += " td.done { background-color:#ccc }\n"; 00601 css += " td.subhead { text-align:center; background-color:#ccf }\n"; 00602 css += " td.datehead { text-align:center; background-color:#ccf }\n"; 00603 css += " td.space { background-color:white }\n"; 00604 css += " td.date { text-align:left }\n"; 00605 css += " td.dateholiday { text-align:left; color:red }\n"; 00606 } 00607 00608 return css; 00609 } 00610 00611 00612 void HtmlExport::addHoliday( QDate date, QString name) 00613 { 00614 mHolidayMap[date] = name; 00615 } 00616
KDE Logo
This file is part of the documentation for libkcal Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jul 28 23:57:44 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003