Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

odatebookaccessbackend_xml.cpp

Go to the documentation of this file.
00001 /*
00002                              This file is part of the Opie Project
00003                              Copyright (C) Stefan Eilers (Eilers.Stefan@epost.de)
00004               =.             Copyright (C) The Opie Team <opie-devel@handhelds.org>
00005             .=l.
00006            .>+-=
00007  _;:,     .>    :=|.         This program is free software; you can
00008 .> <`_,   >  .   <=          redistribute it and/or  modify it under
00009 :`=1 )Y*s>-.--   :           the terms of the GNU Library General Public
00010 .="- .-=="i,     .._         License as published by the Free Software
00011  - .   .-<_>     .<>         Foundation; either version 2 of the License,
00012      ._= =}       :          or (at your option) any later version.
00013     .%`+i>       _;_.
00014     .i_,=:_.      -<s.       This program is distributed in the hope that
00015      +  .  -:.       =       it will be useful,  but WITHOUT ANY WARRANTY;
00016     : ..    .:,     . . .    without even the implied warranty of
00017     =_        +     =;=|`    MERCHANTABILITY or FITNESS FOR A
00018   _.=:.       :    :=>`:     PARTICULAR PURPOSE. See the GNU
00019 ..}^=.=       =       ;      Library General Public License for more
00020 ++=   -.     .`     .:       details.
00021  :     =  ...= . :.=-
00022  -.   .:....=;==+<;          You should have received a copy of the GNU
00023   -_. . .   )=.  =           Library General Public License along with
00024     --        :-=`           this library; see the file COPYING.LIB.
00025                              If not, write to the Free Software Foundation,
00026                              Inc., 59 Temple Place - Suite 330,
00027                              Boston, MA 02111-1307, USA.
00028 */
00029 
00030 /* OPIE */
00031 #include <opie2/opimnotifymanager.h>
00032 #include <opie2/opimrecurrence.h>
00033 #include <opie2/opimtimezone.h>
00034 #include <opie2/odatebookaccessbackend_xml.h>
00035 #include <opie2/odebug.h>
00036 
00037 #include <qtopia/global.h>
00038 #include <qtopia/stringutil.h>
00039 #include <qtopia/timeconversion.h>
00040 
00041 /* QT */
00042 #include <qasciidict.h>
00043 #include <qfile.h>
00044 
00045 /* STD */
00046 #include <errno.h>
00047 #include <fcntl.h>
00048 
00049 #include <stdio.h>
00050 #include <stdlib.h>
00051 
00052 #include <sys/types.h>
00053 #include <sys/mman.h>
00054 #include <sys/stat.h>
00055 
00056 #include <unistd.h>
00057 
00058 
00059 using namespace Opie;
00060 
00061 namespace {
00062     // FROM TT again
00063 char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
00064 {
00065     char needleChar;
00066     char haystackChar;
00067     if (!needle || !haystack || !hLen || !nLen)
00068     return 0;
00069 
00070     const char* hsearch = haystack;
00071 
00072     if ((needleChar = *needle++) != 0) {
00073     nLen--; //(to make up for needle++)
00074     do {
00075         do {
00076         if ((haystackChar = *hsearch++) == 0)
00077             return (0);
00078         if (hsearch >= haystack + hLen)
00079             return (0);
00080         } while (haystackChar != needleChar);
00081     } while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
00082     hsearch--;
00083     }
00084     return ((char *)hsearch);
00085 }
00086 }
00087 
00088 namespace {
00089     time_t start, end, created, rp_end;
00090     OPimRecurrence* rec;
00091     static OPimRecurrence* recur() {
00092         if (!rec)
00093             rec = new OPimRecurrence;
00094 
00095         return rec;
00096     }
00097     int alarmTime;
00098     int snd;
00099     enum Attribute{
00100         FDescription = 0,
00101         FLocation,
00102         FCategories,
00103         FUid,
00104         FType,
00105         FAlarm,
00106         FSound,
00107         FRType,
00108         FRWeekdays,
00109         FRPosition,
00110         FRFreq,
00111         FRHasEndDate,
00112         FREndDate,
00113         FRStart,
00114         FREnd,
00115         FNote,
00116         FCreated,      // Should't this be called FRCreated ?
00117         FTimeZone,
00118         FRecParent,
00119         FRecChildren,
00120         FExceptions
00121     };
00122 
00123     // FIXME: Use OPimEvent::toMap() here !! (eilers)
00124     static void save( const OPimEvent& ev, QString& buf ) {
00125         buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\"";
00126         if (!ev.location().isEmpty() )
00127             buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\"";
00128 
00129         if (!ev.categories().isEmpty() )
00130             buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\"";
00131 
00132         buf += " uid=\"" + QString::number( ev.uid() ) + "\"";
00133 
00134         if (ev.isAllDay() )
00135             buf += " type=\"AllDay\""; // is that all ?? (eilers)
00136 
00137         if (ev.hasNotifiers() ) {
00138             OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first
00139             int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60;
00140             buf += " alarm=\"" + QString::number(minutes) + "\" sound=\"";
00141             if ( alarm.sound() == OPimAlarm::Loud )
00142                 buf += "loud";
00143             else
00144                 buf += "silent";
00145             buf += "\"";
00146         }
00147         if ( ev.hasRecurrence() ) {
00148             buf += ev.recurrence().toString();
00149         }
00150 
00151         /*
00152          * fscking timezones :) well, we'll first convert
00153          * the QDateTime to a QDateTime in UTC time
00154          * and then we'll create a nice time_t
00155          */
00156         OPimTimeZone zone( (ev.timeZone().isEmpty()||ev.isAllDay()) ? OPimTimeZone::utc() : OPimTimeZone::current() );
00157         buf += " start=\"" + QString::number( zone.fromDateTime( ev.startDateTime()))  + "\"";
00158         buf += " end=\""   + QString::number( zone.fromDateTime( ev.endDateTime()  ))  + "\"";
00159         if (!ev.note().isEmpty() ) {
00160             buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\"";
00161         }
00162 
00163         /*
00164          * Don't save a timezone if AllDay Events
00165          * as they're UTC only anyway
00166          */
00167         if (!ev.isAllDay() ) {
00168 
00169             buf += " timezone=\"";
00170             if ( ev.timeZone().isEmpty() )
00171                 buf += "None";
00172             else
00173                 buf += ev.timeZone();
00174             buf += "\"";
00175         }
00176 
00177         if (ev.parent() != 0 ) {
00178             buf += " recparent=\""+QString::number(ev.parent() )+"\"";
00179         }
00180 
00181         if (ev.children().count() != 0 ) {
00182             QArray<int> children = ev.children();
00183             buf += " recchildren=\"";
00184             for ( uint i = 0; i < children.count(); i++ ) {
00185                 if ( i != 0 ) buf += " ";
00186                 buf += QString::number( children[i] );
00187             }
00188             buf+= "\"";
00189         }
00190 
00191         // skip custom writing
00192     }
00193 
00194     static bool saveEachEvent( const QMap<int, OPimEvent>& list, QFile& file ) {
00195         QMap<int, OPimEvent>::ConstIterator it;
00196         QString buf;
00197         QCString str;
00198         int total_written;
00199         for ( it = list.begin(); it != list.end(); ++it ) {
00200             buf = "<event";
00201             save( it.data(), buf );
00202             buf += " />\n";
00203             str = buf.utf8();
00204 
00205             total_written = file.writeBlock(str.data(), str.length() );
00206             if ( total_written != int(str.length() ) )
00207                 return false;
00208         }
00209         return true;
00210     }
00211 }
00212 
00213 namespace Opie {
00214 ODateBookAccessBackend_XML::ODateBookAccessBackend_XML( const QString& ,
00215                                                         const QString& fileName )
00216     : ODateBookAccessBackend() {
00217     m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.xml" ) : fileName;
00218     m_changed = false;
00219 }
00220 ODateBookAccessBackend_XML::~ODateBookAccessBackend_XML() {
00221 }
00222 bool ODateBookAccessBackend_XML::load() {
00223     return loadFile();
00224 }
00225 bool ODateBookAccessBackend_XML::reload() {
00226     clear();
00227     return load();
00228 }
00229 bool ODateBookAccessBackend_XML::save() {
00230     if (!m_changed) return true;
00231 
00232     int total_written;
00233     QString strFileNew = m_name + ".new";
00234 
00235     QFile f( strFileNew );
00236     if (!f.open( IO_WriteOnly | IO_Raw ) ) return false;
00237 
00238     QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
00239     buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
00240     buf += "<events>\n";
00241     QCString str = buf.utf8();
00242     total_written = f.writeBlock( str.data(), str.length() );
00243     if ( total_written != int(str.length() ) ) {
00244         f.close();
00245         QFile::remove( strFileNew );
00246         return false;
00247     }
00248 
00249     if (!saveEachEvent( m_raw, f ) ) {
00250         f.close();
00251         QFile::remove( strFileNew );
00252         return false;
00253     }
00254     if (!saveEachEvent( m_rep, f ) ) {
00255         f.close();
00256         QFile::remove( strFileNew );
00257         return false;
00258     }
00259 
00260     buf = "</events>\n</DATEBOOK>\n";
00261     str = buf.utf8();
00262     total_written = f.writeBlock( str.data(), str.length() );
00263     if ( total_written != int(str.length() ) ) {
00264         f.close();
00265         QFile::remove( strFileNew );
00266         return false;
00267     }
00268     f.close();
00269 
00270     if ( ::rename( strFileNew, m_name ) < 0 ) {
00271         QFile::remove( strFileNew );
00272         return false;
00273     }
00274 
00275     m_changed = false;
00276     return true;
00277 }
00278 QArray<int> ODateBookAccessBackend_XML::allRecords()const {
00279     QArray<int> ints( m_raw.count()+ m_rep.count() );
00280     uint i = 0;
00281     QMap<int, OPimEvent>::ConstIterator it;
00282 
00283     for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
00284         ints[i] = it.key();
00285         i++;
00286     }
00287     for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
00288         ints[i] = it.key();
00289         i++;
00290     }
00291 
00292     return ints;
00293 }
00294 QArray<int> ODateBookAccessBackend_XML::queryByExample(const OPimEvent&, int,  const QDateTime& ) {
00295     return QArray<int>();
00296 }
00297 void ODateBookAccessBackend_XML::clear() {
00298     m_changed = true;
00299     m_raw.clear();
00300     m_rep.clear();
00301 }
00302 OPimEvent ODateBookAccessBackend_XML::find( int uid ) const{
00303     if ( m_raw.contains( uid ) )
00304         return m_raw[uid];
00305     else
00306         return m_rep[uid];
00307 }
00308 bool ODateBookAccessBackend_XML::add( const OPimEvent& ev ) {
00309     m_changed = true;
00310     if (ev.hasRecurrence() )
00311         m_rep.insert( ev.uid(), ev );
00312     else
00313         m_raw.insert( ev.uid(), ev );
00314 
00315     return true;
00316 }
00317 bool ODateBookAccessBackend_XML::remove( int uid ) {
00318     m_changed = true;
00319     m_raw.remove( uid );
00320     m_rep.remove( uid );
00321 
00322     return true;
00323 }
00324 bool ODateBookAccessBackend_XML::replace( const OPimEvent& ev ) {
00325     replace( ev.uid() ); // ??? Shouldn't this be "remove( ev.uid() ) ??? (eilers)
00326     return add( ev );
00327 }
00328 
00329 QArray<int> ODateBookAccessBackend_XML::rawRepeats()const {
00330     QArray<int> ints( m_rep.count() );
00331     uint i = 0;
00332     QMap<int, OPimEvent>::ConstIterator it;
00333 
00334     for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
00335         ints[i] = it.key();
00336         i++;
00337     }
00338 
00339     return ints;
00340 }
00341 QArray<int> ODateBookAccessBackend_XML::nonRepeats()const {
00342     QArray<int> ints( m_raw.count() );
00343     uint i = 0;
00344     QMap<int, OPimEvent>::ConstIterator it;
00345 
00346     for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
00347         ints[i] = it.key();
00348         i++;
00349     }
00350 
00351     return ints;
00352 }
00353 OPimEvent::ValueList ODateBookAccessBackend_XML::directNonRepeats()const {
00354     OPimEvent::ValueList list;
00355     QMap<int, OPimEvent>::ConstIterator it;
00356     for (it = m_raw.begin(); it != m_raw.end(); ++it )
00357         list.append( it.data() );
00358 
00359     return list;
00360 }
00361 OPimEvent::ValueList ODateBookAccessBackend_XML::directRawRepeats()const {
00362     OPimEvent::ValueList list;
00363     QMap<int, OPimEvent>::ConstIterator it;
00364     for (it = m_rep.begin(); it != m_rep.end(); ++it )
00365         list.append( it.data() );
00366 
00367     return list;
00368 }
00369 
00370 // FIXME: Use OPimEvent::fromMap() (eilers)
00371 bool ODateBookAccessBackend_XML::loadFile() {
00372     m_changed = false;
00373 
00374     int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY );
00375     if ( fd < 0 ) return false;
00376 
00377     struct stat attribute;
00378     if ( ::fstat(fd, &attribute ) == -1 ) {
00379         ::close( fd );
00380         return false;
00381     }
00382     void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 );
00383     if ( map_addr == ( (caddr_t)-1) ) {
00384         ::close( fd );
00385         return false;
00386     }
00387 
00388     ::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL );
00389     ::close( fd );
00390 
00391     QAsciiDict<int> dict(FExceptions+1);
00392     dict.setAutoDelete( true );
00393     dict.insert( "description", new int(FDescription) );
00394     dict.insert( "location", new int(FLocation) );
00395     dict.insert( "categories", new int(FCategories) );
00396     dict.insert( "uid", new int(FUid) );
00397     dict.insert( "type", new int(FType) );
00398     dict.insert( "alarm", new int(FAlarm) );
00399     dict.insert( "sound", new int(FSound) );
00400     dict.insert( "rtype", new int(FRType) );
00401     dict.insert( "rweekdays", new int(FRWeekdays) );
00402     dict.insert( "rposition", new int(FRPosition) );
00403     dict.insert( "rfreq", new int(FRFreq) );
00404     dict.insert( "rhasenddate", new int(FRHasEndDate) );
00405     dict.insert( "enddt", new int(FREndDate) );
00406     dict.insert( "start", new int(FRStart) );
00407     dict.insert( "end", new int(FREnd) );
00408     dict.insert( "note", new int(FNote) );
00409     dict.insert( "created", new int(FCreated) );  // Shouldn't this be FRCreated ??
00410     dict.insert( "recparent", new int(FRecParent) );
00411     dict.insert( "recchildren", new int(FRecChildren) );
00412     dict.insert( "exceptions", new int(FExceptions) );
00413     dict.insert( "timezone", new int(FTimeZone) );
00414 
00415 
00416     // initialiaze db hack
00417     m_noTimeZone = true;
00418 
00419     char* dt = (char*)map_addr;
00420     int len = attribute.st_size;
00421     int i = 0;
00422     char* point;
00423     const char* collectionString = "<event ";
00424     int strLen = ::strlen(collectionString);
00425     int *find;
00426     while ( (  point = ::strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0  ) {
00427         i = point -dt;
00428         i+= strLen;
00429 
00430         alarmTime = -1;
00431         snd = 0; // silent
00432 
00433         OPimEvent ev;
00434         rec = 0;
00435 
00436         while ( TRUE ) {
00437             while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
00438         ++i;
00439         if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
00440         break;
00441 
00442 
00443         // we have another attribute, read it.
00444         int j = i;
00445         while ( j < len && dt[j] != '=' )
00446         ++j;
00447         QCString attr( dt+i, j-i+1);
00448 
00449         i = ++j; // skip =
00450 
00451         // find the start of quotes
00452         while ( i < len && dt[i] != '"' )
00453         ++i;
00454         j = ++i;
00455 
00456         bool haveUtf = FALSE;
00457         bool haveEnt = FALSE;
00458         while ( j < len && dt[j] != '"' ) {
00459         if ( ((unsigned char)dt[j]) > 0x7f )
00460             haveUtf = TRUE;
00461         if ( dt[j] == '&' )
00462             haveEnt = TRUE;
00463         ++j;
00464         }
00465         if ( i == j ) {
00466         // empty value
00467         i = j + 1;
00468         continue;
00469         }
00470 
00471         QCString value( dt+i, j-i+1 );
00472         i = j + 1;
00473 
00474         QString str = (haveUtf ? QString::fromUtf8( value )
00475             : QString::fromLatin1( value ) );
00476         if ( haveEnt )
00477         str = Qtopia::plainString( str );
00478 
00479             /*
00480              * add key + value
00481              */
00482             find = dict[attr.data()];
00483             if (!find)
00484                 ev.setCustomField( attr, str );
00485             else {
00486                 setField( ev, *find, str );
00487             }
00488         }
00489         /* time to finalize */
00490         finalizeRecord( ev );
00491         delete rec;
00492         m_noTimeZone = true;
00493     }
00494     ::munmap(map_addr, attribute.st_size );
00495     m_changed = false; // changed during add
00496 
00497     return true;
00498 }
00499 
00500 // FIXME: Use OPimEvent::fromMap() which makes this obsolete.. (eilers)
00501 void ODateBookAccessBackend_XML::finalizeRecord( OPimEvent& ev ) {
00502 
00503     /*
00504      * quirk to import datebook files. They normally don't have a
00505      * timeZone attribute and we treat this as to use OPimTimeZone::current()
00506      */
00507     if (m_noTimeZone )
00508         ev.setTimeZone( OPimTimeZone::current().timeZone() );
00509 
00510 
00511 
00512     /* AllDay is alway in UTC */
00513     if ( ev.isAllDay() ) {
00514         OPimTimeZone utc = OPimTimeZone::utc();
00515         ev.setStartDateTime( utc.toDateTime( start ) );
00516         ev.setEndDateTime  ( utc.toDateTime( end   ) );
00517     }else {
00518         /* to current date time */
00519         OPimTimeZone   to_zone( ev.timeZone().isEmpty() ? OPimTimeZone::utc() : OPimTimeZone::current() );
00520 
00521         ev.setStartDateTime(to_zone.toDateTime( start));
00522         ev.setEndDateTime  (to_zone.toDateTime( end));
00523     }
00524     if ( rec && rec->doesRecur() ) {
00525         OPimTimeZone utc = OPimTimeZone::utc();
00526         OPimRecurrence recu( *rec ); // call copy c'tor;
00527         recu.setEndDate ( utc.toDateTime( rp_end ).date() );
00528         recu.setCreatedDateTime( utc.toDateTime( created ) );
00529         recu.setStart( ev.startDateTime().date() );
00530         ev.setRecurrence( recu );
00531     }
00532 
00533     if (alarmTime != -1 ) {
00534         QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 );
00535         OPimAlarm al( snd ,  dt  );
00536         ev.notifiers().add( al );
00537     }
00538     if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) {
00539         ev.setUid( 1 );
00540     }
00541 
00542     if ( ev.hasRecurrence() )
00543         m_rep.insert( ev.uid(), ev );
00544     else
00545         m_raw.insert( ev.uid(), ev );
00546 
00547 }
00548 void ODateBookAccessBackend_XML::setField( OPimEvent& e, int id, const QString& value) {
00549     switch( id ) {
00550     case FDescription:
00551         e.setDescription( value );
00552         break;
00553     case FLocation:
00554         e.setLocation( value );
00555         break;
00556     case FCategories:
00557         e.setCategories( e.idsFromString( value ) );
00558         break;
00559     case FUid:
00560         e.setUid( value.toInt() );
00561         break;
00562     case FType:
00563         if ( value == "AllDay" ) {
00564             e.setAllDay( true );
00565         }
00566         break;
00567     case FAlarm:
00568         alarmTime = value.toInt();
00569         break;
00570     case FSound:
00571         snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent;
00572         break;
00573         // recurrence stuff
00574     case FRType:
00575         if ( value == "Daily" )
00576             recur()->setType( OPimRecurrence::Daily );
00577         else if ( value == "Weekly" )
00578             recur()->setType( OPimRecurrence::Weekly);
00579         else if ( value == "MonthlyDay" )
00580             recur()->setType( OPimRecurrence::MonthlyDay );
00581         else if ( value == "MonthlyDate" )
00582             recur()->setType( OPimRecurrence::MonthlyDate );
00583         else if ( value == "Yearly" )
00584             recur()->setType( OPimRecurrence::Yearly );
00585         else
00586             recur()->setType( OPimRecurrence::NoRepeat );
00587         break;
00588     case FRWeekdays:
00589         recur()->setDays( value.toInt() );
00590         break;
00591     case FRPosition:
00592         recur()->setPosition( value.toInt() );
00593         break;
00594     case FRFreq:
00595         recur()->setFrequency( value.toInt() );
00596         break;
00597     case FRHasEndDate:
00598         recur()->setHasEndDate( value.toInt() );
00599         break;
00600     case FREndDate: {
00601         rp_end = (time_t) value.toLong();
00602         break;
00603     }
00604     case FRStart: {
00605         start =  (time_t) value.toLong();
00606         break;
00607     }
00608     case FREnd: {
00609         end = ( (time_t) value.toLong() );
00610         break;
00611     }
00612     case FNote:
00613         e.setNote( value );
00614         break;
00615     case FCreated:
00616         created = value.toInt();
00617         break;
00618     case FRecParent:
00619         e.setParent( value.toInt() );
00620         break;
00621     case FRecChildren:{
00622         QStringList list = QStringList::split(' ', value );
00623         for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00624             e.addChild( (*it).toInt() );
00625         }
00626     }
00627         break;
00628     case FExceptions:{
00629         QStringList list = QStringList::split(' ', value );
00630         for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00631             QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() );
00632             recur()->exceptions().append( date );
00633         }
00634     }
00635         break;
00636     case FTimeZone:
00637         m_noTimeZone = false;
00638         if ( value != "None" )
00639             e.setTimeZone( value );
00640         break;
00641     default:
00642         break;
00643     }
00644 }
00645 QArray<int> ODateBookAccessBackend_XML::matchRegexp(  const QRegExp &r ) const
00646 {
00647     QArray<int> m_currentQuery( m_raw.count()+ m_rep.count() );
00648     uint arraycounter = 0;
00649     QMap<int, OPimEvent>::ConstIterator it;
00650 
00651     for ( it = m_raw.begin(); it != m_raw.end(); ++it )
00652         if ( it.data().match( r ) )
00653             m_currentQuery[arraycounter++] = it.data().uid();
00654     for ( it = m_rep.begin(); it != m_rep.end(); ++it )
00655         if ( it.data().match( r ) )
00656             m_currentQuery[arraycounter++] = it.data().uid();
00657 
00658     // Shrink to fit..
00659     m_currentQuery.resize(arraycounter);
00660 
00661     return m_currentQuery;
00662 }
00663 
00664 }

Generated on Sat Nov 5 16:16:16 2005 for OPIE by  doxygen 1.4.2