00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include <opie2/opimdateconversion.h>
00032 #include <opie2/opimstate.h>
00033 #include <opie2/opimtimezone.h>
00034 #include <opie2/opimnotifymanager.h>
00035 #include <opie2/opimrecurrence.h>
00036 #include <opie2/otodoaccessxml.h>
00037 #include <opie2/otodoaccess.h>
00038 #include <opie2/odebug.h>
00039
00040 #include <opie2/private/opimtodosortvector.h>
00041
00042 #include <qpe/global.h>
00043 #include <qpe/stringutil.h>
00044 #include <qpe/timeconversion.h>
00045
00046
00047 #include <qfile.h>
00048 #include <qvector.h>
00049
00050
00051 #include <errno.h>
00052 #include <fcntl.h>
00053
00054 #include <sys/mman.h>
00055 #include <sys/stat.h>
00056 #include <sys/types.h>
00057
00058 #include <unistd.h>
00059
00060
00061 using namespace Opie;
00062
00063 namespace {
00064 time_t rp_end;
00065 OPimRecurrence* rec;
00066 OPimRecurrence *recur() {
00067 if (!rec ) rec = new OPimRecurrence;
00068 return rec;
00069 }
00070 int snd;
00071 enum MoreAttributes {
00072 FRType = OPimTodo::CompletedDate + 2,
00073 FRWeekdays,
00074 FRPosition,
00075 FRFreq,
00076 FRHasEndDate,
00077 FREndDate,
00078 FRStart,
00079 FREnd
00080 };
00081
00082 char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
00083 {
00084 char needleChar;
00085 char haystackChar;
00086 if (!needle || !haystack || !hLen || !nLen)
00087 return 0;
00088
00089 const char* hsearch = haystack;
00090
00091 if ((needleChar = *needle++) != 0) {
00092 nLen--;
00093 do {
00094 do {
00095 if ((haystackChar = *hsearch++) == 0)
00096 return (0);
00097 if (hsearch >= haystack + hLen)
00098 return (0);
00099 } while (haystackChar != needleChar);
00100 } while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
00101 hsearch--;
00102 }
00103 return ((char *)hsearch);
00104 }
00105 }
00106
00107 namespace Opie {
00108
00109 OPimTodoAccessXML::OPimTodoAccessXML( const QString& appName,
00110 const QString& fileName )
00111 : OPimTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false )
00112 {
00113 if (!fileName.isEmpty() )
00114 m_file = fileName;
00115 else
00116 m_file = Global::applicationFileName( "todolist", "todolist.xml" );
00117 }
00118 OPimTodoAccessXML::~OPimTodoAccessXML() {
00119
00120 }
00121 bool OPimTodoAccessXML::load() {
00122 rec = 0;
00123 m_opened = true;
00124 m_changed = false;
00125
00126
00127
00128
00129 QAsciiDict<int> dict(26);
00130 dict.setAutoDelete( TRUE );
00131 dict.insert("Categories" , new int(OPimTodo::Category) );
00132 dict.insert("Uid" , new int(OPimTodo::Uid) );
00133 dict.insert("HasDate" , new int(OPimTodo::HasDate) );
00134 dict.insert("Completed" , new int(OPimTodo::Completed) );
00135 dict.insert("Description" , new int(OPimTodo::Description) );
00136 dict.insert("Summary" , new int(OPimTodo::Summary) );
00137 dict.insert("Priority" , new int(OPimTodo::Priority) );
00138 dict.insert("DateDay" , new int(OPimTodo::DateDay) );
00139 dict.insert("DateMonth" , new int(OPimTodo::DateMonth) );
00140 dict.insert("DateYear" , new int(OPimTodo::DateYear) );
00141 dict.insert("Progress" , new int(OPimTodo::Progress) );
00142 dict.insert("CompletedDate", new int(OPimTodo::CompletedDate) );
00143 dict.insert("StartDate", new int(OPimTodo::StartDate) );
00144 dict.insert("CrossReference", new int(OPimTodo::CrossReference) );
00145 dict.insert("State", new int(OPimTodo::State) );
00146 dict.insert("Alarms", new int(OPimTodo::Alarms) );
00147 dict.insert("Reminders", new int(OPimTodo::Reminders) );
00148 dict.insert("Maintainer", new int(OPimTodo::Maintainer) );
00149 dict.insert("rtype", new int(FRType) );
00150 dict.insert("rweekdays", new int(FRWeekdays) );
00151 dict.insert("rposition", new int(FRPosition) );
00152 dict.insert("rfreq", new int(FRFreq) );
00153 dict.insert("start", new int(FRStart) );
00154 dict.insert("rhasenddate", new int(FRHasEndDate) );
00155 dict.insert("enddt", new int(FREndDate) );
00156
00157
00158
00159
00160 int fd = ::open( QFile::encodeName(m_file).data(), O_RDONLY );
00161 struct stat attribut;
00162 if ( fd < 0 ) return false;
00163
00164 if ( fstat(fd, &attribut ) == -1 ) {
00165 ::close( fd );
00166 return false;
00167 }
00168 void* map_addr = ::mmap(NULL, attribut.st_size, PROT_READ, MAP_SHARED, fd, 0 );
00169 if ( map_addr == ( (caddr_t)-1) ) {
00170 ::close(fd );
00171 return false;
00172 }
00173
00174 ::madvise( map_addr, attribut.st_size, MADV_SEQUENTIAL );
00175
00176 ::close( fd );
00177
00178 char* dt = (char*)map_addr;
00179 int len = attribut.st_size;
00180 int i = 0;
00181 char *point;
00182 const char* collectionString = "<Task ";
00183 int strLen = strlen(collectionString);
00184 while ( ( point = strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0l ) {
00185 i = point -dt;
00186 i+= strLen;
00187
00188 OPimTodo ev;
00189 m_year = m_month = m_day = 0;
00190
00191 while ( TRUE ) {
00192 while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
00193 ++i;
00194 if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
00195 break;
00196
00197
00198 int j = i;
00199 while ( j < len && dt[j] != '=' )
00200 ++j;
00201 QCString attr( dt+i, j-i+1);
00202
00203 i = ++j;
00204
00205
00206 while ( i < len && dt[i] != '"' )
00207 ++i;
00208 j = ++i;
00209
00210 bool haveUtf = FALSE;
00211 bool haveEnt = FALSE;
00212 while ( j < len && dt[j] != '"' ) {
00213 if ( ((unsigned char)dt[j]) > 0x7f )
00214 haveUtf = TRUE;
00215 if ( dt[j] == '&' )
00216 haveEnt = TRUE;
00217 ++j;
00218 }
00219 if ( i == j ) {
00220
00221 i = j + 1;
00222 continue;
00223 }
00224
00225 QCString value( dt+i, j-i+1 );
00226 i = j + 1;
00227
00228 QString str = (haveUtf ? QString::fromUtf8( value )
00229 : QString::fromLatin1( value ) );
00230 if ( haveEnt )
00231 str = Qtopia::plainString( str );
00232
00233
00234
00235
00236 todo( &dict, ev, attr, str );
00237
00238 }
00239
00240
00241
00242 if (m_events.contains( ev.uid() ) || ev.uid() == 0) {
00243 ev.setUid( 1 );
00244 m_changed = true;
00245 }
00246 if ( ev.hasDueDate() ) {
00247 ev.setDueDate( QDate(m_year, m_month, m_day) );
00248 }
00249 if ( rec && rec->doesRecur() ) {
00250 OPimTimeZone utc = OPimTimeZone::utc();
00251 OPimRecurrence recu( *rec );
00252 recu.setEndDate( utc.fromUTCDateTime( rp_end ).date() );
00253 recu.setStart( ev.dueDate() );
00254 ev.setRecurrence( recu );
00255 }
00256 m_events.insert(ev.uid(), ev );
00257 m_year = m_month = m_day = -1;
00258 delete rec;
00259 rec = 0;
00260 }
00261
00262 munmap(map_addr, attribut.st_size );
00263
00264 return true;
00265 }
00266 bool OPimTodoAccessXML::reload() {
00267 m_events.clear();
00268 return load();
00269 }
00270 bool OPimTodoAccessXML::save() {
00271 if (!m_opened || !m_changed ) {
00272 return true;
00273 }
00274 QString strNewFile = m_file + ".new";
00275 QFile f( strNewFile );
00276 if (!f.open( IO_WriteOnly|IO_Raw ) )
00277 return false;
00278
00279 int written;
00280 QString out;
00281 out = "<!DOCTYPE Tasks>\n<Tasks>\n";
00282
00283
00284 QMap<int, OPimTodo>::Iterator it;
00285 for (it = m_events.begin(); it != m_events.end(); ++it ) {
00286 out+= "<Task " + toString( (*it) ) + " />\n";
00287 QCString cstr = out.utf8();
00288 written = f.writeBlock( cstr.data(), cstr.length() );
00289
00290
00291 if ( written != (int)cstr.length() ) {
00292 f.close();
00293 QFile::remove( strNewFile );
00294 return false;
00295 }
00296 out = QString::null;
00297 }
00298
00299 out += "</Tasks>";
00300 QCString cstr = out.utf8();
00301 written = f.writeBlock( cstr.data(), cstr.length() );
00302
00303 if ( written != (int)cstr.length() ) {
00304 f.close();
00305 QFile::remove( strNewFile );
00306 return false;
00307 }
00308
00309 f.close();
00310
00311 if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) {
00312 QFile::remove( strNewFile );
00313 }
00314
00315 m_changed = false;
00316 return true;
00317 }
00318 QArray<int> OPimTodoAccessXML::allRecords()const {
00319 QArray<int> ids( m_events.count() );
00320 QMap<int, OPimTodo>::ConstIterator it;
00321 int i = 0;
00322
00323 for ( it = m_events.begin(); it != m_events.end(); ++it )
00324 ids[i++] = it.key();
00325
00326
00327 return ids;
00328 }
00329
00330 OPimTodo OPimTodoAccessXML::find( int uid )const {
00331 OPimTodo todo;
00332 todo.setUid( 0 );
00333 QMap<int, OPimTodo>::ConstIterator it = m_events.find( uid );
00334 if ( it != m_events.end() )
00335 todo = it.data();
00336
00337 return todo;
00338 }
00339 void OPimTodoAccessXML::clear() {
00340 if (m_opened )
00341 m_changed = true;
00342
00343 m_events.clear();
00344 }
00345 bool OPimTodoAccessXML::add( const OPimTodo& todo ) {
00346 m_changed = true;
00347 m_events.insert( todo.uid(), todo );
00348
00349 return true;
00350 }
00351 bool OPimTodoAccessXML::remove( int uid ) {
00352 m_changed = true;
00353 m_events.remove( uid );
00354
00355 return true;
00356 }
00357 bool OPimTodoAccessXML::replace( const OPimTodo& todo) {
00358 m_changed = true;
00359 m_events.replace( todo.uid(), todo );
00360
00361 return true;
00362 }
00363 QArray<int> OPimTodoAccessXML::effectiveToDos( const QDate& start,
00364 const QDate& end,
00365 bool includeNoDates )const {
00366 QArray<int> ids( m_events.count() );
00367 QMap<int, OPimTodo>::ConstIterator it;
00368
00369 int i = 0;
00370 for ( it = m_events.begin(); it != m_events.end(); ++it ) {
00371 if ( !it.data().hasDueDate() && includeNoDates) {
00372 ids[i++] = it.key();
00373 }else if ( it.data().dueDate() >= start &&
00374 it.data().dueDate() <= end ) {
00375 ids[i++] = it.key();
00376 }
00377 }
00378 ids.resize( i );
00379 return ids;
00380 }
00381 QArray<int> OPimTodoAccessXML::overDue()const {
00382 QArray<int> ids( m_events.count() );
00383 int i = 0;
00384
00385 QMap<int, OPimTodo>::ConstIterator it;
00386 for ( it = m_events.begin(); it != m_events.end(); ++it ) {
00387 if ( it.data().isOverdue() ) {
00388 ids[i] = it.key();
00389 i++;
00390 }
00391 }
00392 ids.resize( i );
00393 return ids;
00394 }
00395
00396
00397
00398 void OPimTodoAccessXML::todo( QAsciiDict<int>* dict, OPimTodo& ev,
00399 const QCString& attr, const QString& val) {
00400
00401 int *find=0;
00402
00403 find = (*dict)[ attr.data() ];
00404 if (!find ) {
00405 ev.setCustomField( attr, val );
00406 return;
00407 }
00408
00409 switch( *find ) {
00410 case OPimTodo::Uid:
00411 ev.setUid( val.toInt() );
00412 break;
00413 case OPimTodo::Category:
00414 ev.setCategories( ev.idsFromString( val ) );
00415 break;
00416 case OPimTodo::HasDate:
00417 ev.setHasDueDate( val.toInt() );
00418 break;
00419 case OPimTodo::Completed:
00420 ev.setCompleted( val.toInt() );
00421 break;
00422 case OPimTodo::Description:
00423 ev.setDescription( val );
00424 break;
00425 case OPimTodo::Summary:
00426 ev.setSummary( val );
00427 break;
00428 case OPimTodo::Priority:
00429 ev.setPriority( val.toInt() );
00430 break;
00431 case OPimTodo::DateDay:
00432 m_day = val.toInt();
00433 break;
00434 case OPimTodo::DateMonth:
00435 m_month = val.toInt();
00436 break;
00437 case OPimTodo::DateYear:
00438 m_year = val.toInt();
00439 break;
00440 case OPimTodo::Progress:
00441 ev.setProgress( val.toInt() );
00442 break;
00443 case OPimTodo::CompletedDate:
00444 ev.setCompletedDate( OPimDateConversion::dateFromString( val ) );
00445 break;
00446 case OPimTodo::StartDate:
00447 ev.setStartDate( OPimDateConversion::dateFromString( val ) );
00448 break;
00449 case OPimTodo::State:
00450 ev.setState( val.toInt() );
00451 break;
00452 case OPimTodo::Alarms:{
00453 OPimNotifyManager &manager = ev.notifiers();
00454 QStringList als = QStringList::split(";", val );
00455 for (QStringList::Iterator it = als.begin(); it != als.end(); ++it ) {
00456 QStringList alarm = QStringList::split(":", (*it), TRUE );
00457 OPimAlarm al( alarm[2].toInt(), OPimDateConversion::dateTimeFromString( alarm[0] ), alarm[1].toInt() );
00458 manager.add( al );
00459 }
00460 }
00461 break;
00462 case OPimTodo::Reminders:{
00463 OPimNotifyManager &manager = ev.notifiers();
00464 QStringList rems = QStringList::split(";", val );
00465 for (QStringList::Iterator it = rems.begin(); it != rems.end(); ++it ) {
00466 OPimReminder rem( (*it).toInt() );
00467 manager.add( rem );
00468 }
00469 }
00470 break;
00471 case OPimTodo::CrossReference:
00472 {
00473
00474
00475
00476
00477
00478 QStringList refs = QStringList::split(';', val );
00479 QStringList::Iterator strIt;
00480 for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) {
00481 int pos = (*strIt).find(',');
00482 if ( pos > -1 )
00483 ;
00484
00485 }
00486 break;
00487 }
00488
00489 case FRType:
00490 if ( val == "Daily" )
00491 recur()->setType( OPimRecurrence::Daily );
00492 else if ( val == "Weekly" )
00493 recur()->setType( OPimRecurrence::Weekly);
00494 else if ( val == "MonthlyDay" )
00495 recur()->setType( OPimRecurrence::MonthlyDay );
00496 else if ( val == "MonthlyDate" )
00497 recur()->setType( OPimRecurrence::MonthlyDate );
00498 else if ( val == "Yearly" )
00499 recur()->setType( OPimRecurrence::Yearly );
00500 else
00501 recur()->setType( OPimRecurrence::NoRepeat );
00502 break;
00503 case FRWeekdays:
00504 recur()->setDays( val.toInt() );
00505 break;
00506 case FRPosition:
00507 recur()->setPosition( val.toInt() );
00508 break;
00509 case FRFreq:
00510 recur()->setFrequency( val.toInt() );
00511 break;
00512 case FRHasEndDate:
00513 recur()->setHasEndDate( val.toInt() );
00514 break;
00515 case FREndDate: {
00516 rp_end = (time_t) val.toLong();
00517 break;
00518 }
00519 default:
00520 ev.setCustomField( attr, val );
00521 break;
00522 }
00523 }
00524
00525
00526 namespace {
00527 QString customToXml(const QMap<QString, QString>& customMap )
00528 {
00529 QString buf(" ");
00530 for ( QMap<QString, QString>::ConstIterator cit = customMap.begin();
00531 cit != customMap.end(); ++cit) {
00532 buf += cit.key();
00533 buf += "=\"";
00534 buf += Qtopia::escapeString(cit.data());
00535 buf += "\" ";
00536 }
00537 return buf;
00538 }
00539
00540
00541 }
00542
00543 QString OPimTodoAccessXML::toString( const OPimTodo& ev )const {
00544 QString str;
00545
00546 str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" ";
00547 str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" ";
00548 str += "Priority=\"" + QString::number( ev.priority() ) + "\" ";
00549 str += "Progress=\"" + QString::number(ev.progress() ) + "\" ";
00550
00551 str += "Categories=\"" + toString( ev.categories() ) + "\" ";
00552 str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" ";
00553 str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" ";
00554
00555 if ( ev.hasDueDate() ) {
00556 str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" ";
00557 str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" ";
00558 str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" ";
00559 }
00560 str += "Uid=\"" + QString::number( ev.uid() ) + "\" ";
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576 if ( ev.hasRecurrence() ) {
00577 str += ev.recurrence().toString();
00578 }
00579 if ( ev.hasStartDate() )
00580 str += "StartDate=\""+ OPimDateConversion::dateToString( ev.startDate() ) +"\" ";
00581 if ( ev.hasCompletedDate() )
00582 str += "CompletedDate=\""+ OPimDateConversion::dateToString( ev.completedDate() ) +"\" ";
00583 if ( ev.hasState() )
00584 str += "State=\""+QString::number( ev.state().state() )+"\" ";
00585
00586
00587
00588
00589
00590 if ( ev.hasNotifiers() ) {
00591 OPimNotifyManager manager = ev.notifiers();
00592 OPimNotifyManager::Alarms alarms = manager.alarms();
00593 if (!alarms.isEmpty() ) {
00594 QStringList als;
00595 OPimNotifyManager::Alarms::Iterator it = alarms.begin();
00596 for ( ; it != alarms.end(); ++it ) {
00597
00598 if ( (*it).dateTime().isValid() ) {
00599 als << OPimDateConversion::dateTimeToString( (*it).dateTime() )
00600 + ":" + QString::number( (*it).duration() )
00601 + ":" + QString::number( (*it).sound() )
00602 + ":";
00603 }
00604 }
00605
00606 str += "Alarms=\""+als.join(";") +"\" ";
00607 }
00608
00609
00610
00611
00612 OPimNotifyManager::Reminders reminders = manager.reminders();
00613 if (!reminders.isEmpty() ) {
00614 OPimNotifyManager::Reminders::Iterator it = reminders.begin();
00615 QStringList records;
00616 for ( ; it != reminders.end(); ++it ) {
00617 records << QString::number( (*it).recordUid() );
00618 }
00619 str += "Reminders=\""+ records.join(";") +"\" ";
00620 }
00621 }
00622 str += customToXml( ev.toExtraMap() );
00623
00624
00625 return str;
00626 }
00627 QString OPimTodoAccessXML::toString( const QArray<int>& ints ) const {
00628 return Qtopia::Record::idsToString( ints );
00629 }
00630
00631
00632 QArray<int> OPimTodoAccessXML::sorted( const UIDArray& events, bool asc,
00633 int sortOrder,int sortFilter,
00634 const QArray<int>& categories )const {
00635 Internal::OPimTodoSortVector vector(events.count(), asc,sortOrder );
00636 int item = 0;
00637
00638 bool bCat = sortFilter & OPimTodoAccess::FilterCategory ? true : false;
00639 bool bOnly = sortFilter & OPimTodoAccess::OnlyOverDue ? true : false;
00640 bool comp = sortFilter & OPimTodoAccess::DoNotShowCompleted ? true : false;
00641 bool catPassed = false;
00642 int cat;
00643
00644 for ( uint i = 0; i < events.count(); ++i ) {
00645
00646 if ( !m_events.contains( events[i] ) )
00647 continue;
00648
00649 OPimTodo todo = m_events[events[i]];
00650
00651
00652
00653 catPassed = false;
00654 for ( uint cat_nu = 0; cat_nu < categories.count(); ++cat_nu ) {
00655 cat = categories[cat_nu];
00656 if ( bCat && cat == -1 ) {
00657 if(!todo.categories().isEmpty() )
00658 continue;
00659 } else if ( bCat && cat != 0)
00660 if (!todo.categories().contains( cat ) )
00661 continue;
00662 catPassed = true;
00663 break;
00664 }
00665
00666
00667
00668
00669
00670 if ( !catPassed )
00671 continue;
00672 if ( !todo.isOverdue() && bOnly )
00673 continue;
00674 if (todo.isCompleted() && comp )
00675 continue;
00676
00677 vector.insert(item++, todo );
00678 }
00679
00680 vector.resize( item );
00681
00682 vector.sort();
00683
00684 UIDArray array( vector.count() );
00685 for (uint i= 0; i < vector.count(); i++ )
00686 array[i] = vector.uidAt( i );
00687
00688 return array;
00689 }
00690
00691 void OPimTodoAccessXML::removeAllCompleted() {
00692 QMap<int, OPimTodo> events = m_events;
00693 for ( QMap<int, OPimTodo>::Iterator it = m_events.begin(); it != m_events.end(); ++it ) {
00694 if ( (*it).isCompleted() )
00695 events.remove( it.key() );
00696 }
00697 m_events = events;
00698 }
00699
00700 QArray<int> OPimTodoAccessXML::matchRegexp( const QRegExp &r ) const
00701 {
00702 QArray<int> currentQuery( m_events.count() );
00703 uint arraycounter = 0;
00704
00705 QMap<int, OPimTodo>::ConstIterator it;
00706 for (it = m_events.begin(); it != m_events.end(); ++it ) {
00707 if ( it.data().match( r ) )
00708 currentQuery[arraycounter++] = it.data().uid();
00709
00710 }
00711
00712 currentQuery.resize(arraycounter);
00713
00714 return currentQuery;
00715 }
00716
00717 }