Re: PimConverter - addressbook export to CSV - CSV backend creation
"Marcos \"Japa\" Umino" <[email protected]>
| Newsgroups | gmane.comp.handhelds.opie.devel |
|---|---|
| Message-ID | <[email protected]> |
Oh, I forgot to attach new files. I had the silly idea that cvs diff took care of new files too. They go into opie/libopie2/opiepim/backend Namely, they're the text backend itself. TIA, Marcos 'TheJapa' On 7/15/06, Lorn Potter <[email protected]> wrote: > Thanks for your effort. > I can look into and patch. > Will do if someone else doesnt get to it first. > > > > On Saturday 15 July 2006 11:56, Marcos "Japa" Umino wrote: > > Hello opie-devs > > > > This is my first attempt submitting code, so if I did something wrong, > > go ahead and tell me (but please tell me what I did wrong) so I can > > try to fix. > > > > I generated these files using 'cvs diff -Nau' against opie HEAD tag. > > > > Basically what I implemented is a little backend working only for > > addressbook to do CSV export (or pimbackend's save() function), so we > > can export addressbook data to CSV, useful to send to your > > friend/secretary/manager non-vcard supporting contacts program. > > (Namely microsoft outlook) > > > > I'll implement CSV export for datebook and todo, and import from CSV > > too, that is, if you have no objections to it. > > > > It's primary use, as I see it, is for users migrating from Outlook > > based PDAs, like Windows Mobile iPaqs. I know from personal experience > > that once migration to linux on pda has started, the first concern is > > fast conversion of PIM data, and setting up opensync and syncing isn't > > exactly fast or easy. (for the beginner) > > > > It was necessary to create a new Rules.Make entry, as I designed > > everything as a new TEXT_BACKEND option. This way I hope text support > > will only be added if the distro maintainer finds it useful at all. > > > > Since this is my first adventure submitting code, I may have forgotten > > to modify some changelog-like file, IIRC, I haven't found any such > > file. > > > > Sorry for the long post, if you have any sort of criticism to make, > > please do, I want to do it right. > > > > Marcos "Thejapa" Umino > > -- > Lorn 'ljp' Potter > Trolltech Qtopia Community Manager > Opie Core Developer > http://qtopia.net > _______________________________________________ http://opie.handhelds.org/cgi-bin/moin.cgi/DeveloperWikiIndex Opie-devel mailing list [email protected] https://handhelds.org/mailman/listinfo/opie-devel
ocontactaccessbackend_text.cpp
(text/x-c++src, 17.6 KB)
/*
This file is part of the Opie Project
Copyright (C) Stefan Eilers ([email protected])
=. Copyright (C) The Opie Team <[email protected]>
.=l.
.>+-=
_;:, .> :=|. This program is free software; you can
.> <`_, > . <= redistribute it and/or modify it under
:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
.="- .-=="i, .._ License as published by the Free Software
- . .-<_> .<> Foundation; either version 2 of the License,
._= =} : or (at your option) any later version.
.%`+i> _;_.
.i_,=:_. -<s. This program is distributed in the hope that
+ . -:. = it will be useful, but WITHOUT ANY WARRANTY;
: .. .:, . . . without even the implied warranty of
=_ + =;=|` MERCHANTABILITY or FITNESS FOR A
_.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
..}^=.= = ; Library General Public License for more
++= -. .` .: details.
: = ...= . :.=-
-. .:....=;==+<; You should have received a copy of the GNU
-_. . . )=. = Library General Public License along with
-- :-=` this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/*
* Text Backend for the OPIE-Contact Database.
*/
/* OPIE */
#include <opie2/ocontactaccessbackend_text.h>
#include <opie2/xmltree.h>
#include <opie2/ocontactaccessbackend.h>
#include <opie2/ocontactaccess.h>
#include <opie2/odebug.h>
#include <opie2/opimcontact.h>
#include <qpe/global.h>
#include <qpe/stringutil.h>
/* QT */
#include <qasciidict.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qregexp.h>
#include <qarray.h>
#include <qmap.h>
/* STD */
#include <stdlib.h>
#include <errno.h>
using namespace Opie::Core;
namespace Opie {
OPimContactAccessBackend_Text::OPimContactAccessBackend_Text ( const QString& appname, const QString& filename ):
m_changed( false )
{
// Just m_contactlist should call delete if an entry
// is removed.
m_contactList.setAutoDelete( true );
m_uidToContact.setAutoDelete( false );
m_appName = appname;
/* Set journalfile name ... */
m_journalName = getenv("HOME");
m_journalName +="/.abjournal" + appname;
/* Expecting to access the default filename if nothing else is set */
if ( filename.isEmpty() ){
m_fileName = Global::applicationFileName( "addressbook","addressbook.csv" );
} else
m_fileName = filename;
/* Load Database now */
load ();
}
bool OPimContactAccessBackend_Text::save()
{
if ( !m_changed )
return true;
QString strNewFile = m_fileName + ".new";
QFile f( strNewFile );
if ( !f.open( IO_WriteOnly|IO_Raw ) )
return false;
int total_written;
int idx_offset = 0;
QString out;
// CSV in popular uses field names in first line and each record uses 1 line. You
// just separate fields using ','.
// The first shot will be using all fields and exporting empty fields as plain empty string.
// Write all contacts
QListIterator<OPimContact> it( m_contactList );
// Write Header, abusing first item to get fields()
static QStringList SLFIELDS = (*it)->fields();
//uid is the first field, no categories yet
out += "\"uid\", \"category id\", ";
for ( QStringList::Iterator it2 = SLFIELDS.begin(); it2 != SLFIELDS.end(); ++it2 ) {
const QString &field = *it2;
out += "\"" + Qtopia::escapeString( field ) + "\",";
}
out += "\"EMPTY\"\n";
QCString cstr = out.utf8();
f.writeBlock( cstr.data(), cstr.length() );
idx_offset += cstr.length();
out = "";
//now writing contacts
for ( ; it.current(); ++it ) {
(*it)->CSV( out );
cstr = out.utf8();
total_written = f.writeBlock( cstr.data(), cstr.length() );
idx_offset += cstr.length();
if ( total_written != int(cstr.length()) ) {
f.close();
QFile::remove( strNewFile );
return false;
}
out = "";
}
out += "\n";
// Write Footer
cstr = out.utf8();
total_written = f.writeBlock( cstr.data(), cstr.length() );
if ( total_written != int( cstr.length() ) ) {
f.close();
QFile::remove( strNewFile );
return false;
}
f.close();
// move the file over, I'm just going to use the system call
// because, I don't feel like using QDir.
if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
// remove the tmp file...
QFile::remove( strNewFile );
}
/* The journalfile should be removed now... */
removeJournal();
m_changed = false;
return true;
}
bool OPimContactAccessBackend_Text::load ()
{
m_contactList.clear();
m_uidToContact.clear();
/* Load XML-File and journal if it exists */
if ( !load ( m_fileName, false ) )
return false;
/* The returncode of the journalfile is ignored due to the
* fact that it does not exist when this class is instantiated !
* But there may such a file exist, if the application crashed.
* Therefore we try to load it to get the changes before the #
* crash happened...
*/
load (m_journalName, true);
return true;
}
void OPimContactAccessBackend_Text::clear ()
{
m_contactList.clear();
m_uidToContact.clear();
m_changed = false;
}
bool OPimContactAccessBackend_Text::wasChangedExternally()
{
QFileInfo fi( m_fileName );
QDateTime lastmod = fi.lastModified ();
return (lastmod != m_readtime);
}
UIDArray OPimContactAccessBackend_Text::allRecords() const
{
UIDArray uid_list( m_contactList.count() );
uint counter = 0;
QListIterator<OPimContact> it( m_contactList );
for( ; it.current(); ++it ){
uid_list[counter++] = (*it)->uid();
}
return ( uid_list );
}
OPimContact OPimContactAccessBackend_Text::find ( int uid ) const
{
OPimContact foundContact; //Create empty contact
OPimContact* found = m_uidToContact.find( QString().setNum( uid ) );
if ( found ){
foundContact = *found;
}
return ( foundContact );
}
UIDArray OPimContactAccessBackend_Text::matchRegexp( const QRegExp &r ) const
{
UIDArray m_currentQuery( m_contactList.count() );
QListIterator<OPimContact> it( m_contactList );
uint arraycounter = 0;
for( ; it.current(); ++it ){
if ( (*it)->match( r ) ){
m_currentQuery[arraycounter++] = (*it)->uid();
}
}
// Shrink to fit..
m_currentQuery.resize(arraycounter);
return m_currentQuery;
}
#if 0
// Currently only asc implemented..
UIDArray OPimContactAccessBackend_Text::sorted( bool asc, int , int , int )
{
QMap<QString, int> nameToUid;
QStringList names;
UIDArray m_currentQuery( m_contactList.count() );
// First fill map and StringList with all Names
// Afterwards sort namelist and use map to fill array to return..
QListIterator<OPimContact> it( m_contactList );
for( ; it.current(); ++it ){
names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) );
nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() );
}
names.sort();
int i = 0;
if ( asc ){
for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
m_currentQuery[i++] = nameToUid[ (*it) ];
}else{
for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
m_currentQuery[i++] = nameToUid[ (*it) ];
}
return m_currentQuery;
}
#endif
bool OPimContactAccessBackend_Text::add ( const OPimContact &newcontact )
{
updateJournal (newcontact, ACTION_ADD);
addContact_p( newcontact );
m_changed = true;
return true;
}
bool OPimContactAccessBackend_Text::replace ( const OPimContact &contact )
{
m_changed = true;
OPimContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) );
if ( found ) {
OPimContact* newCont = new OPimContact( contact );
updateJournal ( *newCont, ACTION_REPLACE);
m_contactList.removeRef ( found );
m_contactList.append ( newCont );
m_uidToContact.remove( QString().setNum( contact.uid() ) );
m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont );
return true;
} else
return false;
}
bool OPimContactAccessBackend_Text::remove ( int uid )
{
m_changed = true;
OPimContact* found = m_uidToContact.find ( QString().setNum( uid ) );
if ( found ) {
updateJournal ( *found, ACTION_REMOVE);
m_contactList.removeRef ( found );
m_uidToContact.remove( QString().setNum( uid ) );
return true;
} else
return false;
}
bool OPimContactAccessBackend_Text::reload(){
/* Reload is the same as load in this implementation */
return ( load() );
}
void OPimContactAccessBackend_Text::addContact_p( const OPimContact &newcontact )
{
OPimContact* contRef = new OPimContact( newcontact );
m_contactList.append ( contRef );
m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef );
}
/* This function loads the xml-database and the journalfile */
bool OPimContactAccessBackend_Text::load( const QString filename, bool isJournal )
{
/* We use the time of the last read to check if the file was
* changed externally.
*/
if ( !isJournal ){
QFileInfo fi( filename );
m_readtime = fi.lastModified ();
}
const int JOURNALACTION = Qtopia::Notes + 1;
const int JOURNALROW = JOURNALACTION + 1;
bool foundAction = false;
journal_action action = ACTION_ADD;
int journalKey = 0;
QMap<int, QString> contactMap;
QMap<QString, QString> customMap;
QMap<QString, QString>::Iterator customIt;
QAsciiDict<int> dict( 47 );
dict.setAutoDelete( TRUE );
dict.insert( "Uid", new int(Qtopia::AddressUid) );
dict.insert( "Title", new int(Qtopia::Title) );
dict.insert( "FirstName", new int(Qtopia::FirstName) );
dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
dict.insert( "LastName", new int(Qtopia::LastName) );
dict.insert( "Suffix", new int(Qtopia::Suffix) );
dict.insert( "FileAs", new int(Qtopia::FileAs) );
dict.insert( "Categories", new int(Qtopia::AddressCategory) );
dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
dict.insert( "Emails", new int(Qtopia::Emails) );
dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
dict.insert( "HomeState", new int(Qtopia::HomeState) );
dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
dict.insert( "Company", new int(Qtopia::Company) );
dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
dict.insert( "Department", new int(Qtopia::Department) );
dict.insert( "Office", new int(Qtopia::Office) );
dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
dict.insert( "Profession", new int(Qtopia::Profession) );
dict.insert( "Assistant", new int(Qtopia::Assistant) );
dict.insert( "Manager", new int(Qtopia::Manager) );
dict.insert( "Spouse", new int(Qtopia::Spouse) );
dict.insert( "Children", new int(Qtopia::Children) );
dict.insert( "Gender", new int(Qtopia::Gender) );
dict.insert( "Birthday", new int(Qtopia::Birthday) );
dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
dict.insert( "Nickname", new int(Qtopia::Nickname) );
dict.insert( "Notes", new int(Qtopia::Notes) );
dict.insert( "action", new int(JOURNALACTION) );
dict.insert( "actionrow", new int(JOURNALROW) );
XMLElement *root = XMLElement::load( filename );
if(root != 0l ){ // start parsing
/* Parse all XML-Elements and put the data into the
* Contact-Class
*/
XMLElement *element = root->firstChild();
element = element ? element->firstChild() : 0;
/* Search Tag "Contacts" which is the parent of all Contacts */
while( element && !isJournal ){
if( element->tagName() != QString::fromLatin1("Contacts") ){
element = element->nextChild();
} else {
element = element->firstChild();
break;
}
}
/* Parse all Contacts and ignore unknown tags */
while( element ){
if( element->tagName() != QString::fromLatin1("Contact") ){
element = element->nextChild();
continue;
}
/* Found alement with tagname "contact", now parse and store all
* attributes contained
*/
QString dummy;
foundAction = false;
XMLElement::AttributeMap aMap = element->attributes();
XMLElement::AttributeMap::Iterator it;
contactMap.clear();
customMap.clear();
for( it = aMap.begin(); it != aMap.end(); ++it ){
int *find = dict[ it.key() ];
/* Unknown attributes will be stored as "Custom" elements */
if ( !find ) {
//contact.setCustomField(it.key(), it.data());
customMap.insert( it.key(), it.data() );
continue;
}
/* Check if special conversion is needed and add attribute
* into Contact class
*/
switch( *find ) {
/*
case Qtopia::AddressUid:
contact.setUid( it.data().toInt() );
break;
case Qtopia::AddressCategory:
contact.setCategories( Qtopia::Record::idsFromString( it.data( )));
break;
*/
case JOURNALACTION:
action = journal_action(it.data().toInt());
foundAction = true;
owarn << "ODefBack(journal)::ACTION found: " << action << oendl;
break;
case JOURNALROW:
journalKey = it.data().toInt();
break;
default: // no conversion needed add them to the map
contactMap.insert( *find, it.data() );
break;
}
}
/* now generate the Contact contact */
OPimContact contact( contactMap );
for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
contact.setCustomField( customIt.key(), customIt.data() );
}
if (foundAction){
foundAction = false;
switch ( action ) {
case ACTION_ADD:
addContact_p (contact);
break;
case ACTION_REMOVE:
if ( !remove (contact.uid()) )
owarn << "ODefBack(journal)::Unable to remove uid: " << contact.uid() << oendl;
break;
case ACTION_REPLACE:
if ( !replace ( contact ) )
owarn << "ODefBack(journal)::Unable to replace uid: " << contact.uid() << oendl;
break;
default:
owarn << "Unknown action: ignored !" << oendl;
break;
}
}else{
/* Add contact to list */
addContact_p (contact);
}
/* Move to next element */
element = element->nextChild();
}
}else {
}
delete root;
return true;
}
void OPimContactAccessBackend_Text::updateJournal( const OPimContact& cnt,
journal_action action )
{
QFile f( m_journalName );
bool created = !f.exists();
if ( !f.open(IO_WriteOnly|IO_Append) )
return;
QString buf;
QCString str;
// if the file was created, we have to set the Tag "<CONTACTS>" to
// get a Text-File which is readable by our parser.
// This is just a cheat, but better than rewrite the parser.
if ( created ){
buf = "<Contacts>";
QCString cstr = buf.utf8();
f.writeBlock( cstr.data(), cstr.length() );
}
buf = "<Contact ";
cnt.save( buf );
buf += " action=\"" + QString::number( (int)action ) + "\" ";
buf += "/>\n";
QCString cstr = buf.utf8();
f.writeBlock( cstr.data(), cstr.length() );
}
void OPimContactAccessBackend_Text::removeJournal()
{
QFile f ( m_journalName );
if ( f.exists() )
f.remove();
}
}
ocontactaccessbackend_text.h
(text/x-chdr, 3.1 KB)
/*
This file is part of the Opie Project
Copyright (C) Stefan Eilers ([email protected])
=. Copyright (C) The Opie Team <[email protected]>
.=l.
.>+-=
_;:, .> :=|. This program is free software; you can
.> <`_, > . <= redistribute it and/or modify it under
:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
.="- .-=="i, .._ License as published by the Free Software
- . .-<_> .<> Foundation; either version 2 of the License,
._= =} : or (at your option) any later version.
.%`+i> _;_.
.i_,=:_. -<s. This program is distributed in the hope that
+ . -:. = it will be useful, but WITHOUT ANY WARRANTY;
: .. .:, . . . without even the implied warranty of
=_ + =;=|` MERCHANTABILITY or FITNESS FOR A
_.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
..}^=.= = ; Library General Public License for more
++= -. .` .: details.
: = ...= . :.=-
-. .:....=;==+<; You should have received a copy of the GNU
-_. . . )=. = Library General Public License along with
-- :-=` this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/*
* XML Backend for the OPIE-Contact Database.
*/
#ifndef _OPimContactAccessBackend_Text_
#define _OPimContactAccessBackend_Text_
#include <opie2/ocontactaccessbackend.h>
#include <opie2/ocontactaccess.h>
#include <qlist.h>
#include <qdict.h>
namespace Opie {
/* the Text implementation, adapted from XML by thejapa*/
/**
* This class is the Text implementation of a Contact backend
* it does implement everything available for OPimContact.
* @see OPimAccessBackend for more information of available methods
*/
class OPimContactAccessBackend_Text : public OPimContactAccessBackend {
public:
OPimContactAccessBackend_Text ( const QString& appname, const QString& filename = QString::null );
bool save();
bool load ();
void clear ();
bool wasChangedExternally();
UIDArray allRecords() const;
OPimContact find ( int uid ) const;
UIDArray matchRegexp( const QRegExp &r ) const;
bool add ( const OPimContact &newcontact );
bool replace ( const OPimContact &contact );
bool remove ( int uid );
bool reload();
private:
enum journal_action { ACTION_ADD, ACTION_REMOVE, ACTION_REPLACE };
void addContact_p( const OPimContact &newcontact );
/* This function loads the xml-database and the journalfile */
bool load( const QString filename, bool isJournal );
void updateJournal( const OPimContact& cnt, journal_action action );
void removeJournal();
protected:
bool m_changed;
QString m_journalName;
QString m_fileName;
QString m_appName;
QList<OPimContact> m_contactList;
QDateTime m_readtime;
QDict<OPimContact> m_uidToContact;
};
}
#endif
odatebookaccessbackend_text.cpp
(text/x-c++src, 19.3 KB)
/*
This file is part of the Opie Project
Copyright (C) Stefan Eilers ([email protected])
=. Copyright (C) The Opie Team <[email protected]>
.=l.
.>+-=
_;:, .> :=|. This program is free software; you can
.> <`_, > . <= redistribute it and/or modify it under
:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
.="- .-=="i, .._ License as published by the Free Software
- . .-<_> .<> Foundation; either version 2 of the License,
._= =} : or (at your option) any later version.
.%`+i> _;_.
.i_,=:_. -<s. This program is distributed in the hope that
+ . -:. = it will be useful, but WITHOUT ANY WARRANTY;
: .. .:, . . . without even the implied warranty of
=_ + =;=|` MERCHANTABILITY or FITNESS FOR A
_.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
..}^=.= = ; Library General Public License for more
++= -. .` .: details.
: = ...= . :.=-
-. .:....=;==+<; You should have received a copy of the GNU
-_. . . )=. = Library General Public License along with
-- :-=` this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/* OPIE */
#include <opie2/opimnotifymanager.h>
#include <opie2/opimrecurrence.h>
#include <opie2/opimtimezone.h>
#include <opie2/odatebookaccessbackend_text.h>
#include <opie2/odebug.h>
#include <qtopia/global.h>
#include <qtopia/stringutil.h>
#include <qtopia/timeconversion.h>
/* QT */
#include <qasciidict.h>
#include <qfile.h>
/* STD */
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace Opie;
namespace {
// FROM TT again
char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
{
char needleChar;
char haystackChar;
if (!needle || !haystack || !hLen || !nLen)
return 0;
const char* hsearch = haystack;
if ((needleChar = *needle++) != 0) {
nLen--; //(to make up for needle++)
do {
do {
if ((haystackChar = *hsearch++) == 0)
return (0);
if (hsearch >= haystack + hLen)
return (0);
} while (haystackChar != needleChar);
} while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
hsearch--;
}
return ((char *)hsearch);
}
}
namespace {
time_t start, end, created, rp_end;
OPimRecurrence* rec;
static OPimRecurrence* recur() {
if (!rec)
rec = new OPimRecurrence;
return rec;
}
int alarmTime;
int snd;
enum Attribute{
FDescription = 0,
FLocation,
FCategories,
FUid,
FType,
FAlarm,
FSound,
FRType,
FRWeekdays,
FRPosition,
FRFreq,
FRHasEndDate,
FREndDate,
FRStart,
FREnd,
FNote,
FCreated, // Should't this be called FRCreated ?
FTimeZone,
FRecParent,
FRecChildren,
FExceptions
};
// FIXME: Use OPimEvent::toMap() here !! (eilers)
static void save( const OPimEvent& ev, QString& buf ) {
buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\"";
if (!ev.location().isEmpty() )
buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\"";
if (!ev.categories().isEmpty() )
buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\"";
buf += " uid=\"" + QString::number( ev.uid() ) + "\"";
if (ev.isAllDay() )
buf += " type=\"AllDay\""; // is that all ?? (eilers)
if (ev.hasNotifiers() ) {
OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first
int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60;
buf += " alarm=\"" + QString::number(minutes) + "\" sound=\"";
if ( alarm.sound() == OPimAlarm::Loud )
buf += "loud";
else
buf += "silent";
buf += "\"";
}
if ( ev.hasRecurrence() ) {
buf += ev.recurrence().toString();
}
/*
* fscking timezones :) well, we'll first convert
* the QDateTime to a QDateTime in UTC time
* and then we'll create a nice time_t
*/
OPimTimeZone zone( (ev.timeZone().isEmpty()||ev.isAllDay()) ? OPimTimeZone::utc() : OPimTimeZone::current() );
buf += " start=\"" + QString::number( zone.fromDateTime( ev.startDateTime())) + "\"";
buf += " end=\"" + QString::number( zone.fromDateTime( ev.endDateTime() )) + "\"";
if (!ev.note().isEmpty() ) {
buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\"";
}
/*
* Don't save a timezone if AllDay Events
* as they're UTC only anyway
*/
if (!ev.isAllDay() ) {
buf += " timezone=\"";
if ( ev.timeZone().isEmpty() )
buf += "None";
else
buf += ev.timeZone();
buf += "\"";
}
if (ev.parent() != 0 ) {
buf += " recparent=\""+QString::number(ev.parent() )+"\"";
}
if (ev.children().count() != 0 ) {
QArray<int> children = ev.children();
buf += " recchildren=\"";
for ( uint i = 0; i < children.count(); i++ ) {
if ( i != 0 ) buf += " ";
buf += QString::number( children[i] );
}
buf+= "\"";
}
// skip custom writing
}
static bool saveEachEvent( const QMap<int, OPimEvent>& list, QFile& file ) {
QMap<int, OPimEvent>::ConstIterator it;
QString buf;
QCString str;
int total_written;
for ( it = list.begin(); it != list.end(); ++it ) {
buf = "<event";
save( it.data(), buf );
buf += " />\n";
str = buf.utf8();
total_written = file.writeBlock(str.data(), str.length() );
if ( total_written != int(str.length() ) )
return false;
}
return true;
}
}
namespace Opie {
ODateBookAccessBackend_Text::ODateBookAccessBackend_Text( const QString& ,
const QString& fileName )
: ODateBookAccessBackend() {
m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.csv" ) : fileName;
m_changed = false;
}
ODateBookAccessBackend_Text::~ODateBookAccessBackend_Text() {
}
bool ODateBookAccessBackend_Text::load() {
return loadFile();
}
bool ODateBookAccessBackend_Text::reload() {
clear();
return load();
}
bool ODateBookAccessBackend_Text::save() {
if (!m_changed) return true;
int total_written;
QString strFileNew = m_name + ".new";
QFile f( strFileNew );
if (!f.open( IO_WriteOnly | IO_Raw ) ) return false;
QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
buf += "<events>\n";
QCString str = buf.utf8();
total_written = f.writeBlock( str.data(), str.length() );
if ( total_written != int(str.length() ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
if (!saveEachEvent( m_raw, f ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
if (!saveEachEvent( m_rep, f ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
buf = "</events>\n</DATEBOOK>\n";
str = buf.utf8();
total_written = f.writeBlock( str.data(), str.length() );
if ( total_written != int(str.length() ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
f.close();
if ( ::rename( strFileNew, m_name ) < 0 ) {
QFile::remove( strFileNew );
return false;
}
m_changed = false;
return true;
}
QArray<int> ODateBookAccessBackend_Text::allRecords()const {
QArray<int> ints( m_raw.count()+ m_rep.count() );
uint i = 0;
QMap<int, OPimEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
ints[i] = it.key();
i++;
}
for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
QArray<int> ODateBookAccessBackend_Text::queryByExample(const OPimEvent&, int, const QDateTime& ) {
return QArray<int>();
}
void ODateBookAccessBackend_Text::clear() {
m_changed = true;
m_raw.clear();
m_rep.clear();
}
OPimEvent ODateBookAccessBackend_Text::find( int uid ) const{
if ( m_raw.contains( uid ) )
return m_raw[uid];
else
return m_rep[uid];
}
bool ODateBookAccessBackend_Text::add( const OPimEvent& ev ) {
m_changed = true;
if (ev.hasRecurrence() )
m_rep.insert( ev.uid(), ev );
else
m_raw.insert( ev.uid(), ev );
return true;
}
bool ODateBookAccessBackend_Text::remove( int uid ) {
m_changed = true;
m_raw.remove( uid );
m_rep.remove( uid );
return true;
}
bool ODateBookAccessBackend_Text::replace( const OPimEvent& ev ) {
replace( ev.uid() ); // ??? Shouldn't this be "remove( ev.uid() ) ??? (eilers)
return add( ev );
}
QArray<int> ODateBookAccessBackend_Text::rawRepeats()const {
QArray<int> ints( m_rep.count() );
uint i = 0;
QMap<int, OPimEvent>::ConstIterator it;
for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
QArray<int> ODateBookAccessBackend_Text::nonRepeats()const {
QArray<int> ints( m_raw.count() );
uint i = 0;
QMap<int, OPimEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
OPimEvent::ValueList ODateBookAccessBackend_Text::directNonRepeats()const {
OPimEvent::ValueList list;
QMap<int, OPimEvent>::ConstIterator it;
for (it = m_raw.begin(); it != m_raw.end(); ++it )
list.append( it.data() );
return list;
}
OPimEvent::ValueList ODateBookAccessBackend_Text::directRawRepeats()const {
OPimEvent::ValueList list;
QMap<int, OPimEvent>::ConstIterator it;
for (it = m_rep.begin(); it != m_rep.end(); ++it )
list.append( it.data() );
return list;
}
// FIXME: Use OPimEvent::fromMap() (eilers)
bool ODateBookAccessBackend_Text::loadFile() {
m_changed = false;
int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY );
if ( fd < 0 ) return false;
struct stat attribute;
if ( ::fstat(fd, &attribute ) == -1 ) {
::close( fd );
return false;
}
void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 );
if ( map_addr == ( (caddr_t)-1) ) {
::close( fd );
return false;
}
::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL );
::close( fd );
QAsciiDict<int> dict(FExceptions+1);
dict.setAutoDelete( true );
dict.insert( "description", new int(FDescription) );
dict.insert( "location", new int(FLocation) );
dict.insert( "categories", new int(FCategories) );
dict.insert( "uid", new int(FUid) );
dict.insert( "type", new int(FType) );
dict.insert( "alarm", new int(FAlarm) );
dict.insert( "sound", new int(FSound) );
dict.insert( "rtype", new int(FRType) );
dict.insert( "rweekdays", new int(FRWeekdays) );
dict.insert( "rposition", new int(FRPosition) );
dict.insert( "rfreq", new int(FRFreq) );
dict.insert( "rhasenddate", new int(FRHasEndDate) );
dict.insert( "enddt", new int(FREndDate) );
dict.insert( "start", new int(FRStart) );
dict.insert( "end", new int(FREnd) );
dict.insert( "note", new int(FNote) );
dict.insert( "created", new int(FCreated) ); // Shouldn't this be FRCreated ??
dict.insert( "recparent", new int(FRecParent) );
dict.insert( "recchildren", new int(FRecChildren) );
dict.insert( "exceptions", new int(FExceptions) );
dict.insert( "timezone", new int(FTimeZone) );
// initialiaze db hack
m_noTimeZone = true;
char* dt = (char*)map_addr;
int len = attribute.st_size;
int i = 0;
char* point;
const char* collectionString = "<event ";
int strLen = ::strlen(collectionString);
int *find;
while ( ( point = ::strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0 ) {
i = point -dt;
i+= strLen;
alarmTime = -1;
snd = 0; // silent
OPimEvent ev;
rec = 0;
while ( TRUE ) {
while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
++i;
if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
break;
// we have another attribute, read it.
int j = i;
while ( j < len && dt[j] != '=' )
++j;
QCString attr( dt+i, j-i+1);
i = ++j; // skip =
// find the start of quotes
while ( i < len && dt[i] != '"' )
++i;
j = ++i;
bool haveUtf = FALSE;
bool haveEnt = FALSE;
while ( j < len && dt[j] != '"' ) {
if ( ((unsigned char)dt[j]) > 0x7f )
haveUtf = TRUE;
if ( dt[j] == '&' )
haveEnt = TRUE;
++j;
}
if ( i == j ) {
// empty value
i = j + 1;
continue;
}
QCString value( dt+i, j-i+1 );
i = j + 1;
QString str = (haveUtf ? QString::fromUtf8( value )
: QString::fromLatin1( value ) );
if ( haveEnt )
str = Qtopia::plainString( str );
/*
* add key + value
*/
find = dict[attr.data()];
if (!find)
ev.setCustomField( attr, str );
else {
setField( ev, *find, str );
}
}
/* time to finalize */
finalizeRecord( ev );
delete rec;
m_noTimeZone = true;
}
::munmap(map_addr, attribute.st_size );
m_changed = false; // changed during add
return true;
}
// FIXME: Use OPimEvent::fromMap() which makes this obsolete.. (eilers)
void ODateBookAccessBackend_Text::finalizeRecord( OPimEvent& ev ) {
/*
* quirk to import datebook files. They normally don't have a
* timeZone attribute and we treat this as to use OPimTimeZone::current()
*/
if (m_noTimeZone )
ev.setTimeZone( OPimTimeZone::current().timeZone() );
/* AllDay is alway in UTC */
if ( ev.isAllDay() ) {
OPimTimeZone utc = OPimTimeZone::utc();
ev.setStartDateTime( utc.toDateTime( start ) );
ev.setEndDateTime ( utc.toDateTime( end ) );
}else {
/* to current date time */
OPimTimeZone to_zone( ev.timeZone().isEmpty() ? OPimTimeZone::utc() : OPimTimeZone::current() );
ev.setStartDateTime(to_zone.toDateTime( start));
ev.setEndDateTime (to_zone.toDateTime( end));
}
if ( rec && rec->doesRecur() ) {
OPimTimeZone utc = OPimTimeZone::utc();
OPimRecurrence recu( *rec ); // call copy c'tor;
recu.setEndDate ( utc.toDateTime( rp_end ).date() );
recu.setCreatedDateTime( utc.toDateTime( created ) );
recu.setStart( ev.startDateTime().date() );
ev.setRecurrence( recu );
}
if (alarmTime != -1 ) {
QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 );
OPimAlarm al( snd , dt );
ev.notifiers().add( al );
}
if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) {
ev.setUid( 1 );
}
if ( ev.hasRecurrence() )
m_rep.insert( ev.uid(), ev );
else
m_raw.insert( ev.uid(), ev );
}
void ODateBookAccessBackend_Text::setField( OPimEvent& e, int id, const QString& value) {
switch( id ) {
case FDescription:
e.setDescription( value );
break;
case FLocation:
e.setLocation( value );
break;
case FCategories:
e.setCategories( e.idsFromString( value ) );
break;
case FUid:
e.setUid( value.toInt() );
break;
case FType:
if ( value == "AllDay" ) {
e.setAllDay( true );
}
break;
case FAlarm:
alarmTime = value.toInt();
break;
case FSound:
snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent;
break;
// recurrence stuff
case FRType:
if ( value == "Daily" )
recur()->setType( OPimRecurrence::Daily );
else if ( value == "Weekly" )
recur()->setType( OPimRecurrence::Weekly);
else if ( value == "MonthlyDay" )
recur()->setType( OPimRecurrence::MonthlyDay );
else if ( value == "MonthlyDate" )
recur()->setType( OPimRecurrence::MonthlyDate );
else if ( value == "Yearly" )
recur()->setType( OPimRecurrence::Yearly );
else
recur()->setType( OPimRecurrence::NoRepeat );
break;
case FRWeekdays:
recur()->setDays( value.toInt() );
break;
case FRPosition:
recur()->setPosition( value.toInt() );
break;
case FRFreq:
recur()->setFrequency( value.toInt() );
break;
case FRHasEndDate:
recur()->setHasEndDate( value.toInt() );
break;
case FREndDate: {
rp_end = (time_t) value.toLong();
break;
}
case FRStart: {
start = (time_t) value.toLong();
break;
}
case FREnd: {
end = ( (time_t) value.toLong() );
break;
}
case FNote:
e.setNote( value );
break;
case FCreated:
created = value.toInt();
break;
case FRecParent:
e.setParent( value.toInt() );
break;
case FRecChildren:{
QStringList list = QStringList::split(' ', value );
for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
e.addChild( (*it).toInt() );
}
}
break;
case FExceptions:{
QStringList list = QStringList::split(' ', value );
for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() );
recur()->exceptions().append( date );
}
}
break;
case FTimeZone:
m_noTimeZone = false;
if ( value != "None" )
e.setTimeZone( value );
break;
default:
break;
}
}
QArray<int> ODateBookAccessBackend_Text::matchRegexp( const QRegExp &r ) const
{
QArray<int> m_currentQuery( m_raw.count()+ m_rep.count() );
uint arraycounter = 0;
QMap<int, OPimEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it )
if ( it.data().match( r ) )
m_currentQuery[arraycounter++] = it.data().uid();
for ( it = m_rep.begin(); it != m_rep.end(); ++it )
if ( it.data().match( r ) )
m_currentQuery[arraycounter++] = it.data().uid();
// Shrink to fit..
m_currentQuery.resize(arraycounter);
return m_currentQuery;
}
}
odatebookaccessbackend_text.h
(text/x-chdr, 3 KB)
/*
This file is part of the Opie Project
Copyright (C) Stefan Eilers ([email protected])
=. Copyright (C) The Opie Team <[email protected]>
.=l.
.>+-=
_;:, .> :=|. This program is free software; you can
.> <`_, > . <= redistribute it and/or modify it under
:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
.="- .-=="i, .._ License as published by the Free Software
- . .-<_> .<> Foundation; either version 2 of the License,
._= =} : or (at your option) any later version.
.%`+i> _;_.
.i_,=:_. -<s. This program is distributed in the hope that
+ . -:. = it will be useful, but WITHOUT ANY WARRANTY;
: .. .:, . . . without even the implied warranty of
=_ + =;=|` MERCHANTABILITY or FITNESS FOR A
_.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
..}^=.= = ; Library General Public License for more
++= -. .` .: details.
: = ...= . :.=-
-. .:....=;==+<; You should have received a copy of the GNU
-_. . . )=. = Library General Public License along with
-- :-=` this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_TEXT__H
#define OPIE_DATE_BOOK_ACCESS_BACKEND_TEXT__H
#include <qmap.h>
#include <opie2/odatebookaccessbackend.h>
namespace Opie {
/**
* Adapted from XML backend. Any thoughts please share with thejapa.
* @see ODateBookAccessBackend
* @see OPimAccessBackend
*/
class ODateBookAccessBackend_Text : public ODateBookAccessBackend {
public:
ODateBookAccessBackend_Text( const QString& appName,
const QString& fileName = QString::null);
~ODateBookAccessBackend_Text();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
QArray<int> matchRegexp(const QRegExp &r) const;
QArray<int> queryByExample( const OPimEvent&, int, const QDateTime& d = QDateTime() );
OPimEvent find( int uid )const;
void clear();
bool add( const OPimEvent& ev );
bool remove( int uid );
bool replace( const OPimEvent& ev );
QArray<UID> rawEvents()const;
QArray<UID> rawRepeats()const;
QArray<UID> nonRepeats()const;
OPimEvent::ValueList directNonRepeats()const;
OPimEvent::ValueList directRawRepeats()const;
private:
bool m_changed :1 ;
bool m_noTimeZone : 1;
bool loadFile();
inline void finalizeRecord( OPimEvent& ev );
inline void setField( OPimEvent&, int field, const QString& val );
QString m_name;
QMap<int, OPimEvent> m_raw;
QMap<int, OPimEvent> m_rep;
struct Data;
Data* data;
class Private;
Private *d;
};
}
#endif
otodoaccesstext.cpp
(text/x-c++src, 22.1 KB)
/*
This file is part of the Opie Project
Copyright (C) Stefan Eilers ([email protected])
=. Copyright (C) The Opie Team <[email protected]>
.=l.
.>+-=
_;:, .> :=|. This program is free software; you can
.> <`_, > . <= redistribute it and/or modify it under
:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
.="- .-=="i, .._ License as published by the Free Software
- . .-<_> .<> Foundation; either version 2 of the License,
._= =} : or (at your option) any later version.
.%`+i> _;_.
.i_,=:_. -<s. This program is distributed in the hope that
+ . -:. = it will be useful, but WITHOUT ANY WARRANTY;
: .. .:, . . . without even the implied warranty of
=_ + =;=|` MERCHANTABILITY or FITNESS FOR A
_.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
..}^=.= = ; Library General Public License for more
++= -. .` .: details.
: = ...= . :.=-
-. .:....=;==+<; You should have received a copy of the GNU
-_. . . )=. = Library General Public License along with
-- :-=` this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/* OPIE */
#include <opie2/opimdateconversion.h>
#include <opie2/opimstate.h>
#include <opie2/opimtimezone.h>
#include <opie2/opimnotifymanager.h>
#include <opie2/opimrecurrence.h>
#include <opie2/otodoaccesstext.h>
#include <opie2/otodoaccess.h>
#include <opie2/odebug.h>
#include <opie2/private/opimtodosortvector.h>
#include <qpe/global.h>
#include <qpe/stringutil.h>
#include <qpe/timeconversion.h>
/* QT */
#include <qfile.h>
#include <qvector.h>
/* STD */
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace Opie;
namespace {
time_t rp_end;
OPimRecurrence* rec;
OPimRecurrence *recur() {
if (!rec ) rec = new OPimRecurrence;
return rec;
}
int snd;
enum MoreAttributes {
FRType = OPimTodo::CompletedDate + 2,
FRWeekdays,
FRPosition,
FRFreq,
FRHasEndDate,
FREndDate,
FRStart,
FREnd
};
// FROM TT again
char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
{
char needleChar;
char haystackChar;
if (!needle || !haystack || !hLen || !nLen)
return 0;
const char* hsearch = haystack;
if ((needleChar = *needle++) != 0) {
nLen--; //(to make up for needle++)
do {
do {
if ((haystackChar = *hsearch++) == 0)
return (0);
if (hsearch >= haystack + hLen)
return (0);
} while (haystackChar != needleChar);
} while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
hsearch--;
}
return ((char *)hsearch);
}
}
namespace Opie {
OPimTodoAccessText::OPimTodoAccessText( const QString& appName,
const QString& fileName )
: OPimTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false )
{
if (!fileName.isEmpty() )
m_file = fileName;
else
m_file = Global::applicationFileName( "todolist", "todolist.csv" );
}
OPimTodoAccessText::~OPimTodoAccessText() {
}
bool OPimTodoAccessText::load() {
rec = 0;
m_opened = true;
m_changed = false;
/* initialize dict */
/*
* UPDATE dict if you change anything!!!
*/
QAsciiDict<int> dict(26);
dict.setAutoDelete( TRUE );
dict.insert("Categories" , new int(OPimTodo::Category) );
dict.insert("Uid" , new int(OPimTodo::Uid) );
dict.insert("HasDate" , new int(OPimTodo::HasDate) );
dict.insert("Completed" , new int(OPimTodo::Completed) );
dict.insert("Description" , new int(OPimTodo::Description) );
dict.insert("Summary" , new int(OPimTodo::Summary) );
dict.insert("Priority" , new int(OPimTodo::Priority) );
dict.insert("DateDay" , new int(OPimTodo::DateDay) );
dict.insert("DateMonth" , new int(OPimTodo::DateMonth) );
dict.insert("DateYear" , new int(OPimTodo::DateYear) );
dict.insert("Progress" , new int(OPimTodo::Progress) );
dict.insert("CompletedDate", new int(OPimTodo::CompletedDate) );
dict.insert("StartDate", new int(OPimTodo::StartDate) );
dict.insert("CrossReference", new int(OPimTodo::CrossReference) );
dict.insert("State", new int(OPimTodo::State) );
dict.insert("Alarms", new int(OPimTodo::Alarms) );
dict.insert("Reminders", new int(OPimTodo::Reminders) );
dict.insert("Maintainer", new int(OPimTodo::Maintainer) );
dict.insert("rtype", new int(FRType) );
dict.insert("rweekdays", new int(FRWeekdays) );
dict.insert("rposition", new int(FRPosition) );
dict.insert("rfreq", new int(FRFreq) );
dict.insert("start", new int(FRStart) );
dict.insert("rhasenddate", new int(FRHasEndDate) );
dict.insert("enddt", new int(FREndDate) );
// here the custom XML parser from TT it's GPL
// but we want to push OpiePIM... to TT.....
// mmap part from zecke :)
int fd = ::open( QFile::encodeName(m_file).data(), O_RDONLY );
struct stat attribut;
if ( fd < 0 ) return false;
if ( fstat(fd, &attribut ) == -1 ) {
::close( fd );
return false;
}
void* map_addr = ::mmap(NULL, attribut.st_size, PROT_READ, MAP_SHARED, fd, 0 );
if ( map_addr == ( (caddr_t)-1) ) {
::close(fd );
return false;
}
/* advise the kernel who we want to read it */
::madvise( map_addr, attribut.st_size, MADV_SEQUENTIAL );
/* we do not the file any more */
::close( fd );
char* dt = (char*)map_addr;
int len = attribut.st_size;
int i = 0;
char *point;
const char* collectionString = "<Task ";
int strLen = strlen(collectionString);
while ( ( point = strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0l ) {
i = point -dt;
i+= strLen;
OPimTodo ev;
m_year = m_month = m_day = 0;
while ( TRUE ) {
while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
++i;
if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
break;
// we have another attribute, read it.
int j = i;
while ( j < len && dt[j] != '=' )
++j;
QCString attr( dt+i, j-i+1);
i = ++j; // skip =
// find the start of quotes
while ( i < len && dt[i] != '"' )
++i;
j = ++i;
bool haveUtf = FALSE;
bool haveEnt = FALSE;
while ( j < len && dt[j] != '"' ) {
if ( ((unsigned char)dt[j]) > 0x7f )
haveUtf = TRUE;
if ( dt[j] == '&' )
haveEnt = TRUE;
++j;
}
if ( i == j ) {
// empty value
i = j + 1;
continue;
}
QCString value( dt+i, j-i+1 );
i = j + 1;
QString str = (haveUtf ? QString::fromUtf8( value )
: QString::fromLatin1( value ) );
if ( haveEnt )
str = Qtopia::plainString( str );
/*
* add key + value
*/
todo( &dict, ev, attr, str );
}
/*
* now add it
*/
if (m_events.contains( ev.uid() ) || ev.uid() == 0) {
ev.setUid( 1 );
m_changed = true;
}
if ( ev.hasDueDate() ) {
ev.setDueDate( QDate(m_year, m_month, m_day) );
}
if ( rec && rec->doesRecur() ) {
OPimTimeZone utc = OPimTimeZone::utc();
OPimRecurrence recu( *rec ); // call copy c'tor
recu.setEndDate( utc.fromUTCDateTime( rp_end ).date() );
recu.setStart( ev.dueDate() );
ev.setRecurrence( recu );
}
m_events.insert(ev.uid(), ev );
m_year = m_month = m_day = -1;
delete rec;
rec = 0;
}
munmap(map_addr, attribut.st_size );
return true;
}
bool OPimTodoAccessText::reload() {
m_events.clear();
return load();
}
bool OPimTodoAccessText::save() {
if (!m_opened || !m_changed ) {
return true;
}
QString strNewFile = m_file + ".new";
QFile f( strNewFile );
if (!f.open( IO_WriteOnly|IO_Raw ) )
return false;
int written;
QString out;
out = "<!DOCTYPE Tasks>\n<Tasks>\n";
// for all todos
QMap<int, OPimTodo>::Iterator it;
for (it = m_events.begin(); it != m_events.end(); ++it ) {
out+= "<Task " + toString( (*it) ) + " />\n";
QCString cstr = out.utf8();
written = f.writeBlock( cstr.data(), cstr.length() );
/* less written then we wanted */
if ( written != (int)cstr.length() ) {
f.close();
QFile::remove( strNewFile );
return false;
}
out = QString::null;
}
out += "</Tasks>";
QCString cstr = out.utf8();
written = f.writeBlock( cstr.data(), cstr.length() );
if ( written != (int)cstr.length() ) {
f.close();
QFile::remove( strNewFile );
return false;
}
/* flush before renaming */
f.close();
if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) {
QFile::remove( strNewFile );
}
m_changed = false;
return true;
}
QArray<int> OPimTodoAccessText::allRecords()const {
QArray<int> ids( m_events.count() );
QMap<int, OPimTodo>::ConstIterator it;
int i = 0;
for ( it = m_events.begin(); it != m_events.end(); ++it )
ids[i++] = it.key();
return ids;
}
OPimTodo OPimTodoAccessText::find( int uid )const {
OPimTodo todo;
todo.setUid( 0 ); // isEmpty()
QMap<int, OPimTodo>::ConstIterator it = m_events.find( uid );
if ( it != m_events.end() )
todo = it.data();
return todo;
}
void OPimTodoAccessText::clear() {
if (m_opened )
m_changed = true;
m_events.clear();
}
bool OPimTodoAccessText::add( const OPimTodo& todo ) {
m_changed = true;
m_events.insert( todo.uid(), todo );
return true;
}
bool OPimTodoAccessText::remove( int uid ) {
m_changed = true;
m_events.remove( uid );
return true;
}
bool OPimTodoAccessText::replace( const OPimTodo& todo) {
m_changed = true;
m_events.replace( todo.uid(), todo );
return true;
}
QArray<int> OPimTodoAccessText::effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates )const {
QArray<int> ids( m_events.count() );
QMap<int, OPimTodo>::ConstIterator it;
int i = 0;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
if ( !it.data().hasDueDate() && includeNoDates) {
ids[i++] = it.key();
}else if ( it.data().dueDate() >= start &&
it.data().dueDate() <= end ) {
ids[i++] = it.key();
}
}
ids.resize( i );
return ids;
}
QArray<int> OPimTodoAccessText::overDue()const {
QArray<int> ids( m_events.count() );
int i = 0;
QMap<int, OPimTodo>::ConstIterator it;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
if ( it.data().isOverdue() ) {
ids[i] = it.key();
i++;
}
}
ids.resize( i );
return ids;
}
/* private */
void OPimTodoAccessText::todo( QAsciiDict<int>* dict, OPimTodo& ev,
const QCString& attr, const QString& val) {
int *find=0;
find = (*dict)[ attr.data() ];
if (!find ) {
ev.setCustomField( attr, val );
return;
}
switch( *find ) {
case OPimTodo::Uid:
ev.setUid( val.toInt() );
break;
case OPimTodo::Category:
ev.setCategories( ev.idsFromString( val ) );
break;
case OPimTodo::HasDate:
ev.setHasDueDate( val.toInt() );
break;
case OPimTodo::Completed:
ev.setCompleted( val.toInt() );
break;
case OPimTodo::Description:
ev.setDescription( val );
break;
case OPimTodo::Summary:
ev.setSummary( val );
break;
case OPimTodo::Priority:
ev.setPriority( val.toInt() );
break;
case OPimTodo::DateDay:
m_day = val.toInt();
break;
case OPimTodo::DateMonth:
m_month = val.toInt();
break;
case OPimTodo::DateYear:
m_year = val.toInt();
break;
case OPimTodo::Progress:
ev.setProgress( val.toInt() );
break;
case OPimTodo::CompletedDate:
ev.setCompletedDate( OPimDateConversion::dateFromString( val ) );
break;
case OPimTodo::StartDate:
ev.setStartDate( OPimDateConversion::dateFromString( val ) );
break;
case OPimTodo::State:
ev.setState( val.toInt() );
break;
case OPimTodo::Alarms:{
OPimNotifyManager &manager = ev.notifiers();
QStringList als = QStringList::split(";", val );
for (QStringList::Iterator it = als.begin(); it != als.end(); ++it ) {
QStringList alarm = QStringList::split(":", (*it), TRUE ); // allow empty
OPimAlarm al( alarm[2].toInt(), OPimDateConversion::dateTimeFromString( alarm[0] ), alarm[1].toInt() );
manager.add( al );
}
}
break;
case OPimTodo::Reminders:{
OPimNotifyManager &manager = ev.notifiers();
QStringList rems = QStringList::split(";", val );
for (QStringList::Iterator it = rems.begin(); it != rems.end(); ++it ) {
OPimReminder rem( (*it).toInt() );
manager.add( rem );
}
}
break;
case OPimTodo::CrossReference:
{
/*
* A cross refernce looks like
* appname,id;appname,id
* we need to split it up
*/
QStringList refs = QStringList::split(';', val );
QStringList::Iterator strIt;
for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) {
int pos = (*strIt).find(',');
if ( pos > -1 )
; // ev.addRelation( (*strIt).left(pos), (*strIt).mid(pos+1).toInt() );
}
break;
}
/* Recurrence stuff below + post processing later */
case FRType:
if ( val == "Daily" )
recur()->setType( OPimRecurrence::Daily );
else if ( val == "Weekly" )
recur()->setType( OPimRecurrence::Weekly);
else if ( val == "MonthlyDay" )
recur()->setType( OPimRecurrence::MonthlyDay );
else if ( val == "MonthlyDate" )
recur()->setType( OPimRecurrence::MonthlyDate );
else if ( val == "Yearly" )
recur()->setType( OPimRecurrence::Yearly );
else
recur()->setType( OPimRecurrence::NoRepeat );
break;
case FRWeekdays:
recur()->setDays( val.toInt() );
break;
case FRPosition:
recur()->setPosition( val.toInt() );
break;
case FRFreq:
recur()->setFrequency( val.toInt() );
break;
case FRHasEndDate:
recur()->setHasEndDate( val.toInt() );
break;
case FREndDate: {
rp_end = (time_t) val.toLong();
break;
}
default:
ev.setCustomField( attr, val );
break;
}
}
// from PalmtopRecord... GPL ### FIXME
namespace {
QString customToText(const QMap<QString, QString>& customMap )
{
QString buf(" ");
for ( QMap<QString, QString>::ConstIterator cit = customMap.begin();
cit != customMap.end(); ++cit) {
buf += cit.key();
buf += "=\"";
buf += Qtopia::escapeString(cit.data());
buf += "\" ";
}
return buf;
}
}
QString OPimTodoAccessText::toString( const OPimTodo& ev )const {
QString str;
str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" ";
str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" ";
str += "Priority=\"" + QString::number( ev.priority() ) + "\" ";
str += "Progress=\"" + QString::number(ev.progress() ) + "\" ";
str += "Categories=\"" + toString( ev.categories() ) + "\" ";
str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" ";
str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" ";
if ( ev.hasDueDate() ) {
str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" ";
str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" ";
str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" ";
}
str += "Uid=\"" + QString::number( ev.uid() ) + "\" ";
// append the extra options
/* FIXME Qtopia::Record this is currently not
* possible you can set custom fields
* but don' iterate over the list
* I may do #define private protected
* for this case - cough --zecke
*/
/*
QMap<QString, QString> extras = ev.extras();
QMap<QString, QString>::Iterator extIt;
for (extIt = extras.begin(); extIt != extras.end(); ++extIt )
str += extIt.key() + "=\"" + extIt.data() + "\" ";
*/
// cross refernce
if ( ev.hasRecurrence() ) {
str += ev.recurrence().toString();
}
if ( ev.hasStartDate() )
str += "StartDate=\""+ OPimDateConversion::dateToString( ev.startDate() ) +"\" ";
if ( ev.hasCompletedDate() )
str += "CompletedDate=\""+ OPimDateConversion::dateToString( ev.completedDate() ) +"\" ";
if ( ev.hasState() )
str += "State=\""+QString::number( ev.state().state() )+"\" ";
/*
* save reminders and notifiers!
* DATE_TIME:DURATION:SOUND:NOT_USED_YET;OTHER_DATE_TIME:OTHER_DURATION:SOUND:....
*/
if ( ev.hasNotifiers() ) {
OPimNotifyManager manager = ev.notifiers();
OPimNotifyManager::Alarms alarms = manager.alarms();
if (!alarms.isEmpty() ) {
QStringList als;
OPimNotifyManager::Alarms::Iterator it = alarms.begin();
for ( ; it != alarms.end(); ++it ) {
/* only if time is valid */
if ( (*it).dateTime().isValid() ) {
als << OPimDateConversion::dateTimeToString( (*it).dateTime() )
+ ":" + QString::number( (*it).duration() )
+ ":" + QString::number( (*it).sound() )
+ ":";
}
}
// now write the list
str += "Alarms=\""+als.join(";") +"\" ";
}
/*
* now the same for reminders but more easy. We just save the uid of the OPimEvent.
*/
OPimNotifyManager::Reminders reminders = manager.reminders();
if (!reminders.isEmpty() ) {
OPimNotifyManager::Reminders::Iterator it = reminders.begin();
QStringList records;
for ( ; it != reminders.end(); ++it ) {
records << QString::number( (*it).recordUid() );
}
str += "Reminders=\""+ records.join(";") +"\" ";
}
}
str += customToText( ev.toExtraMap() );
return str;
}
QString OPimTodoAccessText::toString( const QArray<int>& ints ) const {
return Qtopia::Record::idsToString( ints );
}
QArray<int> OPimTodoAccessText::sorted( const UIDArray& events, bool asc,
int sortOrder,int sortFilter,
const QArray<int>& categories )const {
Internal::OPimTodoSortVector vector(events.count(), asc,sortOrder );
int item = 0;
bool bCat = sortFilter & OPimTodoAccess::FilterCategory ? true : false;
bool bOnly = sortFilter & OPimTodoAccess::OnlyOverDue ? true : false;
bool comp = sortFilter & OPimTodoAccess::DoNotShowCompleted ? true : false;
bool catPassed = false;
int cat;
for ( uint i = 0; i < events.count(); ++i ) {
/* Guard against creating a new item... */
if ( !m_events.contains( events[i] ) )
continue;
OPimTodo todo = m_events[events[i]];
/* show category */
/* -1 == unfiled */
catPassed = false;
for ( uint cat_nu = 0; cat_nu < categories.count(); ++cat_nu ) {
cat = categories[cat_nu];
if ( bCat && cat == -1 ) {
if(!todo.categories().isEmpty() )
continue;
} else if ( bCat && cat != 0)
if (!todo.categories().contains( cat ) )
continue;
catPassed = true;
break;
}
/*
* If none of the Categories matched
* continue
*/
if ( !catPassed )
continue;
if ( !todo.isOverdue() && bOnly )
continue;
if (todo.isCompleted() && comp )
continue;
vector.insert(item++, todo );
}
vector.resize( item );
/* sort it now */
vector.sort();
/* now get the uids */
UIDArray array( vector.count() );
for (uint i= 0; i < vector.count(); i++ )
array[i] = vector.uidAt( i );
return array;
}
void OPimTodoAccessText::removeAllCompleted() {
QMap<int, OPimTodo> events = m_events;
for ( QMap<int, OPimTodo>::Iterator it = m_events.begin(); it != m_events.end(); ++it ) {
if ( (*it).isCompleted() )
events.remove( it.key() );
}
m_events = events;
}
QArray<int> OPimTodoAccessText::matchRegexp( const QRegExp &r ) const
{
QArray<int> currentQuery( m_events.count() );
uint arraycounter = 0;
QMap<int, OPimTodo>::ConstIterator it;
for (it = m_events.begin(); it != m_events.end(); ++it ) {
if ( it.data().match( r ) )
currentQuery[arraycounter++] = it.data().uid();
}
// Shrink to fit..
currentQuery.resize(arraycounter);
return currentQuery;
}
}
otodoaccesstext.h
(text/x-chdr, 3 KB)
/*
This file is part of the Opie Project
Copyright (C) Stefan Eilers ([email protected])
=. Copyright (C) The Opie Team <[email protected]>
.=l.
.>+-=
_;:, .> :=|. This program is free software; you can
.> <`_, > . <= redistribute it and/or modify it under
:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
.="- .-=="i, .._ License as published by the Free Software
- . .-<_> .<> Foundation; either version 2 of the License,
._= =} : or (at your option) any later version.
.%`+i> _;_.
.i_,=:_. -<s. This program is distributed in the hope that
+ . -:. = it will be useful, but WITHOUT ANY WARRANTY;
: .. .:, . . . without even the implied warranty of
=_ + =;=|` MERCHANTABILITY or FITNESS FOR A
_.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
..}^=.= = ; Library General Public License for more
++= -. .` .: details.
: = ...= . :.=-
-. .:....=;==+<; You should have received a copy of the GNU
-_. . . )=. = Library General Public License along with
-- :-=` this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef OPIE_TODO_ACCESS_TEXT_H
#define OPIE_TODO_ACCESS_TEXT_H
#include <qasciidict.h>
#include <qmap.h>
#include <opie2/otodoaccessbackend.h>
namespace Opie {
class TextElement;
class OPimTodoAccessText : public OPimTodoAccessBackend {
public:
/**
* fileName if Empty we will use the default path
*/
OPimTodoAccessText( const QString& appName,
const QString& fileName = QString::null );
~OPimTodoAccessText();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
QArray<int> matchRegexp(const QRegExp &r) const;
OPimTodo find( int uid )const;
void clear();
bool add( const OPimTodo& );
bool remove( int uid );
void removeAllCompleted();
bool replace( const OPimTodo& );
/* our functions */
QArray<int> effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates )const;
QArray<int> overDue()const;
//@{
UIDArray sorted( const UIDArray&, bool, int, int, const QArray<int>& )const;
//@}
private:
void todo( QAsciiDict<int>*, OPimTodo&,const QCString&,const QString& );
QString toString( const OPimTodo& )const;
QString toString( const QArray<int>& ints ) const;
QMap<int, OPimTodo> m_events;
QString m_file;
QString m_app;
bool m_opened : 1;
bool m_changed : 1;
class OPimTodoAccessTextPrivate;
OPimTodoAccessTextPrivate* d;
int m_year, m_month, m_day;
};
};
#endif