From 15318cad33835e4e2dc620d033e43cd930676cdd Mon Sep 17 00:00:00 2001 From: kergoth Date: Fri, 25 Jan 2002 22:14:26 +0000 Subject: Initial revision --- (limited to 'library/backend') diff --git a/library/backend/.cvsignore b/library/backend/.cvsignore new file mode 100644 index 0000000..e047b1f --- a/dev/null +++ b/library/backend/.cvsignore @@ -0,0 +1,2 @@ +moc_* +*.moc diff --git a/library/backend/categories.cpp b/library/backend/categories.cpp new file mode 100644 index 0000000..91331db --- a/dev/null +++ b/library/backend/categories.cpp @@ -0,0 +1,701 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included +** in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A +** PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#include "categories.h" +#include +#include +#include +#include "stringutil.h" + +using namespace Qtopia; + +/*********************************************************** + * + * CategoryGroup + * + **********************************************************/ + +#ifdef PALMTOPCENTER +UidGen CategoryGroup::sUidGen( UidGen::PalmtopCenter ); +#else +UidGen CategoryGroup::sUidGen( UidGen::Qtopia ); +#endif + +int CategoryGroup::add( const QString &label ) +{ + if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") ) + return 0; + + QMap::Iterator findIt = mLabelIdMap.find( label ); + if ( findIt != mLabelIdMap.end() ) + return 0; + int newUid = uidGen().generate(); + insert( newUid, label ); + return newUid; +} + +void CategoryGroup::insert( int uid, const QString &label ) +{ + uidGen().store( uid ); + mIdLabelMap[uid] = label; + mLabelIdMap[label] = uid; +} + +bool CategoryGroup::add( int uid, const QString &label ) +{ + if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") ) + return FALSE; + + QMap::ConstIterator labelIt = mLabelIdMap.find( label ); + if ( labelIt != mLabelIdMap.end() ) + return FALSE; + QMap::ConstIterator idIt = mIdLabelMap.find( uid ); + if ( idIt != mIdLabelMap.end() ) + return FALSE; + insert( uid, label ); + return TRUE; +} + +bool CategoryGroup::remove( const QString &label ) +{ + QMap::Iterator findIt = mLabelIdMap.find( label ); + if ( findIt == mLabelIdMap.end() ) + return FALSE; + + mIdLabelMap.remove( *findIt ); + mLabelIdMap.remove( findIt ); + + return TRUE; +} + +bool CategoryGroup::remove( int uid ) +{ + QMap::Iterator idIt = mIdLabelMap.find( uid ); + if ( idIt == mIdLabelMap.end() ) + return FALSE; + + mLabelIdMap.remove( *idIt ); + mIdLabelMap.remove( idIt ); + + return TRUE; +} + +bool CategoryGroup::rename( int uid, const QString &newLabel ) +{ + if ( newLabel == QObject::tr("All") || newLabel == QObject::tr("Unfiled") ) + return FALSE; + + QMap::Iterator idIt = mIdLabelMap.find( uid ); + if ( idIt == mIdLabelMap.end() ) + return FALSE; + + mLabelIdMap.remove( *idIt ); + mLabelIdMap[newLabel] = uid; + *idIt = newLabel; + + return TRUE; +} + +bool CategoryGroup::rename( const QString &oldLabel, const QString &newLabel ) +{ + return rename( id(oldLabel), newLabel ); +} + +bool CategoryGroup::contains(int uid) const +{ + return ( mIdLabelMap.find( uid ) != mIdLabelMap.end() ); +} + +bool CategoryGroup::contains(const QString &label) const +{ + return ( mLabelIdMap.find( label ) != mLabelIdMap.end() ); +} + +/** Returns label associated with the uid or QString::null if + * not found + */ +const QString &CategoryGroup::label(int uid) const +{ + QMap::ConstIterator idIt = mIdLabelMap.find( uid ); + if ( idIt == mIdLabelMap.end() ) + return QString::null; + return *idIt; +} + +/** Returns the uid associated with label or 0 if not found */ +int CategoryGroup::id(const QString &label) const +{ + QMap::ConstIterator labelIt = mLabelIdMap.find( label ); + if ( labelIt == mLabelIdMap.end() ) + return 0; + return *labelIt; +} + +QStringList CategoryGroup::labels() const +{ + QStringList labels; + for ( QMap::ConstIterator it = mIdLabelMap.begin(); + it != mIdLabelMap.end(); ++it ) + labels += *it; + // ### I don't think this is the place for this... +// labels.sort(); + return labels; +} + +QStringList CategoryGroup::labels(const QArray &catids ) const +{ + QStringList labels; + if ( catids.count() == 0 ) + return labels; + for ( QMap::ConstIterator it = mIdLabelMap.begin(); + it != mIdLabelMap.end(); ++it ) + if ( catids.find( it.key() ) != -1 ) + labels += *it; + return labels; +} + +QArray CategoryGroup::ids( const QStringList &cats ) const +{ + QArray results; + + for ( QStringList::ConstIterator catIt = cats.begin(); + catIt != cats.end(); ++catIt ) { + if ( *catIt == QObject::tr("All") || *catIt == QObject::tr("Unfiled") ) + continue; + int value = id( *catIt ); + if ( value != 0 ) { + int tmp = results.size(); + results.resize( tmp + 1 ); + results[ tmp ] = value; + } + } + + return results; +} + +QArray CategoryGroup::ids() const +{ + QArray results( mIdLabelMap.count() ); + int i = 0; + for ( QMap::ConstIterator it = mIdLabelMap.begin(); + it != mIdLabelMap.end(); ++it ) + results[i++] = it.key(); + + return results; +} + +/*********************************************************** + * + * Categories + * + **********************************************************/ + +/** Add the category name as long as it doesn't already exist locally + * or globally. Return TRUE if added, FALSE if conflicts. + */ +int Categories::addCategory( const QString &appname, + const QString &catname, + int uid ) +{ + if ( mGlobalCats.contains(catname) ) + return 0; + + QMap< QString, CategoryGroup >::Iterator + appIt = mAppCats.find( appname ); + + if ( appIt == mAppCats.end() ) { + CategoryGroup newgroup; + newgroup.add( uid, catname ); + mAppCats.insert( appname, newgroup ); + emit categoryAdded( *this, appname, uid ); + return uid; + } + + CategoryGroup &cats = *appIt; + cats.add( uid, catname ); + emit categoryAdded( *this, appname, uid ); + return uid; +} + +int Categories::addCategory( const QString &appname, + const QString &catname ) +{ + if ( mGlobalCats.contains(catname) ) + return 0; + + QMap< QString, CategoryGroup >::Iterator + appIt = mAppCats.find( appname ); + + if ( appIt == mAppCats.end() ) { + CategoryGroup newgroup; + int uid = newgroup.add( catname ); + mAppCats.insert( appname, newgroup ); + emit categoryAdded( *this, appname, uid ); + return uid; + } + + CategoryGroup &cats = *appIt; + int uid = cats.add( catname ); + if ( !uid ) + return 0; + emit categoryAdded( *this, appname, uid ); + return uid; +} + +int Categories::addGlobalCategory( const QString &catname, int uid ) +{ + mGlobalCats.add( uid, catname ); + emit categoryAdded( *this, QString::null, uid ); + return uid; +} + +int Categories::addGlobalCategory( const QString &catname ) +{ + int uid = mGlobalCats.add( catname ); + if ( !uid ) + return 0; + emit categoryAdded( *this, QString::null, uid ); + return uid; +} + +/** Removes the category from the application; if it is not found + * in the application, then it attempts to remove it from + * the global list + */ +bool Categories::removeCategory( const QString &appname, + const QString &catname, + bool checkGlobal ) +{ + QMap< QString, CategoryGroup >::Iterator + appIt = mAppCats.find( appname ); + if ( appIt != mAppCats.end() ) { + CategoryGroup &cats = *appIt; + int uid = cats.id( catname ); + if ( cats.remove( uid ) ) { + emit categoryRemoved( *this, appname, uid ); + return TRUE; + } + } + if ( !checkGlobal ) + return FALSE; + return removeGlobalCategory( catname ); +} + +bool Categories::removeCategory( const QString &appname, int uid ) +{ + QMap< QString, CategoryGroup >::Iterator + appIt = mAppCats.find( appname ); + if ( appIt != mAppCats.end() ) { + CategoryGroup &cats = *appIt; + if ( cats.remove( uid ) ) { + emit categoryRemoved( *this, appname, uid ); + return TRUE; + } + } + return FALSE; +} + +bool Categories::removeGlobalCategory( const QString &catname ) +{ + int uid = mGlobalCats.id( catname ); + if ( mGlobalCats.remove( uid ) ) { + emit categoryRemoved( *this, QString::null, uid ); + return TRUE; + } + return FALSE; +} + + +bool Categories::removeGlobalCategory( int uid ) +{ + if ( mGlobalCats.remove( uid ) ) { + emit categoryRemoved( *this, QString::null, uid ); + return TRUE; + } + return FALSE; +} + +/** Returns the sorted list of all categories that are associated with + * the app. If includeGlobal parameter is TRUE then the returned + * categories will include the global category items. + */ +QStringList Categories::labels( const QString &app, + bool includeGlobal, + ExtraLabels extra ) const +{ + QMap< QString, CategoryGroup >::ConstIterator + appIt = mAppCats.find( app ); + QStringList cats; + switch ( extra ) { + case NoExtra: break; + case AllUnfiled: + cats.append( tr("All") ); + cats.append( tr("Unfiled") ); + break; + case AllLabel: + cats.append( tr("All") ); + break; + case UnfiledLabel: + cats.append( tr("Unfiled") ); + break; + } + if ( appIt != mAppCats.end() ) + cats += (*appIt).labels(); + else qDebug("Categories::labels didn't find app %s", app.latin1() ); + if ( includeGlobal ) + cats += mGlobalCats.labels(); + // I don't think a sorted list is useful, the user might find prefer + // it in the original order. +// cats.sort(); + return cats; +} + +QString Categories::label( const QString &app, int id ) const +{ + if ( mGlobalCats.contains( id ) ) + return mGlobalCats.label( id ); + QMap< QString, CategoryGroup >::ConstIterator + appIt = mAppCats.find( app ); + if ( appIt == mAppCats.end() ) + return QString::null; + return (*appIt).label( id ); +} + +QStringList Categories::labels( const QString & app, + const QArray &catids ) const +{ + QStringList strs = mGlobalCats.labels( catids ); + strs += mAppCats[app].labels( catids ); + return strs; +} + +/** Returns a single string associated with the cat ids for display in + * a combobox or any area that requires one string. If catids are empty + * then "Unfiled" will be returned. If multiple categories are assigned + * the first cat id is shown with " (multi)" appended to the string. + */ +QString Categories::displaySingle( const QString &app, + const QArray &catids, + DisplaySingle display ) const +{ + QStringList strs = labels( app, catids ); + if ( !strs.count() ) + return tr("Unfiled"); + strs.sort(); + QString r; + if ( strs.count() > 1 ) { + switch ( display ) { + case ShowFirst: + r = strs.first(); + break; + case ShowMulti: + r = strs.first() + tr(" (multi.)"); + break; + case ShowAll: + r = strs.join(" "); + break; + } + } + else r = strs.first(); + return r; +} + +QArray Categories::ids( const QString &app ) const +{ + QArray allIds = mGlobalCats.ids(); + QArray appIds = mAppCats[app].ids(); + + // we should make the guarentee that the ids are in the + // same order as the labels, (i.e. app cats then global) + // otherwise there is no point in having these two separate functions. + uint appSize = appIds.size(); + appIds.resize( appSize + allIds.size() ); + for ( uint i = appSize; i < appIds.size(); ++i ) + appIds[int(i)] = allIds[int(i - appSize)]; + + return appIds; +} + +QArray Categories::ids( const QString &app, const QStringList &cats ) const +{ + QArray allIds = mGlobalCats.ids( cats ); + QArray appIds = mAppCats[app].ids( cats ); + + uint appSize = appIds.size(); + appIds.resize( appSize + allIds.size() ); + for ( uint i = appSize; i < appIds.size(); ++i ) + appIds[int(i)] = allIds[int(i - appSize)]; + + return appIds; +} + +int Categories::id( const QString &app, const QString &cat ) const +{ + if ( cat == tr("Unfiled") || cat.contains( tr(" (multi.)") ) ) + return 0; + int uid = mGlobalCats.id( cat ); + if ( uid != 0 ) + return uid; + return mAppCats[app].id( cat ); +} + + +/** Return TRUE if renaming succeeded; FALSE if app name not found, + * or if there was a name conflict + */ +bool Categories::renameCategory( const QString &appname, + const QString &oldName, + const QString &newName ) +{ + QMap< QString, CategoryGroup >::Iterator + appIt = mAppCats.find( appname ); + + if ( appIt != mAppCats.end() ) { + CategoryGroup &cats = *appIt; + int id = cats.id( oldName ); + if ( id != 0 && cats.rename( id, newName ) ) { + emit categoryRenamed( *this, appname, id ); + return TRUE; + } + } + return renameGlobalCategory( oldName, newName ); +} + +bool Categories::renameGlobalCategory( const QString &oldName, + const QString &newName ) +{ + int uid = mGlobalCats.id( oldName ); + if ( uid != 0 && mGlobalCats.rename( uid, newName ) ) { + emit categoryRenamed( *this, QString::null, uid ); + return TRUE; + } + return FALSE; +} + +void Categories::setGlobal( const QString &appname, + const QString &catname, + bool global ) +{ + // if in global and should be in app; then move it + if ( mGlobalCats.contains( catname ) && !global ) { + mGlobalCats.remove( catname ); + addCategory( appname, catname ); + return ; + } + + // if in app and should be in global, then move it + if ( !global ) + return; + if ( removeCategory( appname, catname, FALSE ) ) + addGlobalCategory( catname ); +} + +bool Categories::isGlobal( const QString &catname ) const +{ + return mGlobalCats.contains( catname ); +} + + +/** Returns true if the catname is associated with any application + */ +bool Categories::exists( const QString &catname ) const +{ + if ( isGlobal(catname) ) + return TRUE; + + for ( QMap::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt ) + if ( exists( appsIt.key(), catname ) ) + return TRUE; + + return FALSE; +} + +bool Categories::exists( const QString &appname, + const QString &catname) const +{ + QMap< QString, CategoryGroup >::ConstIterator + appIt = mAppCats.find( appname ); + + if ( appIt == mAppCats.end() ) + return FALSE; + + return (*appIt).contains( catname ); +} + +bool Categories::save( const QString &fname ) const +{ + QFile file( fname ); + if ( !file.open( IO_WriteOnly ) ) { + qWarning("Unable to write to %s", fname.latin1()); + return FALSE; + } + + QTextStream ts( &file ); + ts << "\n"; + ts << "" << endl; + + ts << "" << endl; + for ( QMap::ConstIterator git = mGlobalCats.idMap().begin(); + git != mGlobalCats.idMap().end(); ++git ) + ts << "" << endl; + + for ( QMap::ConstIterator appsIt=mAppCats.begin(); + appsIt != mAppCats.end(); ++appsIt ) { + const QString &app = appsIt.key(); + const QMap &appcats = (*appsIt).idMap(); + for ( QMap::ConstIterator appcatit = appcats.begin(); + appcatit != appcats.end(); ++appcatit ) + ts << "" << endl; + } + ts << "" << endl; + + file.close(); + return TRUE; +} + +bool Categories::load( const QString &fname ) +{ + QFile file( fname ); + if ( !file.open( IO_ReadOnly ) ) { + qWarning("Unable to open %s", fname.latin1()); + return FALSE; + } + + clear(); + QByteArray ba = file.readAll(); + QString data = QString::fromUtf8( ba.data(), ba.size() ); + QChar *uc = (QChar *)data.unicode(); + int len = data.length(); + + // QTime t; + // t.start(); + QString name; + QString id; + QString app; + int i = 0; + while ( (i = data.find( "= len-2 || (uc[i] == '/' && uc[i+1] == '>') ) + break; + // we have another attribute read it. + int j = i; + while ( j < len && uc[j] != '=' ) + j++; + QString attr = QConstString( uc+i, j-i ).string(); + i = ++j; // skip = + while ( i < len && uc[i] != '"' ) + i++; + j = ++i; + while ( j < len && uc[j] != '"' ) + j++; + QString value = Qtopia::plainString( QConstString( uc+i, j-i ).string() ); + i = j + 1; + +// qDebug("attr='%s' value='%s'", attr.latin1(), value.latin1() ); + if ( attr == "id" ) + id = value; + else if ( attr == "app" ) + app = value; + + else if ( attr == "name" ) + name = value; + } + + if ( name.isNull() || id.isNull() ) { + qWarning("No name or id in the category"); + continue; + } + if ( app.isNull() ) + mGlobalCats.add( id.toInt(), name ); + else + mAppCats[ app ].add( id.toInt(), name ); + } + + return TRUE; +} + +void Categories::clear() +{ + mGlobalCats.clear(); + mAppCats.clear(); +} + +void Categories::dump() const +{ + qDebug("\tglobal categories = %s", mGlobalCats.labels().join(", ").latin1() ); + for ( QMap::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt ) { + const QString &app = appsIt.key(); + QStringList appcats = (*appsIt).labels(); + qDebug("\tapp = %s\tcategories = %s", app.latin1(), + appcats.join(", ").latin1() ); + + } +} + +QStringList CheckedListView::checked() const +{ + QStringList strs; + for ( QCheckListItem *i = (QCheckListItem *) firstChild(); + i; i = (QCheckListItem *)i->nextSibling() ) + if ( i->isOn() ) + strs += i->text( 0 ); + return strs; +} + +void CheckedListView::addCheckableList( const QStringList &options ) +{ + for ( QStringList::ConstIterator it = options.begin(); + it != options.end(); ++it ) { + (void) new QCheckListItem( this, *it, + QCheckListItem::CheckBox ); + } +} + +void CheckedListView::setChecked( const QStringList &checked ) +{ + // iterate over all items + bool showingChecked = FALSE; + for ( QCheckListItem *i = (QCheckListItem *) firstChild(); + i; i = (QCheckListItem *)i->nextSibling() ) + // see if the item should be checked by searching the + // checked list + if ( checked.find( i->text( 0 ) ) != checked.end() ) { + i->setOn( TRUE ); + // make sure it is showing at least one checked item + if ( !showingChecked ) { + ensureItemVisible( i ); + showingChecked = TRUE; + } + } + else + i->setOn( FALSE ); +} diff --git a/library/backend/categories.h b/library/backend/categories.h new file mode 100644 index 0000000..82d765b --- a/dev/null +++ b/library/backend/categories.h @@ -0,0 +1,232 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included +** in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A +** PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QTPALMTOP_CATEGORIES_H +#define QTPALMTOP_CATEGORIES_H + +#include +#include +#include +#include +#include +#include "qpcglobal.h" +#include "palmtopuidgen.h" + +class CategoryGroup; + +#if defined(QPC_TEMPLATEDLL) +// MOC_SKIP_BEGIN +template class QPC_EXPORT QMap; +template class QPC_EXPORT QMap; +template class QPC_EXPORT QMap< QString, CategoryGroup >; +// MOC_SKIP_END +#endif + +class QPC_EXPORT CategoryGroup +{ + friend class Categories; +public: + CategoryGroup(): mIdLabelMap(), mLabelIdMap() { } + CategoryGroup( const CategoryGroup &c ) : + mIdLabelMap( c.mIdLabelMap), mLabelIdMap( c.mLabelIdMap ) { } + + void clear() { mIdLabelMap.clear(); mLabelIdMap.clear(); } + + int add( const QString &label ); + bool add( int uid, const QString &label ); + + bool remove( const QString &label ); + bool remove( int uid ); + + bool rename( int uid, const QString &newLabel ); + bool rename( const QString &oldLabel, const QString &newLabel ); + + bool contains(int id) const; + bool contains(const QString &label) const; + + /** Returns label associated with the uid or QString::null if + * not found + */ + const QString &label(int id) const; + /** Returns the uid associated with label or 0 if not found */ + int id(const QString &label) const; + + /** Returns a sorted list of labels */ + QStringList labels() const; + QArray ids( const QStringList &cats ) const; + QArray ids() const; + QStringList labels( const QArray &catids ) const; + + const QMap &idMap() const { return mIdLabelMap; } + +private: + void insert( int uid, const QString &label ); + QMap mIdLabelMap; + QMap mLabelIdMap; + + static Qtopia::UidGen &uidGen() { return sUidGen; } + static Qtopia::UidGen sUidGen; +}; + +/** Map from application name to categories */ +class QPC_EXPORT Categories : public QObject +{ + Q_OBJECT +public: + Categories( QObject *parent=0, const char *name = 0 ) + : QObject( parent, name ), mGlobalCats(), mAppCats() { } + Categories( const Categories ©From ) : QObject( copyFrom.parent() ), + mGlobalCats( copyFrom.mGlobalCats ), + mAppCats( copyFrom.mAppCats ) { } + virtual ~Categories() { } + + Categories &operator= ( const Categories &c ) +{ mAppCats = c.mAppCats; mGlobalCats = c.mGlobalCats; return *this; } + + void clear(); + + /** Add the category name as long as it doesn't already exist + * locally or globally. Return UID if added, 0 if conflicts + * (error). + */ + int addCategory( const QString &appname, const QString &catname); + /** Add the category name as long as it doesn't already exist + * locally or globally. Return UID if added, 0 if conflicts + * (error). + */ + int addCategory( const QString &appname, const QString &catname, int uid); + /** Add the global category just checking that it doesn't + * already exist globally. Return UID if added, 0 if conflicts. + */ + int addGlobalCategory( const QString &catname ); + /** Add the global category just checking that it doesn't + * already exist globally. Return UID if added, 0 if conflicts. + */ + int addGlobalCategory( const QString &catname, int uid ); + /** Removes the category from the application; if it is not found + * in the application, then it removes it from the global list + */ + bool removeCategory( const QString &appName, const QString &catName, + bool checkGlobal = TRUE); + bool removeCategory( const QString &appName, int uid ); + bool removeGlobalCategory( const QString &catName ); + bool removeGlobalCategory( int uid ); + + QArray ids( const QString &app ) const; + QArray ids( const QString &app, + const QStringList &cats ) const; + /** Returns the id associated with the app */ + int id( const QString &app, const QString &cat ) const; + /** Returns the label associated with the id */ + QString label( const QString &app, int id ) const; + + enum ExtraLabels { NoExtra, AllUnfiled, AllLabel, UnfiledLabel }; + /** Returns the sorted list of all categories that are + * associated with the app. + * If includeGlobal parameter is TRUE then the returned + * categories will include the global category items. + * If extra = NoExtra, then + * If extra = AllUnfiled, then All and Unfiled will be prepended to + * the list + * If extra = AllLabel, then All is prepended + * If extra = UnfiledLabel, then Unfiled is prepended + */ + QStringList labels( const QString &app, + bool includeGlobal = TRUE, + ExtraLabels extra = NoExtra ) const; + + /** Returns the labels of the categories associated with the uids */ + QStringList labels( const QString & app, + const QArray &catids ) const; + + enum DisplaySingle { ShowMulti, ShowAll, ShowFirst }; + + /** Returns a single string associated with the cat ids for display in + * a combobox or any area that requires one string. If catids are empty + * then "Unfiled" will be returned. If multiple categories are assigned + * then the behavior depends on the DisplaySingle type. + * If /a display is set to ShowMulti then " (multi)" appended to the + * first string. If /a display is set to ShowAll, then a space seperated + * string is returned with all categories. If ShowFirst is returned, + * the just the first string is returned. + */ + QString displaySingle( const QString &app, + const QArray &catids, + DisplaySingle display ) const; + + QStringList globalCategories() const { return mGlobalCats.labels();} + + bool renameCategory( const QString &appname, + const QString &oldName, + const QString &newName ); + bool renameGlobalCategory( const QString &oldName, + const QString &newName ); + + void setGlobal( const QString &appname, const QString &catname, + bool value ); + bool isGlobal( const QString &catname ) const; + + + /** Returns true if the catname is associated with any application + */ + bool exists( const QString &catname ) const; + bool exists( const QString &appname, const QString &catname) const; + + bool save( const QString &fname ) const; + bool load( const QString &fname ); + + // for debugging + void dump() const; + + const QMap &appGroupMap() const{ return mAppCats; } + const CategoryGroup &globalGroup() const { return mGlobalCats; } + +signals: + /** emitted if added a category; + * the second param is the application the category was added to + * or null if global + * the third param is the uid of the newly added category + */ + void categoryAdded( const Categories &, const QString &, int ); + /** emitted if removed a category + * the second param is the application the category was removed from + * or null if global + * the third param is the uid of the removed category + */ + void categoryRemoved( const Categories &, const QString &, int ); + /** emitted if a category is renamed; the second param is the uid of + * the removed category */ + void categoryRenamed( const Categories &, const QString &, int ); + +private: + CategoryGroup mGlobalCats; + QMap< QString, CategoryGroup > mAppCats; +}; + +class QPC_EXPORT CheckedListView : public QListView +{ +public: + void addCheckableList( const QStringList &options ); + void setChecked( const QStringList &checked ); + QStringList checked() const; +}; + +#endif diff --git a/library/backend/contact.cpp b/library/backend/contact.cpp new file mode 100644 index 0000000..a5f10ab --- a/dev/null +++ b/library/backend/contact.cpp @@ -0,0 +1,909 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "contact.h" +#include "vobject_p.h" +#include "qfiledirect_p.h" + +#include +#include + +#include +#include +#include +#include + +#include + +Qtopia::UidGen Contact::sUidGen( Qtopia::UidGen::Qtopia ); + +Contact::Contact() + : Record(), mMap(), d( 0 ) +{ +} + +Contact::Contact( const QMap &fromMap ) : + Record(), mMap( fromMap ), d( 0 ) +{ + QString cats = mMap[ Qtopia::AddressCategory ]; + if ( !cats.isEmpty() ) + setCategories( idsFromString( cats ) ); + QString uidStr = find( Qtopia::AddressUid ); + if ( uidStr.isEmpty() ) + setUid( uidGen().generate() ); + else + setUid( uidStr.toInt() ); +} + +Contact::~Contact() +{ +} + +QMap Contact::toMap() const +{ + QMap map = mMap; + map.insert( Qtopia::AddressCategory, idsToString( categories() )); + return map; +} + +/*! + Returns a rich text formatted QString of the Contact. +*/ +QString Contact::toRichText() const +{ + QString text; + QString value, comp, state; + + // name, jobtitle and company + if ( !(value = fullName()).isEmpty() ) + text += "" + Qtopia::escapeString(value) + "
"; + if ( !(value = jobTitle()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + + comp = company(); + if ( !(value = department()).isEmpty() ) { + text += Qtopia::escapeString(value); + if ( comp ) + text += ", "; + else + text += "
"; + } + if ( !comp.isEmpty() ) + text += Qtopia::escapeString(comp) + "
"; + + // business address + if ( !businessStreet().isEmpty() || !businessCity().isEmpty() || + !businessZip().isEmpty() || !businessCountry().isEmpty() ) { + text += "
"; + text += QObject::tr( "Work Address:" ); + text += "
"; + } + + if ( !(value = businessStreet()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + state = businessState(); + if ( !(value = businessCity()).isEmpty() ) { + text += Qtopia::escapeString(value); + if ( state ) + text += ", " + Qtopia::escapeString(state); + text += "
"; + } else if ( !state.isEmpty() ) + text += Qtopia::escapeString(state) + "
"; + if ( !(value = businessZip()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + if ( !(value = businessCountry()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + + // home address + if ( !homeStreet().isEmpty() || !homeCity().isEmpty() || + !homeZip().isEmpty() || !homeCountry().isEmpty() ) { + text += "
"; + text += QObject::tr( "Home Address:" ); + text += "
"; + } + + if ( !(value = homeStreet()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + state = homeState(); + if ( !(value = homeCity()).isEmpty() ) { + text += Qtopia::escapeString(value); + if ( !state.isEmpty() ) + text += ", " + Qtopia::escapeString(state); + text += "
"; + } else if (!state.isEmpty()) + text += Qtopia::escapeString(state) + "
"; + if ( !(value = homeZip()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + if ( !(value = homeCountry()).isEmpty() ) + text += Qtopia::escapeString(value) + "
"; + + // the others... + QString str; + str = emails(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Email Addresses: ") + "" + + Qtopia::escapeString(str) + "
"; + str = homePhone(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Home Phone: ") + "" + + Qtopia::escapeString(str) + "
"; + str = homeFax(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Home Fax: ") + "" + + Qtopia::escapeString(str) + "
"; + str = homeMobile(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Home Mobile: ") + "" + + Qtopia::escapeString(str) + "
"; + str = homeWebpage(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Home Web Page: ") + "" + + Qtopia::escapeString(str) + "
"; + str = businessWebpage(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Business Web Page: ") + "" + + Qtopia::escapeString(str) + "
"; + str = office(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Office: ") + "" + + Qtopia::escapeString(str) + "
"; + str = businessPhone(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Business Phone: ") + "" + + Qtopia::escapeString(str) + "
"; + str = businessFax(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Business Fax: ") + "" + + Qtopia::escapeString(str) + "
"; + str = businessMobile(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Business Mobile: ") + "" + + Qtopia::escapeString(str) + "
"; + str = businessPager(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Business Pager: ") + "" + + Qtopia::escapeString(str) + "
"; + str = profession(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Profession: ") + "" + + Qtopia::escapeString(str) + "
"; + str = assistant(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Assistant: ") + "" + + Qtopia::escapeString(str) + "
"; + str = manager(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Manager: ") + "" + + Qtopia::escapeString(str) + "
"; + str = gender(); + if ( !str.isEmpty() && str.toInt() != 0 ) { + if ( str.toInt() == 1 ) + str = QObject::tr( "Male" ); + else if ( str.toInt() == 2 ) + str = QObject::tr( "Female" ); + text += "" + QObject::tr("Gender: ") + "" + str + "
"; + } + str = spouse(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Spouse: ") + "" + + Qtopia::escapeString(str) + "
"; + str = birthday(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Birthday: ") + "" + + Qtopia::escapeString(str) + "
"; + str = anniversary(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Anniversary: ") + "" + + Qtopia::escapeString(str) + "
"; + str = nickname(); + if ( !str.isEmpty() ) + text += "" + QObject::tr("Nickname: ") + "" + + Qtopia::escapeString(str) + "
"; + + // notes last + if ( (value = notes()) ) { + QRegExp reg("\n"); + + //QString tmp = Qtopia::escapeString(value); + QString tmp = QStyleSheet::convertFromPlainText(value); + //tmp.replace( reg, "
" ); + text += "
" + tmp + "
"; + } + return text; +} + +void Contact::insert( int key, const QString &v ) +{ + QString value = v.stripWhiteSpace(); + if ( value.isEmpty() ) + mMap.remove( key ); + else + mMap.insert( key, value ); +} + +void Contact::replace( int key, const QString & v ) +{ + QString value = v.stripWhiteSpace(); + if ( value.isEmpty() ) + mMap.remove( key ); + else + mMap.replace( key, value ); +} + +QString Contact::find( int key ) const +{ + return mMap[key]; +} + +QString Contact::displayAddress( const QString &street, + const QString &city, + const QString &state, + const QString &zip, + const QString &country ) const +{ + QString s = street; + if ( !street.isEmpty() ) + s+= "\n"; + s += city; + if ( !city.isEmpty() && !state.isEmpty() ) + s += ", "; + s += state; + if ( !state.isEmpty() && !zip.isEmpty() ) + s += " "; + s += zip; + if ( !country.isEmpty() && !s.isEmpty() ) + s += "\n"; + s += country; + return s; +} + +QString Contact::displayBusinessAddress() const +{ + return displayAddress( businessStreet(), businessCity(), + businessState(), businessZip(), + businessCountry() ); +} + +QString Contact::displayHomeAddress() const +{ + return displayAddress( homeStreet(), homeCity(), + homeState(), homeZip(), + homeCountry() ); +} + +QString Contact::fullName() const +{ + QString title = find( Qtopia::Title ); + QString firstName = find( Qtopia::FirstName ); + QString middleName = find( Qtopia::MiddleName ); + QString lastName = find( Qtopia::LastName ); + QString suffix = find( Qtopia::Suffix ); + + QString name = title; + if ( !firstName.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += firstName; + } + if ( !middleName.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += middleName; + } + if ( !lastName.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += lastName; + } + if ( !suffix.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += suffix; + } + return name.simplifyWhiteSpace(); +} + +QStringList Contact::childrenList() const +{ + return QStringList::split( " ", find( Qtopia::Children ) ); +} + +QStringList Contact::emailList() const +{ + return QStringList::split( ";", find( Qtopia::Emails ) ); +} + +void Contact::setFileAs() +{ + QString lastName, firstName, middleName, fileas; + + lastName = find( Qtopia::LastName ); + firstName = find( Qtopia::FirstName ); + middleName = find( Qtopia::MiddleName ); + if ( !lastName.isEmpty() && !firstName.isEmpty() + && !middleName.isEmpty() ) + fileas = lastName + ", " + firstName + " " + middleName; + else if ( !lastName.isEmpty() && !firstName.isEmpty() ) + fileas = lastName + ", " + firstName; + else if ( !lastName.isEmpty() || !firstName.isEmpty() || + !middleName.isEmpty() ) + fileas = firstName + ( firstName.isEmpty() ? "" : " " ) + + middleName + ( middleName.isEmpty() ? "" : " " ) + + lastName; + + replace( Qtopia::FileAs, fileas ); +} + +void Contact::save( QString &buf ) const +{ + static const QStringList SLFIELDS = fields(); + // I'm expecting "::ConstIterator it = mMap.begin(); + it != mMap.end(); ++it ) { + const QString &value = it.data(); + int key = it.key(); + if ( !value.isEmpty() ) { + if ( key == Qtopia::AddressCategory || key == Qtopia::AddressUid) + continue; + + key -= Qtopia::AddressCategory+1; + buf += SLFIELDS[key]; + buf += "=\"" + Qtopia::escapeString(value) + "\" "; + } + } + buf += customToXml(); + if ( categories().count() > 0 ) + buf += "Categories=\"" + idsToString( categories() ) + "\" "; + buf += "Uid=\"" + QString::number( uid() ) + "\" "; + // You need to close this yourself +} + +QStringList Contact::fields() +{ + QStringList list; + + list.append( "Title" ); // Not Used! + list.append( "FirstName" ); + list.append( "MiddleName" ); + list.append( "LastName" ); + list.append( "Suffix" ); + list.append( "FileAs" ); + + list.append( "DefaultEmail" ); + list.append( "Emails" ); + + list.append( "HomeStreet" ); + list.append( "HomeCity" ); + list.append( "HomeState" ); + list.append( "HomeZip" ); + list.append( "HomeCountry" ); + list.append( "HomePhone" ); + list.append( "HomeFax" ); + list.append( "HomeMobile" ); + list.append( "HomeWebPage" ); + + list.append( "Company" ); + list.append( "BusinessStreet" ); + list.append( "BusinessCity" ); + list.append( "BusinessState" ); + list.append( "BusinessZip" ); + list.append( "BusinessCountry" ); + list.append( "BusinessWebPage" ); + list.append( "JobTitle" ); + list.append( "Department" ); + list.append( "Office" ); + list.append( "BusinessPhone" ); + list.append( "BusinessFax" ); + list.append( "BusinessMobile" ); + list.append( "BusinessPager" ); + list.append( "Profession" ); + list.append( "Assistant" ); + list.append( "Manager" ); + + list.append( "Spouse" ); + list.append( "Gender" ); + list.append( "Birthday" ); + list.append( "Anniversary" ); + list.append( "Nickname" ); + + list.append( "Children" ); + list.append( "Notes" ); + + return list; +} + +QStringList Contact::trfields() +{ + QStringList list; + + list.append( QObject::tr( "Name Title") ); + list.append( QObject::tr( "First Name" ) ); + list.append( QObject::tr( "Middle Name" ) ); + list.append( QObject::tr( "Last Name" ) ); + list.append( QObject::tr( "Suffix" ) ); + list.append( QObject::tr( "File As" ) ); + + list.append( QObject::tr( "Default Email" ) ); + list.append( QObject::tr( "Emails" ) ); + + list.append( QObject::tr( "Home Street" ) ); + list.append( QObject::tr( "Home City" ) ); + list.append( QObject::tr( "Home State" ) ); + list.append( QObject::tr( "Home Zip" ) ); + list.append( QObject::tr( "Home Country" ) ); + list.append( QObject::tr( "Home Phone" ) ); + list.append( QObject::tr( "Home Fax" ) ); + list.append( QObject::tr( "Home Mobile" ) ); + list.append( QObject::tr( "Home Web Page" ) ); + + list.append( QObject::tr( "Company" ) ); + list.append( QObject::tr( "Business Street" ) ); + list.append( QObject::tr( "Business City" ) ); + list.append( QObject::tr( "Business State" ) ); + list.append( QObject::tr( "Business Zip" ) ); + list.append( QObject::tr( "Business Country" ) ); + list.append( QObject::tr( "Business WebPage" ) ); + list.append( QObject::tr( "Job Title" ) ); + list.append( QObject::tr( "Department" ) ); + list.append( QObject::tr( "Office" ) ); + list.append( QObject::tr( "Business Phone" ) ); + list.append( QObject::tr( "Business Fax" ) ); + list.append( QObject::tr( "Business Mobile" ) ); + list.append( QObject::tr( "Business Pager" ) ); + list.append( QObject::tr( "Profession" ) ); + list.append( QObject::tr( "Assistant" ) ); + list.append( QObject::tr( "Manager" ) ); + + list.append( QObject::tr( "Spouse" ) ); + list.append( QObject::tr( "Gender" ) ); + list.append( QObject::tr( "Birthday" ) ); + list.append( QObject::tr( "Anniversary" ) ); + list.append( QObject::tr( "Nickname" ) ); + + list.append( QObject::tr( "Children" ) ); + list.append( QObject::tr( "Notes" ) ); + + return list; +} + +void Contact::setEmails( const QString &v ) +{ + replace( Qtopia::Emails, v ); + if ( v.isEmpty() ) + setDefaultEmail( QString::null ); +} + +void Contact::setChildren( const QString &v ) +{ + replace( Qtopia::Children, v ); +} + +// vcard conversion code +static inline VObject *safeAddPropValue( VObject *o, const char *prop, const QString &value ) +{ + VObject *ret = 0; + if ( o && !value.isEmpty() ) + ret = addPropValue( o, prop, value.latin1() ); + return ret; +} + +static inline VObject *safeAddProp( VObject *o, const char *prop) +{ + VObject *ret = 0; + if ( o ) + ret = addProp( o, prop ); + return ret; +} + +static VObject *createVObject( const Contact &c ) +{ + VObject *vcard = newVObject( VCCardProp ); + safeAddPropValue( vcard, VCVersionProp, "2.1" ); + safeAddPropValue( vcard, VCLastRevisedProp, TimeConversion::toISO8601( QDateTime::currentDateTime() ) ); + safeAddPropValue( vcard, VCUniqueStringProp, QString::number(c.uid()) ); + + // full name + safeAddPropValue( vcard, VCFullNameProp, c.fullName() ); + + // name properties + VObject *name = safeAddProp( vcard, VCNameProp ); + safeAddPropValue( name, VCFamilyNameProp, c.lastName() ); + safeAddPropValue( name, VCGivenNameProp, c.firstName() ); + safeAddPropValue( name, VCAdditionalNamesProp, c.middleName() ); + safeAddPropValue( name, VCNamePrefixesProp, c.title() ); + safeAddPropValue( name, VCNameSuffixesProp, c.suffix() ); + + // home properties + VObject *home_adr= safeAddProp( vcard, VCAdrProp ); + safeAddProp( home_adr, VCHomeProp ); + safeAddPropValue( home_adr, VCStreetAddressProp, c.homeStreet() ); + safeAddPropValue( home_adr, VCCityProp, c.homeCity() ); + safeAddPropValue( home_adr, VCRegionProp, c.homeState() ); + safeAddPropValue( home_adr, VCPostalCodeProp, c.homeZip() ); + safeAddPropValue( home_adr, VCCountryNameProp, c.homeCountry() ); + + VObject *home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homePhone() ); + safeAddProp( home_phone, VCHomeProp ); + home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homeMobile() ); + safeAddProp( home_phone, VCHomeProp ); + safeAddProp( home_phone, VCCellularProp ); + home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homeFax() ); + safeAddProp( home_phone, VCHomeProp ); + safeAddProp( home_phone, VCFaxProp ); + + VObject *url = safeAddPropValue( vcard, VCURLProp, c.homeWebpage() ); + safeAddProp( url, VCHomeProp ); + + // work properties + VObject *work_adr= safeAddProp( vcard, VCAdrProp ); + safeAddProp( work_adr, VCWorkProp ); + safeAddPropValue( work_adr, VCStreetAddressProp, c.businessStreet() ); + safeAddPropValue( work_adr, VCCityProp, c.businessCity() ); + safeAddPropValue( work_adr, VCRegionProp, c.businessState() ); + safeAddPropValue( work_adr, VCPostalCodeProp, c.businessZip() ); + safeAddPropValue( work_adr, VCCountryNameProp, c.businessCountry() ); + + VObject *work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessPhone() ); + safeAddProp( work_phone, VCWorkProp ); + work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessMobile() ); + safeAddProp( work_phone, VCWorkProp ); + safeAddProp( work_phone, VCCellularProp ); + work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessFax() ); + safeAddProp( work_phone, VCWorkProp ); + safeAddProp( work_phone, VCFaxProp ); + work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessPager() ); + safeAddProp( work_phone, VCWorkProp ); + safeAddProp( work_phone, VCPagerProp ); + + url = safeAddPropValue( vcard, VCURLProp, c.businessWebpage() ); + safeAddProp( url, VCWorkProp ); + + VObject *title = safeAddPropValue( vcard, VCTitleProp, c.jobTitle() ); + safeAddProp( title, VCWorkProp ); + + + QStringList emails = c.emailList(); + emails.prepend( c.defaultEmail() ); + for( QStringList::Iterator it = emails.begin(); it != emails.end(); ++it ) { + VObject *email = safeAddPropValue( vcard, VCEmailAddressProp, *it ); + safeAddProp( email, VCInternetProp ); + } + + safeAddPropValue( vcard, VCNoteProp, c.notes() ); + + safeAddPropValue( vcard, VCBirthDateProp, c.birthday() ); + + if ( !c.company().isEmpty() || !c.department().isEmpty() || !c.office().isEmpty() ) { + VObject *org = safeAddProp( vcard, VCOrgProp ); + safeAddPropValue( org, VCOrgNameProp, c.company() ); + safeAddPropValue( org, VCOrgUnitProp, c.department() ); + safeAddPropValue( org, VCOrgUnit2Prop, c.office() ); + } + + // some values we have to export as custom fields + safeAddPropValue( vcard, "X-Qtopia-Profession", c.profession() ); + safeAddPropValue( vcard, "X-Qtopia-Manager", c.manager() ); + safeAddPropValue( vcard, "X-Qtopia-Assistant", c.assistant() ); + + safeAddPropValue( vcard, "X-Qtopia-Spouse", c.spouse() ); + safeAddPropValue( vcard, "X-Qtopia-Gender", c.gender() ); + safeAddPropValue( vcard, "X-Qtopia-Anniversary", c.anniversary() ); + safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() ); + safeAddPropValue( vcard, "X-Qtopia-Children", c.children() ); + + return vcard; +} + + +static Contact parseVObject( VObject *obj ) +{ + Contact c; + + bool haveDefaultEmail = FALSE; + + VObjectIterator it; + initPropIterator( &it, obj ); + while( moreIteration( &it ) ) { + VObject *o = nextVObject( &it ); + QCString name = vObjectName( o ); + QCString value = vObjectStringZValue( o ); + if ( name == VCNameProp ) { + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + if ( name == VCNamePrefixesProp ) + c.setTitle( value ); + else if ( name == VCNameSuffixesProp ) + c.setSuffix( value ); + else if ( name == VCFamilyNameProp ) + c.setLastName( value ); + else if ( name == VCGivenNameProp ) + c.setFirstName( value ); + else if ( name == VCAdditionalNamesProp ) + c.setMiddleName( value ); + } + } + else if ( name == VCAdrProp ) { + bool work = TRUE; // default address is work address + QString street; + QString city; + QString region; + QString postal; + QString country; + + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + if ( name == VCHomeProp ) + work = FALSE; + else if ( name == VCWorkProp ) + work = TRUE; + else if ( name == VCStreetAddressProp ) + street = value; + else if ( name == VCCityProp ) + city = value; + else if ( name == VCRegionProp ) + region = value; + else if ( name == VCPostalCodeProp ) + postal = value; + else if ( name == VCCountryNameProp ) + country = value; + } + if ( work ) { + c.setBusinessStreet( street ); + c.setBusinessCity( city ); + c.setBusinessCountry( country ); + c.setBusinessZip( postal ); + c.setBusinessState( region ); + } else { + c.setHomeStreet( street ); + c.setHomeCity( city ); + c.setHomeCountry( country ); + c.setHomeZip( postal ); + c.setHomeState( region ); + } + } + else if ( name == VCTelephoneProp ) { + enum { + HOME = 0x01, + WORK = 0x02, + VOICE = 0x04, + CELL = 0x08, + FAX = 0x10, + PAGER = 0x20, + UNKNOWN = 0x80 + }; + int type = 0; + + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + if ( name == VCHomeProp ) + type |= HOME; + else if ( name == VCWorkProp ) + type |= WORK; + else if ( name == VCVoiceProp ) + type |= VOICE; + else if ( name == VCCellularProp ) + type |= CELL; + else if ( name == VCFaxProp ) + type |= FAX; + else if ( name == VCPagerProp ) + type |= PAGER; + else if ( name == VCPreferredProp ) + ; + else + type |= UNKNOWN; + } + if ( (type & UNKNOWN) != UNKNOWN ) { + if ( ( type & (HOME|WORK) ) == 0 ) // default + type |= HOME; + if ( ( type & (VOICE|CELL|FAX|PAGER) ) == 0 ) // default + type |= VOICE; + + if ( (type & (VOICE|HOME) ) == (VOICE|HOME) ) + c.setHomePhone( value ); + if ( ( type & (FAX|HOME) ) == (FAX|HOME) ) + c.setHomeFax( value ); + if ( ( type & (CELL|HOME) ) == (CELL|HOME) ) + c.setHomeMobile( value ); + if ( ( type & (VOICE|WORK) ) == (VOICE|WORK) ) + c.setBusinessPhone( value ); + if ( ( type & (FAX|WORK) ) == (FAX|WORK) ) + c.setBusinessFax( value ); + if ( ( type & (CELL|WORK) ) == (CELL|WORK) ) + c.setBusinessMobile( value ); + if ( ( type & (PAGER|WORK) ) == (PAGER|WORK) ) + c.setBusinessPager( value ); + } + } + else if ( name == VCEmailAddressProp ) { + QString email = vObjectStringZValue( o ); + bool valid = TRUE; + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + if ( name != VCInternetProp && name != VCHomeProp && + name != VCWorkProp && + name != VCPreferredProp ) + // ### preffered should map to default email + valid = FALSE; + } + if ( valid ) { + if ( haveDefaultEmail ) { + QString str = c.emails(); + if ( !str.isEmpty() ) + str += ","+email; + c.setEmails( str ); + } else { + c.setDefaultEmail( email ); + } + } + } + else if ( name == VCURLProp ) { + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + if ( name == VCHomeProp ) + c.setHomeWebpage( value ); + else if ( name == VCWorkProp ) + c.setBusinessWebpage( value ); + } + } + else if ( name == VCOrgProp ) { + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + if ( name == VCOrgNameProp ) + c.setCompany( value ); + else if ( name == VCOrgUnitProp ) + c.setDepartment( value ); + else if ( name == VCOrgUnit2Prop ) + c.setOffice( value ); + } + } + else if ( name == VCTitleProp ) { + c.setJobTitle( value ); + } + else if ( name == "X-Qtopia-Profession" ) { + c.setProfession( value ); + } + else if ( name == "X-Qtopia-Manager" ) { + c.setManager( value ); + } + else if ( name == "X-Qtopia-Assistant" ) { + c.setAssistant( value ); + } + else if ( name == "X-Qtopia-Spouse" ) { + c.setSpouse( value ); + } + else if ( name == "X-Qtopia-Gender" ) { + c.setGender( value ); + } + else if ( name == "X-Qtopia-Anniversary" ) { + c.setAnniversary( value ); + } + else if ( name == "X-Qtopia-Nickname" ) { + c.setNickname( value ); + } + else if ( name == "X-Qtopia-Children" ) { + c.setChildren( value ); + } + + +#if 0 + else { + printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) ); + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + printf(" subprop: %s = %s\n", name.data(), value.latin1() ); + } + } +#endif + } + c.setFileAs(); + return c; +} + +void Contact::writeVCard( const QString &filename, const QValueList &contacts) +{ + QFileDirect f( filename.utf8().data() ); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vcard write"); + return; + } + + QValueList::ConstIterator it; + for( it = contacts.begin(); it != contacts.end(); ++it ) { + VObject *obj = createVObject( *it ); + writeVObject(f.directHandle() , obj ); + cleanVObject( obj ); + } + cleanStrTbl(); +} + +void Contact::writeVCard( const QString &filename, const Contact &contact) +{ + QFileDirect f( filename.utf8().data() ); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vcard write"); + return; + } + + VObject *obj = createVObject( contact ); + writeVObject( f.directHandle() , obj ); + cleanVObject( obj ); + + cleanStrTbl(); +} + + +QValueList Contact::readVCard( const QString &filename ) +{ + qDebug("trying to open %s, exists=%d", filename.utf8().data(), QFileInfo( filename.utf8().data() ).size() ); + VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() ); + + qDebug("vobject = %p", obj ); + + QValueList contacts; + + while ( obj ) { + contacts.append( parseVObject( obj ) ); + + VObject *t = obj; + obj = nextVObjectInList(obj); + cleanVObject( t ); + } + + return contacts; +} + +bool Contact::match( const QRegExp &r ) const +{ + bool match; + match = false; + QMap::ConstIterator it; + for ( it = mMap.begin(); it != mMap.end(); ++it ) { + if ( (*it).find( r ) > -1 ) { + match = true; + break; + } + } + return match; +} diff --git a/library/backend/contact.h b/library/backend/contact.h new file mode 100644 index 0000000..6abdab6 --- a/dev/null +++ b/library/backend/contact.h @@ -0,0 +1,217 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef __CONTACT_H__ +#define __CONTACT_H__ + +#include +#include + +#include + +#if defined(QPC_TEMPLATEDLL) +// MOC_SKIP_BEGIN +template class QPC_EXPORT QMap; +// MOC_SKIP_END +#endif + +class ContactPrivate; +class QPC_EXPORT Contact : public Qtopia::Record +{ + friend class DataSet; +public: + Contact(); + Contact( const QMap &fromMap ); + virtual ~Contact(); + + static void writeVCard( const QString &filename, const QValueList &contacts); + static void writeVCard( const QString &filename, const Contact &c ); + static QValueList readVCard( const QString &filename ); + + enum journal_action { ACTION_ADD, ACTION_REMOVE, ACTION_REPLACE }; + + void setTitle( const QString &v ) { replace( Qtopia::Title, v ); } + void setFirstName( const QString &v ) { replace( Qtopia::FirstName, v ); } + void setMiddleName( const QString &v ) { replace( Qtopia::MiddleName, v ); } + void setLastName( const QString &v ) { replace( Qtopia::LastName, v ); } + void setSuffix( const QString &v ) { replace( Qtopia::Suffix, v ); } + void setFileAs( const QString &v ) { replace( Qtopia::FileAs, v ); } + void setFileAs(); + + // default email address + void setDefaultEmail( const QString &v ) { replace( Qtopia::DefaultEmail, v ); } + // the emails should be seperated by a semicolon + void setEmails( const QString &v ); + + // home + void setHomeStreet( const QString &v ) { replace( Qtopia::HomeStreet, v ); } + void setHomeCity( const QString &v ) { replace( Qtopia::HomeCity, v ); } + void setHomeState( const QString &v ) { replace( Qtopia::HomeState, v ); } + void setHomeZip( const QString &v ) { replace( Qtopia::HomeZip, v ); } + void setHomeCountry( const QString &v ) { replace( Qtopia::HomeCountry, v ); } + void setHomePhone( const QString &v ) { replace( Qtopia::HomePhone, v ); } + void setHomeFax( const QString &v ) { replace( Qtopia::HomeFax, v ); } + void setHomeMobile( const QString &v ) { replace( Qtopia::HomeMobile, v ); } + void setHomeWebpage( const QString &v ) { replace( Qtopia::HomeWebPage, v ); } + + // business + void setCompany( const QString &v ) { replace( Qtopia::Company, v ); } + void setBusinessStreet( const QString &v ) { replace( Qtopia::BusinessStreet, v ); } + void setBusinessCity( const QString &v ) { replace( Qtopia::BusinessCity, v ); } + void setBusinessState( const QString &v ) { replace( Qtopia::BusinessState, v ); } + void setBusinessZip( const QString &v ) { replace( Qtopia::BusinessZip, v ); } + void setBusinessCountry( const QString &v ) { replace( Qtopia::BusinessCountry, v ); } + void setBusinessWebpage( const QString &v ) { replace( Qtopia::BusinessWebPage, v ); } + void setJobTitle( const QString &v ) { replace( Qtopia::JobTitle, v ); } + void setDepartment( const QString &v ) { replace( Qtopia::Department, v ); } + void setOffice( const QString &v ) { replace( Qtopia::Office, v ); } + void setBusinessPhone( const QString &v ) { replace( Qtopia::BusinessPhone, v ); } + void setBusinessFax( const QString &v ) { replace( Qtopia::BusinessFax, v ); } + void setBusinessMobile( const QString &v ) { replace( Qtopia::BusinessMobile, v ); } + void setBusinessPager( const QString &v ) { replace( Qtopia::BusinessPager, v ); } + void setProfession( const QString &v ) { replace( Qtopia::Profession, v ); } + void setAssistant( const QString &v ) { replace( Qtopia::Assistant, v ); } + void setManager( const QString &v ) { replace( Qtopia::Manager, v ); } + + // personal + void setSpouse( const QString &v ) { replace( Qtopia::Spouse, v ); } + void setGender( const QString &v ) { replace( Qtopia::Gender, v ); } + void setBirthday( const QString &v ) { replace( Qtopia::Birthday, v ); } + void setAnniversary( const QString &v ) { replace( Qtopia::Anniversary, v ); } + void setNickname( const QString &v ) { replace( Qtopia::Nickname, v ); } + void setChildren( const QString &v ); + + // other + void setNotes( const QString &v ) { replace( Qtopia::Notes, v); } + + bool match( const QRegExp &r ) const; + +// // custom +// void setCustomField( const QString &key, const QString &v ) +// { replace(Custom- + key, v ); } + + // name + QString fullName() const; + QString title() const { return find( Qtopia::Title ); } + QString firstName() const { return find( Qtopia::FirstName ); } + QString middleName() const { return find( Qtopia::MiddleName ); } + QString lastName() const { return find( Qtopia::LastName ); } + QString suffix() const { return find( Qtopia::Suffix ); } + QString fileAs() const { return find( Qtopia::FileAs ); } + + // email + QString defaultEmail() const { return find( Qtopia::DefaultEmail ); } + QString emails() const { return find( Qtopia::Emails ); } + QStringList emailList() const; + + // home + QString homeStreet() const { return find( Qtopia::HomeStreet ); } + QString homeCity() const { return find( Qtopia::HomeCity ); } + QString homeState() const { return find( Qtopia::HomeState ); } + QString homeZip() const { return find( Qtopia::HomeZip ); } + QString homeCountry() const { return find( Qtopia::HomeCountry ); } + QString homePhone() const { return find( Qtopia::HomePhone ); } + QString homeFax() const { return find( Qtopia::HomeFax ); } + QString homeMobile() const { return find( Qtopia::HomeMobile ); } + QString homeWebpage() const { return find( Qtopia::HomeWebPage ); } + /** Multi line string containing all non-empty address info in the form + * Street + * City, State Zip + * Country + */ + QString displayHomeAddress() const; + + // business + QString company() const { return find( Qtopia::Company ); } + QString businessStreet() const { return find( Qtopia::BusinessStreet ); } + QString businessCity() const { return find( Qtopia::BusinessCity ); } + QString businessState() const { return find( Qtopia::BusinessState ); } + QString businessZip() const { return find( Qtopia::BusinessZip ); } + QString businessCountry() const { return find( Qtopia::BusinessCountry ); } + QString businessWebpage() const { return find( Qtopia::BusinessWebPage ); } + QString jobTitle() const { return find( Qtopia::JobTitle ); } + QString department() const { return find( Qtopia::Department ); } + QString office() const { return find( Qtopia::Office ); } + QString businessPhone() const { return find( Qtopia::BusinessPhone ); } + QString businessFax() const { return find( Qtopia::BusinessFax ); } + QString businessMobile() const { return find( Qtopia::BusinessMobile ); } + QString businessPager() const { return find( Qtopia::BusinessPager ); } + QString profession() const { return find( Qtopia::Profession ); } + QString assistant() const { return find( Qtopia::Assistant ); } + QString manager() const { return find( Qtopia::Manager ); } + /** Multi line string containing all non-empty address info in the form + * Street + * City, State Zip + * Country + */ + QString displayBusinessAddress() const; + + //personal + QString spouse() const { return find( Qtopia::Spouse ); } + QString gender() const { return find( Qtopia::Gender ); } + QString birthday() const { return find( Qtopia::Birthday ); } + QString anniversary() const { return find( Qtopia::Anniversary ); } + QString nickname() const { return find( Qtopia::Nickname ); } + QString children() const { return find( Qtopia::Children ); } + QStringList childrenList() const; + + // other + QString notes() const { return find( Qtopia::Notes ); } + QString groups() const { return find( Qtopia::Groups ); } + QStringList groupList() const; + +// // custom +// const QString &customField( const QString &key ) +// { return find( Custom- + key ); } + + static QStringList fields(); + static QStringList trfields(); + + QString toRichText() const; + QMap toMap() const; + QString field( int key ) const { return find( key ); } + + + // journaling... + void saveJournal( journal_action action, const QString &key = QString::null ); + void save( QString &buf ) const; + + void setUid( int i ) +{ Record::setUid(i); replace( Qtopia::AddressUid , QString::number(i)); } + +private: + friend class AbTable; + void insert( int key, const QString &value ); + void replace( int key, const QString &value ); + QString find( int key ) const; + + QString displayAddress( const QString &street, + const QString &city, + const QString &state, + const QString &zip, + const QString &country ) const; + + Qtopia::UidGen &uidGen() { return sUidGen; } + static Qtopia::UidGen sUidGen; + QMap mMap; + ContactPrivate *d; +}; + +#endif diff --git a/library/backend/event.cpp b/library/backend/event.cpp new file mode 100644 index 0000000..50a663d --- a/dev/null +++ b/library/backend/event.cpp @@ -0,0 +1,830 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "event.h" +#include "qfiledirect_p.h" +#include +#include +#include +#include +#include +#include "vobject_p.h" + +#include + +using namespace Qtopia; + +static void write( QString& buf, const Event::RepeatPattern &r ) +{ + buf += " rtype=\""; + switch ( r.type ) { + case Event::Daily: + buf += "Daily"; + break; + case Event::Weekly: + buf += "Weekly"; + break; + case Event::MonthlyDay: + buf += "MonthlyDay"; + break; + case Event::MonthlyDate: + buf += "MonthlyDate"; + break; + case Event::Yearly: + buf += "Yearly"; + break; + default: + buf += "NoRepeat"; + break; + } + buf += "\""; + if ( r.days > 0 ) + buf += " rweekdays=\"" + QString::number( static_cast( r.days ) ) + "\""; + if ( r.position != 0 ) + buf += " rposition=\"" + QString::number( r.position ) + "\""; + + buf += " rfreq=\"" + QString::number( r.frequency ) + "\""; + buf += " rhasenddate=\"" + QString::number( static_cast( r.hasEndDate ) ) + "\""; + if ( r.hasEndDate ) + buf += " enddt=\"" + + QString::number( r.endDateUTC ? r.endDateUTC : time( 0 ) ) + + "\""; + buf += " created=\"" + QString::number( r.createTime ) + "\""; +} + +Qtopia::UidGen Event::sUidGen( Qtopia::UidGen::Qtopia ); + +Event::Event() : Record() +{ + startUTC = endUTC = time( 0 ); + typ = Normal; + hAlarm = FALSE; + hRepeat = FALSE; + aMinutes = 0; + aSound = Silent; + pattern.type = NoRepeat; + pattern.frequency = -1; +} + +Event::Event( const QMap &map ) +{ + setDescription( map[DatebookDescription] ); + setLocation( map[Location] ); + setCategories( idsFromString( map[DatebookCategory] ) ); + setTimeZone( map[TimeZone] ); + setNotes( map[Note] ); + setStart( TimeConversion::fromUTC( map[StartDateTime].toUInt() ) ); + setEnd( TimeConversion::fromUTC( map[EndDateTime].toUInt() ) ); + setType( (Event::Type) map[DatebookType].toInt() ); + setAlarm( ( map[HasAlarm] == "1" ? TRUE : FALSE ), map[AlarmTime].toInt(), (Event::SoundTypeChoice)map[SoundType].toInt() ); + Event::RepeatPattern p; + p.type = (Event::RepeatType) map[ RepeatPatternType ].toInt(); + p.frequency = map[ RepeatPatternFrequency ].toInt(); + p.position = map[ RepeatPatternPosition ].toInt(); + p.days = map[ RepeatPatternDays ].toInt(); + p.hasEndDate = map[ RepeatPatternHasEndDate ].toInt(); + p.endDateUTC = map[ RepeatPatternEndDate ].toUInt(); + setRepeat( p ); + + setUid( map[ DatebookUid ].toInt() ); +} + +Event::~Event() +{ +} + +int Event::week( const QDate& date ) +{ + // Calculates the week this date is in within that + // month. Equals the "row" is is in in the month view + int week = 1; + QDate tmp( date.year(), date.month(), 1 ); + + if ( date.dayOfWeek() < tmp.dayOfWeek() ) + ++week; + + week += ( date.day() - 1 ) / 7; + return week; +} + +int Event::occurrence( const QDate& date ) +{ + // calculates the number of occurrances of this day of the + // week till the given date (e.g 3rd Wednesday of the month) + return ( date.day() - 1 ) / 7 + 1; +} + +int Event::dayOfWeek( char day ) +{ + int dayOfWeek = 1; + char i = Event::MON; + while ( !( i & day ) && i <= Event::SUN ) { + i <<= 1; + ++dayOfWeek; + } + return dayOfWeek; +} + +int Event::monthDiff( const QDate& first, const QDate& second ) +{ + return ( second.year() - first.year() ) * 12 + + second.month() - first.month(); +} + +QMap Event::toMap() const +{ + QMap m; + m.insert( DatebookDescription, description() ); + m.insert ( Location, location() ); + m.insert ( DatebookCategory, idsToString( categories() ) ); + m.insert ( TimeZone, timeZone() ); + m.insert ( Note, notes() ); + m.insert ( StartDateTime, QString::number( TimeConversion::toUTC( start() ) ) ); + m.insert ( EndDateTime, QString::number( TimeConversion::toUTC( end() ) ) ); + m.insert ( DatebookType, QString::number( (int)type() ) ); + m.insert ( HasAlarm, ( hasAlarm() ? "1" : "0" ) ); + m.insert ( SoundType, QString::number( (int)alarmSound() ) ); + m.insert ( AlarmTime, QString::number( alarmTime() ) ); + m.insert ( RepeatPatternType, QString::number( static_cast( repeatPattern().type ) ) ); + m.insert ( RepeatPatternFrequency, QString::number( repeatPattern().frequency ) ); + m.insert ( RepeatPatternPosition, QString::number( repeatPattern().position ) ); + m.insert ( RepeatPatternDays, QString::number( repeatPattern().days ) ); + m.insert ( RepeatPatternHasEndDate, QString::number( static_cast( repeatPattern().hasEndDate ) ) ); + m.insert ( RepeatPatternEndDate, QString::number( repeatPattern().endDateUTC ) ); + + m.insert( DatebookUid, QString::number( uid()) ); + + return m; +} + +void Event::setRepeat( const RepeatPattern &p ) +{ + setRepeat( p.type != NoRepeat, p ); +} + +void Event::setDescription( const QString &s ) +{ + descript = s; +} + +void Event::setLocation( const QString &s ) +{ + locat = s; +} + +// void Event::setCategory( const QString &s ) +// { +// categ = s; +// } + +void Event::setType( Type t ) +{ + typ = t; +} + +void Event::setStart( const QDateTime &d ) +{ + startUTC = TimeConversion::toUTC( d ); +} + +void Event::setStart( time_t time ) +{ + startUTC = time; +} + +void Event::setEnd( const QDateTime &d ) +{ + endUTC = TimeConversion::toUTC( d ); +} + +void Event::setEnd( time_t time ) +{ + endUTC = time; +} + +void Event::setTimeZone( const QString &z ) +{ + tz = z; +} + +void Event::setAlarm( bool b, int minutes, SoundTypeChoice s ) +{ + hAlarm = b; + aMinutes = minutes; + aSound = s; +} + +void Event::setRepeat( bool b, const RepeatPattern &p ) +{ + hRepeat = b; + pattern = p; +} + +void Event::setNotes( const QString &n ) +{ + note = n; +} + +const QString &Event::description() const +{ + return descript; +} + +const QString &Event::location() const +{ + return locat; +} + +// QString Event::category() const +// { +// return categ; +// } + +Event::Type Event::type() const +{ + return typ; +} + +QDateTime Event::start( bool actual ) const +{ + QDateTime dt = (startUTC > 0) ? TimeConversion::fromUTC( startUTC ) : QDateTime::currentDateTime(); + + if ( actual && typ == AllDay ) { + QTime t = dt.time(); + t.setHMS( 0, 0, 0 ); + dt.setTime( t ); + } + return dt; +} + +QDateTime Event::end( bool actual ) const +{ + QDateTime dt = (endUTC > 0) ? TimeConversion::fromUTC( endUTC ) : QDateTime::currentDateTime(); + + if ( actual && typ == AllDay ) { + QTime t = dt.time(); + t.setHMS( 23, 59, 59 ); + dt.setTime( t ); + } + return dt; +} + +const QString &Event::timeZone() const +{ + return tz; +} + +bool Event::hasAlarm() const +{ + return hAlarm; +} + +int Event::alarmTime() const +{ + return aMinutes; +} + +Event::SoundTypeChoice Event::alarmSound() const +{ + return aSound; +} + +bool Event::hasRepeat() const +{ + return doRepeat(); +} + +const Event::RepeatPattern &Event::repeatPattern() const +{ + return pattern; +} + +Event::RepeatPattern &Event::repeatPattern() +{ + return pattern; +} + +const QString &Event::notes() const +{ + return note; +} + +bool Event::operator==( const Event &e ) const +{ + return ( e.descript == descript && + e.locat == locat && + e.categ == categ && + e.typ == typ && + e.startUTC == startUTC && + e.endUTC == endUTC && + e.tz == tz && + e.hAlarm == hAlarm && + e.aMinutes == aMinutes && + e.aSound == aSound && + e.hRepeat == hRepeat && + e.pattern == pattern && + e.note == note ); +} + +void Event::save( QString& buf ) +{ + buf += " description=\"" + Qtopia::escapeString(descript) + "\""; + if ( !locat.isEmpty() ) + buf += " location=\"" + Qtopia::escapeString(locat) + "\""; + // save the categoies differently.... + QString strCats = idsToString( categories() ); + buf += " categories=\"" + Qtopia::escapeString(strCats) + "\""; + buf += " uid=\"" + QString::number( uid() ) + "\""; + if ( (Type)typ != Normal ) + buf += " type=\"AllDay\""; + if ( hAlarm ) { + buf += " alarm=\"" + QString::number( aMinutes ) + "\" sound=\""; + if ( aSound == Event::Loud ) + buf += "loud"; + else + buf += "silent"; + buf += "\""; + } + if ( hRepeat ) + write( buf, pattern ); + + buf += " start=\"" + + QString::number( startUTC ) + + "\""; + + buf += " end=\"" + + QString::number( endUTC ) + + "\""; + + if ( !note.isEmpty() ) + buf += " note=\"" + Qtopia::escapeString( note ) + "\""; + buf += customToXml(); +} + +bool Event::RepeatPattern::operator==( const Event::RepeatPattern &right ) const +{ + // *sigh* + return ( type == right.type + && frequency == right.frequency + && position == right.position + && days == right.days + && hasEndDate == right.hasEndDate + && endDateUTC == right.endDateUTC + && createTime == right.createTime ); +} + + +class EffectiveEventPrivate +{ +public: + //currently the existence of the d pointer means multi-day repeating, + //msut be changed if we use the d pointer for anything else. + QDate startDate; + QDate endDate; +}; + + +EffectiveEvent::EffectiveEvent() +{ + mDate = QDate::currentDate(); + mStart = mEnd = QTime::currentTime(); + d = 0; +} + +EffectiveEvent::EffectiveEvent( const Event &e, const QDate &date, Position pos ) +{ + mEvent = e; + mDate = date; + if ( pos & Start ) + mStart = e.start( TRUE ).time(); + else + mStart = QTime( 0, 0, 0 ); + + if ( pos & End ) + mEnd = e.end( TRUE ).time(); + else + mEnd = QTime( 23, 59, 59 ); + d = 0; +} + +EffectiveEvent::~EffectiveEvent() +{ + delete d; +} + +EffectiveEvent::EffectiveEvent( const EffectiveEvent &e ) +{ + d = 0; + *this = e; +} + +EffectiveEvent& EffectiveEvent::operator=( const EffectiveEvent & e ) +{ + if ( &e == this ) + return *this; + delete d; + if ( e.d ) { + d = new EffectiveEventPrivate; + d->startDate = e.d->startDate; + d->endDate = e.d->endDate; + } else { + d = 0; + } + mEvent = e.mEvent; + mDate = e.mDate; + mStart = e.mStart; + mEnd = e.mEnd; + + return *this; + +} + +// QString EffectiveEvent::category() const +// { +// return mEvent.category(); +// } + +const QString &EffectiveEvent::description( ) const +{ + return mEvent.description(); +} + +const QString &EffectiveEvent::location( ) const +{ + return mEvent.location(); +} + +const QString &EffectiveEvent::notes() const +{ + return mEvent.notes(); +} + +const Event &EffectiveEvent::event() const +{ + return mEvent; +} + +const QTime &EffectiveEvent::end() const +{ + return mEnd; +} + +const QTime &EffectiveEvent::start() const +{ + return mStart; +} + +const QDate &EffectiveEvent::date() const +{ + return mDate; +} + +int EffectiveEvent::length() const +{ + return (mEnd.hour() * 60 - mStart.hour() * 60) + + QABS(mStart.minute() - mEnd.minute() ); +} + +void EffectiveEvent::setDate( const QDate &dt ) +{ + mDate = dt; +} + +void EffectiveEvent::setStart( const QTime &start ) +{ + mStart = start; +} + +void EffectiveEvent::setEnd( const QTime &end ) +{ + mEnd = end; +} + +void EffectiveEvent::setEvent( Event e ) +{ + mEvent = e; +} + +bool EffectiveEvent::operator<( const EffectiveEvent &e ) const +{ + if ( mDate < e.date() ) + return TRUE; + if ( mDate == e.date() ) + return ( mStart < e.start() ); + else + return FALSE; +} + +bool EffectiveEvent::operator<=( const EffectiveEvent &e ) const +{ + return (mDate <= e.date() ); +} + +bool EffectiveEvent::operator==( const EffectiveEvent &e ) const +{ + return ( mDate == e.date() + && mStart == e.start() + && mEnd == e.end() + && mEvent == e.event() ); +} + +bool EffectiveEvent::operator!=( const EffectiveEvent &e ) const +{ + return !(*this == e); +} + +bool EffectiveEvent::operator>( const EffectiveEvent &e ) const +{ + return !(*this <= e ); +} + +bool EffectiveEvent::operator>=(const EffectiveEvent &e) const +{ + return !(*this < e); +} + +void EffectiveEvent::setEffectiveDates( const QDate &from, const QDate &to ) +{ + if ( !from.isValid() ) { + delete d; + d = 0; + return; + } + if ( !d ) + d = new EffectiveEventPrivate; + d->startDate = from; + d->endDate = to; +} + +QDate EffectiveEvent::startDate() const +{ + if ( d ) + return d->startDate; + else if ( mEvent.hasRepeat() ) + return mDate; // single day, since multi-day should have a d pointer + else + return mEvent.start().date(); +} + +QDate EffectiveEvent::endDate() const +{ + if ( d ) + return d->endDate; + else if ( mEvent.hasRepeat() ) + return mDate; // single day, since multi-day should have a d pointer + else + return mEvent.end().date(); +} + +int EffectiveEvent::size() const +{ + return ( mEnd.hour() - mStart.hour() ) * 3600 + + (mEnd.minute() - mStart.minute() * 60 + + mEnd.second() - mStart.second() ); +} + + +// vcal conversion code +static inline VObject *safeAddPropValue( VObject *o, const char *prop, const QString &value ) +{ + VObject *ret = 0; + if ( o && !value.isEmpty() ) + ret = addPropValue( o, prop, value.latin1() ); + return ret; +} + +static inline VObject *safeAddProp( VObject *o, const char *prop) +{ + VObject *ret = 0; + if ( o ) + ret = addProp( o, prop ); + return ret; +} + +static VObject *createVObject( const Event &e ) +{ + VObject *vcal = newVObject( VCCalProp ); + safeAddPropValue( vcal, VCVersionProp, "1.0" ); + VObject *event = safeAddProp( vcal, VCEventProp ); + + safeAddPropValue( event, VCDTstartProp, TimeConversion::toISO8601( e.start() ) ); + safeAddPropValue( event, VCDTendProp, TimeConversion::toISO8601( e.end() ) ); + safeAddPropValue( event, "X-Qtopia-NOTES", e.description() ); + safeAddPropValue( event, VCDescriptionProp, e.description() ); + safeAddPropValue( event, VCLocationProp, e.location() ); + + if ( e.hasAlarm() ) { + VObject *alarm = safeAddProp( event, VCAAlarmProp ); + QDateTime dt = e.start(); + dt = dt.addSecs( -e.alarmTime()*60 ); + safeAddPropValue( alarm, VCRunTimeProp, TimeConversion::toISO8601( dt ) ); + safeAddPropValue( alarm, VCAudioContentProp, + (e.alarmSound() == Event::Silent ? "silent" : "alarm" ) ); + } + + safeAddPropValue( event, "X-Qtopia-TIMEZONE", e.timeZone() ); + + if ( e.type() == Event::AllDay ) + safeAddPropValue( event, "X-Qtopia-AllDay", e.timeZone() ); + + // ### repeat missing + + // ### categories missing + + return vcal; +} + + +static Event parseVObject( VObject *obj ) +{ + Event e; + + bool haveAlarm = FALSE; + bool haveStart = FALSE; + bool haveEnd = FALSE; + QDateTime alarmTime; + Event::SoundTypeChoice soundType = Event::Silent; + + VObjectIterator it; + initPropIterator( &it, obj ); + while( moreIteration( &it ) ) { + VObject *o = nextVObject( &it ); + QCString name = vObjectName( o ); + QCString value = vObjectStringZValue( o ); + if ( name == VCDTstartProp ) { + e.setStart( TimeConversion::fromISO8601( value ) ); + haveStart = TRUE; + } + else if ( name == VCDTendProp ) { + e.setEnd( TimeConversion::fromISO8601( value ) ); + haveEnd = TRUE; + } + else if ( name == "X-Qtopia-NOTES" ) { + e.setNotes( value ); + } + else if ( name == VCDescriptionProp ) { + e.setDescription( value ); + } + else if ( name == VCLocationProp ) { + e.setLocation( value ); + } + else if ( name == VCAudioContentProp ) { + haveAlarm = TRUE; + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QCString value = vObjectStringZValue( o ); + if ( name == VCRunTimeProp ) + alarmTime = TimeConversion::fromISO8601( value ); + else if ( name == VCAudioContentProp ) { + if ( value == "silent" ) + soundType = Event::Silent; + else + soundType = Event::Loud; + } + } + } + else if ( name == "X-Qtopia-TIMEZONE") { + e.setTimeZone( value ); + } + else if ( name == "X-Qtopia-AllDay" ) { + e.setType( Event::AllDay ); + } +#if 0 + else { + printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) ); + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + printf(" subprop: %s = %s\n", name.data(), value.latin1() ); + } + } +#endif + } + + if ( !haveStart && !haveEnd ) + e.setStart( QDateTime::currentDateTime() ); + + if ( !haveEnd ) { + e.setType( Event::AllDay ); + e.setEnd( e.start() ); + } + + if ( haveAlarm ) { + int minutes = alarmTime.secsTo( e.start() ) / 60; + e.setAlarm( TRUE, minutes, soundType ); + } + return e; +} + + + +void Event::writeVCalendar( const QString &filename, const QValueList &events) +{ + QFileDirect f( filename.utf8().data() ); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vcard write"); + return; + } + + QValueList::ConstIterator it; + for( it = events.begin(); it != events.end(); ++it ) { + VObject *obj = createVObject( *it ); + writeVObject( f.directHandle() , obj ); + cleanVObject( obj ); + } + + cleanStrTbl(); +} + +void Event::writeVCalendar( const QString &filename, const Event &event) +{ + QFileDirect f( filename.utf8().data() ); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vcard write"); + return; + } + + VObject *obj = createVObject( event ); + writeVObject( f.directHandle() , obj ); + cleanVObject( obj ); + + cleanStrTbl(); +} + + +QValueList Event::readVCalendar( const QString &filename ) +{ + VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() ); + + QValueList events; + + while ( obj ) { + QCString name = vObjectName( obj ); + if ( name == VCCalProp ) { + VObjectIterator nit; + initPropIterator( &nit, obj ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + if ( name == VCEventProp ) + events.append( parseVObject( o ) ); + } + } else if ( name == VCEventProp ) { + // shouldn't happen, but just to be sure + events.append( parseVObject( obj ) ); + } + VObject *t = obj; + obj = nextVObjectInList(obj); + cleanVObject( t ); + } + + return events; +} + +bool Event::match( const QRegExp &r ) const +{ + bool returnMe; + returnMe = false; + + if ( descript.find( r ) > -1 ) + returnMe = true; + else if ( locat.find( r ) > -1 ) + returnMe = true; + else if ( TimeConversion::fromUTC( startUTC ).toString().find( r ) > -1 ) + returnMe = true; + else if ( TimeConversion::fromUTC( endUTC ).toString().find( r ) > -1 ) + returnMe = true; + else if ( tz.find( r ) > -1 ) + returnMe = true; + else if ( note.find( r ) > -1 ) + returnMe = true; + else if ( doRepeat() ) { + if ( pattern.hasEndDate ) + if ( TimeConversion::fromUTC( pattern.endDateUTC ).toString().find(r) > -1 ) + returnMe = true; + } + return returnMe; +} diff --git a/library/backend/event.h b/library/backend/event.h new file mode 100644 index 0000000..0ebe9ea --- a/dev/null +++ b/library/backend/event.h @@ -0,0 +1,229 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef __EVENT_H__ +#define __EVENT_H__ + +#include +#include + +#ifdef PALMTOPCENTER +#include +#endif +#include + +#include + +class EventPrivate; +class QPC_EXPORT Event : public Qtopia::Record +{ +public: + enum RepeatType { NoRepeat = -1, Daily, Weekly, MonthlyDay, + MonthlyDate, Yearly }; + enum Days { MON = 0x01, TUE = 0x02, WED = 0x04, THU = 0x08, + FRI = 0x10, SAT = 0x20, SUN = 0x40 }; + struct QPC_EXPORT RepeatPattern + { + RepeatPattern() { + type = NoRepeat; frequency = -1; days = 0; position = 0; createTime = -1; + hasEndDate = FALSE; endDateUTC = 0; } + bool operator ==( const RepeatPattern &right ) const; + + RepeatType type; + int frequency; + int position; // the posistion in the month (e.g. the first sunday, etc) positive, count from the front negative count from the end... + char days; // a mask for days OR in your days! + bool hasEndDate; + QDate endDate() const { return TimeConversion::fromUTC( endDateUTC ).date(); } + void setEndDate( const QDate &dt ) { endDateUTC = TimeConversion::toUTC( dt ); } + time_t endDateUTC; + time_t createTime; + }; + + Event(); + Event( const QMap & map ); + virtual ~Event(); + + QMap toMap() const; + + static void writeVCalendar( const QString &filename, const QValueList &events); + static void writeVCalendar( const QString &filename, const Event &event); + static QValueList readVCalendar( const QString &filename ); + + enum Type { Normal, AllDay }; + enum SoundTypeChoice { Silent, Loud }; + + bool operator<( const Event &e1) const { return start() < e1.start(); }; + bool operator<=( const Event &e1 ) const { return start() <= e1.start(); }; + bool operator!=( const Event &e1 ) const { return !( *this == e1 ); }; + bool operator>( const Event &e1 ) const { return start() > e1.start(); }; + bool operator>=(const Event &e1 ) const { return start() >= e1.start(); }; + bool operator==( const Event &e ) const; + + void setDescription( const QString &s ); + const QString &description() const; + + void setLocation( const QString &s ); + const QString &location() const; + + void setType( Type t ); + Type type() const; + void setStart( const QDateTime &d ); + void setStart( time_t time ); + QDateTime start( bool actual = FALSE ) const; + time_t startTime() const { return startUTC; } + void setEnd( const QDateTime &e ); + void setEnd( time_t time ); + QDateTime end( bool actual = FALSE ) const; + time_t endTime() const { return endUTC; } + void setTimeZone( const QString & ); + const QString &timeZone() const; + void setAlarm( bool b, int minutes, SoundTypeChoice ); + bool hasAlarm() const; + int alarmTime() const; + SoundTypeChoice alarmSound() const; + void setRepeat( bool b, const RepeatPattern &p ); + void setRepeat( const RepeatPattern &p ); + bool hasRepeat() const; + const RepeatPattern &repeatPattern() const; + RepeatPattern &repeatPattern(); + void setNotes( const QString &n ); + const QString ¬es() const; + bool doRepeat() const { return pattern.type != NoRepeat; } + + void save( QString& buf ); + //void load( Node *n ); + + // helper function to calculate the week of the given date + static int week( const QDate& date ); + // calculates the number of occurrences of the week day of + // the given date from the start of the month + static int occurrence( const QDate& date ); + // returns a proper days-char for a given dayOfWeek() + static char day( int dayOfWeek ) { return 1 << ( dayOfWeek - 1 ); } + // returns the dayOfWeek for the *first* day it finds (ignores + // any further days!). Returns 1 (Monday) if there isn't any day found + static int dayOfWeek( char day ); + // returns the difference of months from first to second. + static int monthDiff( const QDate& first, const QDate& second ); + bool match( const QRegExp &r ) const; + +private: + Qtopia::UidGen &uidGen() { return sUidGen; } + static Qtopia::UidGen sUidGen; + + QString descript, locat, categ; + Type typ : 4; + bool startTimeDirty : 1; + bool endTimeDirty : 1; + time_t startUTC, endUTC; + QString tz; + bool hAlarm, hRepeat; + int aMinutes; + SoundTypeChoice aSound; + RepeatPattern pattern; + QString note; + EventPrivate *d; +}; + +// Since an event spans multiple day, it is better to have this +// class to represent a day instead of creating many +// dummy events... + +class EffectiveEventPrivate; +class QPC_EXPORT EffectiveEvent +{ +public: + // If we calculate the effective event of a multi-day event + // we have to figure out whether we are at the first day, + // at the end, or anywhere else ("middle"). This is important + // for the start/end times (00:00/23:59) + // MidWay: 00:00 -> 23:59, as we are "in the middle" of a multi- + // day event + // Start: start time -> 23:59 + // End: 00:00 -> end time + // Start | End == StartEnd: for single-day events (default) + // here we draw start time -> end time + enum Position { MidWay = 0, Start = 1, End = 2, StartEnd = 3 }; + + EffectiveEvent(); + EffectiveEvent( const Event &event, const QDate &startDate, Position pos = StartEnd ); + EffectiveEvent( const EffectiveEvent & ); + EffectiveEvent& operator=( const EffectiveEvent & ); + ~EffectiveEvent(); + + + bool operator<( const EffectiveEvent &e ) const; + bool operator<=( const EffectiveEvent &e ) const; + bool operator==( const EffectiveEvent &e ) const; + bool operator!=( const EffectiveEvent &e ) const; + bool operator>( const EffectiveEvent &e ) const; + bool operator>= ( const EffectiveEvent &e ) const; + + void setStart( const QTime &start ); + void setEnd( const QTime &end ); + void setEvent( Event e ); + void setDate( const QDate &date ); + void setEffectiveDates( const QDate &from, const QDate &to ); + + // QString category() const; + const QString &description() const; + const QString &location() const; + const QString ¬es() const; + const Event &event() const; + const QTime &start() const; + const QTime &end() const; + const QDate &date() const; + int length() const; + int size() const; + + QDate startDate() const; + QDate endDate() const; + +private: + class EffectiveEventPrivate *d; + Event mEvent; + QDate mDate; + QTime mStart, + mEnd; + +}; + +#ifdef PALMTOPCENTER +class QPC_EXPORT EffectiveEventSizeSorter : public QSorter +{ +public: + int compare( const EffectiveEvent& a, const EffectiveEvent& b ) const + { + return a.size() - b.size(); + } +}; + +class QPC_EXPORT EffectiveEventTimeSorter : public QSorter +{ +public: + int compare( const EffectiveEvent& a, const EffectiveEvent& b ) const + { + return a.start().secsTo( b.start() ); + } +}; +#endif + +#endif diff --git a/library/backend/palmtoprecord.cpp b/library/backend/palmtoprecord.cpp new file mode 100644 index 0000000..0d57699 --- a/dev/null +++ b/library/backend/palmtoprecord.cpp @@ -0,0 +1,127 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included +** in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A +** PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#include "palmtoprecord.h" +#include "stringutil.h" +#include + +namespace Qtopia { + +Record &Record::operator=( const Record &c ) +{ + mUid = c.mUid; + mCats = c.mCats; + customMap = c.customMap; + return *this; +} + +void Record::setCategories( int single ) +{ + if ( single == 0 ) + return; + mCats.resize(1); + mCats[0] = single; +} + +// convenience methods provided for loading and saving to xml +QString Record::idsToString( const QArray &cats ) +{ + QString str; + for ( uint i = 0; i < cats.size(); i++ ) + if ( i == 0 ) + str = QString::number( cats[int(i)] ); + else + str += ";" + QString::number( cats[int(i)] ); + + return str; +} + +// convenience methods provided for loading and saving to xml +QArray Record::idsFromString( const QString &str ) +{ + QStringList catStrs = QStringList::split( ";", str ); + QArray cats( catStrs.count() ); + uint i = 0; + for ( QStringList::ConstIterator it = catStrs.begin(); + it != catStrs.end(); ++it ) { + cats[int(i)] = (*it).toInt(); + i++; + } + return cats; +} + +/*! + Returns the string stored for the custom field \a key. + Returns a null string if the field does not exist. + */ +QString Record::customField( const QString &key) const +{ + if (customMap.contains(key)) + return customMap[key]; + + return QString::null; +} + +/*! + Sets the string stored for the custom field \a key to \a value. + */ +void Record::setCustomField( const QString &key, const QString &value) +{ + qWarning("setting custom " + key + " to " + value); + if (customMap.contains(key)) + customMap.replace(key, value); + else + customMap.insert(key, value); + + qWarning(QString("custom size %1").arg(customMap.count())); +} + +/*! + Removes the custom field \a key. + */ +void Record::removeCustomField(const QString &key) +{ + customMap.remove(key); +} + +QString Record::customToXml() const +{ + //qWarning(QString("writing custom %1").arg(customMap.count())); + QString buf(" "); + for ( QMap::ConstIterator cit = customMap.begin(); + cit != customMap.end(); ++cit) { + qWarning(".ITEM."); + buf += cit.key(); + buf += "=\""; + buf += escapeString(cit.data()); + buf += "\" "; + } + return buf; +} + +void Record::dump( const QMap &map ) +{ + QMap::ConstIterator it; + for( it = map.begin(); it != map.end(); ++it ) + qDebug("%d : %s", it.key(), it.data().local8Bit().data() ); +} + +} + diff --git a/library/backend/palmtoprecord.h b/library/backend/palmtoprecord.h new file mode 100644 index 0000000..0372011 --- a/dev/null +++ b/library/backend/palmtoprecord.h @@ -0,0 +1,94 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included +** in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A +** PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QTPALMTOP_RECORD_H +#define QTPALMTOP_RECORD_H + +#include +#include "qpcglobal.h" +#include "palmtopuidgen.h" +#include +#include + +#if defined(QPC_TEMPLATEDLL) +// MOC_SKIP_BEGIN +template class QPC_EXPORT QMap; +// MOC_SKIP_END +#endif + +class QRegExp; +namespace Qtopia { + +class RecordPrivate; +class QPC_EXPORT Record +{ +public: + Record() : mUid(0), mCats() { } + Record( const Record &c ) : mUid( c.mUid ), mCats ( c.mCats ), customMap(c.customMap) { } + virtual ~Record() { } + + Record &operator=( const Record &c ); + + virtual bool match( const QRegExp & ) const { return FALSE; } + + void setCategories( const QArray &v ) { mCats = v; } + void setCategories( int single ); + const QArray &categories() const { return mCats; } + + int uid() const { return mUid; }; + virtual void setUid( int i ) { mUid = i; uidGen().store( mUid ); } + bool isValidUid() const { return mUid != 0; } + void assignUid() { setUid( uidGen().generate() ); } + + virtual QString customField(const QString &) const; + virtual void setCustomField(const QString &, const QString &); + virtual void removeCustomField(const QString &); + + virtual bool operator == ( const Record &r ) const +{ return mUid == r.mUid; } + virtual bool operator != ( const Record &r ) const +{ return mUid != r.mUid; } + + // convenience methods provided for loading and saving to xml + static QString idsToString( const QArray &ids ); + // convenience methods provided for loading and saving to xml + static QArray idsFromString( const QString &str ); + + // for debugging + static void dump( const QMap &map ); + +protected: + virtual UidGen &uidGen() = 0; + + virtual QString customToXml() const; + +private: + int mUid; + QArray mCats; + + QMap customMap; + + RecordPrivate *d; +}; + +} + +#endif diff --git a/library/backend/palmtopuidgen.h b/library/backend/palmtopuidgen.h new file mode 100644 index 0000000..1a16681 --- a/dev/null +++ b/library/backend/palmtopuidgen.h @@ -0,0 +1,83 @@ +#ifndef QTPALMTOP_UIDGEN_H +#define QTPALMTOP_UIDGEN_H +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** Licensees holding valid Qtopia Developer license may use this +** file in accordance with the Qtopia Developer License Agreement +** provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +** PURPOSE. +** +** email sales@trolltech.com for information about Qtopia License +** Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include +#include +#include "qpcglobal.h" + +#if defined(QPC_TEMPLATEDLL) +// MOC_SKIP_BEGIN +template class QPC_EXPORT QMap< int, bool >; +// MOC_SKIP_END +#endif + +namespace Qtopia { + + +class QPC_EXPORT UidGen +{ +public: + enum Type { Qtopia, PalmtopCenter }; + + UidGen() : type( Qtopia ), sign( -1 ), ids() +{ +#ifdef PALMTOPCENTER + type = PalmtopCenter; + sign = 1; +#endif +} + UidGen( Type t ) : type(t), sign(1), ids() +{ + if ( t == Qtopia ) + sign = -1; +} + + virtual ~UidGen() { } + + int generate() const +{ + int id = sign * (int) ::time(NULL); + while ( ids.contains( id ) ) { + id += sign; + + // check for overflow cases; if so, wrap back to beginning of + // set ( -1 or 1 ) + if ( sign == -1 && id > 0 || sign == 1 && id < 0 ) + id = sign; + } + return id; +} + + void store(int id) { ids.insert(id, TRUE); } + bool isUnique(int id) const { return (!ids.contains(id)); } + +private: + Type type; + int sign; + QMap ids; + +}; + +} + +#endif diff --git a/library/backend/qfiledirect_p.h b/library/backend/qfiledirect_p.h new file mode 100644 index 0000000..fc29ac5 --- a/dev/null +++ b/library/backend/qfiledirect_p.h @@ -0,0 +1,36 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of the Qtopia Environment. +** +** Licensees holding valid Qtopia Developer license may use this +** file in accordance with the Qtopia Developer License Agreement +** provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +** PURPOSE. +** +** email sales@trolltech.com for information about Qtopia License +** Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QFILE_DIRECT_H +#define QFILE_DIRECT_H +#include +#include + +class QPC_EXPORT QFileDirect : public QFile +{ +public: + QFileDirect() : QFile() { } + QFileDirect( const QString &name ) : QFile(name) { } + + FILE *directHandle() { return fh; } +}; + +#endif \ No newline at end of file diff --git a/library/backend/qpcglobal.h b/library/backend/qpcglobal.h new file mode 100644 index 0000000..0d60272 --- a/dev/null +++ b/library/backend/qpcglobal.h @@ -0,0 +1,50 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** Licensees holding valid Qtopia Developer license may use this +** file in accordance with the Qtopia Developer License Agreement +** provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +** PURPOSE. +** +** email sales@trolltech.com for information about Qtopia License +** Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QPC_GLOBAL_H +#define QPC_GLOBAL_H + +#if ( defined(Q_OS_WIN32) || defined(Q_OS_WIN64) ) && defined(PALMTOPCENTER) +#include +// # if defined(QT_NODLL) +//# undef QPC_MAKEDLL +//# undef QPC_DLL +# if defined(QPC_MAKEDLL) /* create a Qt DLL library */ +# if defined(QPC_DLL) +# undef QPC_DLL +# endif +# define QPC_EXPORT __declspec(dllexport) +# define QPC_TEMPLATEDLL +# undef QPC_DISABLE_COPY /* avoid unresolved externals */ +# elif defined(QPC_DLL) /* use a Qt DLL library */ +# define QPC_EXPORT __declspec(dllimport) +# define QPC_TEMPLATEDLL +# undef QPC_DISABLE_COPY /* avoid unresolved externals */ +# endif +#else +# undef QPC_MAKEDLL /* ignore these for other platforms */ +# undef QPC_DLL +#endif +#endif + +#ifndef QPC_EXPORT +# define QPC_EXPORT +#endif diff --git a/library/backend/recordfields.h b/library/backend/recordfields.h new file mode 100644 index 0000000..3cddde2 --- a/dev/null +++ b/library/backend/recordfields.h @@ -0,0 +1,135 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** Licensees holding valid Qtopia Developer license may use this +** file in accordance with the Qtopia Developer License Agreement +** provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +** PURPOSE. +** +** email sales@trolltech.com for information about Qtopia License +** Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#ifndef QPC_RECORD_FIELDS_H +#define QPC_RECORD_FIELDS_H +#include "qpcglobal.h" + +// dataset = "addressbook" +namespace Qtopia +{ + static const int UID_ID = 0; + static const int CATEGORY_ID = 1; + + enum AddressBookFields { + AddressUid = UID_ID, + AddressCategory = CATEGORY_ID, + + Title, + FirstName, + MiddleName, + LastName, + Suffix, + FileAs, + + // email + DefaultEmail, + Emails, + + // home + HomeStreet, + HomeCity, + HomeState, + HomeZip, + HomeCountry, + HomePhone, + HomeFax, + HomeMobile, + HomeWebPage, + + // business + Company, + BusinessStreet, + BusinessCity, + BusinessState, + BusinessZip, + BusinessCountry, + BusinessWebPage, + JobTitle, + Department, + Office, + BusinessPhone, + BusinessFax, + BusinessMobile, + BusinessPager, + Profession, + Assistant, + Manager, + + //personal + Spouse, + Gender, + Birthday, + Anniversary, + Nickname, + Children, + + // other + Notes, + Groups + }; + + // dataset = "todolist" + enum TaskFields { + TaskUid = UID_ID, + TaskCategory = CATEGORY_ID, + + HasDate, + Completed, + TaskDescription, + Priority, + Date + }; + + // dataset = "categories" for todos + enum CategoryFields { + CatUid = UID_ID, + CatName, + CatAppGroup + }; + + +// dataset = "datebook" + enum DatebookFields { + DatebookUid = UID_ID, + DatebookCategory = CATEGORY_ID, + + DatebookDescription, + Location, + TimeZone, + Note, + StartDateTime, + EndDateTime, + DatebookType, + HasAlarm, + SoundType, + AlarmTime, + + RepeatPatternType, + RepeatPatternFrequency, + RepeatPatternPosition, + RepeatPatternDays, + RepeatPatternHasEndDate, + RepeatPatternEndDate, + }; +}; + + +#endif diff --git a/library/backend/stringutil.cpp b/library/backend/stringutil.cpp new file mode 100644 index 0000000..df58f54 --- a/dev/null +++ b/library/backend/stringutil.cpp @@ -0,0 +1,415 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included +** in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A +** PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "stringutil.h" +#include +#include + +namespace Qtopia +{ + + + +/* + Very, very simple Latin-1 only collation guaranteed to displease anyone + who actually uses the non-ASCII characters. + */ + +static const char collationHack[] = { +0x00, //C-@ +0x01, //C-A +0x02, //C-B +0x03, //C-C +0x04, //C-D +0x05, //C-E +0x06, //C-F +0x07, //C-G +0x08, //C-H +0x09, //C-I +0x0a, //C-J +0x0b, //C-K +0x0c, //C-L +0x0d, //C-M +0x0e, //C-N +0x0f, //C-O +0x10, //C-P +0x11, //C-Q +0x12, //C-R +0x13, //C-S +0x14, //C-T +0x15, //C-U +0x16, //C-V +0x17, //C-W +0x18, //C-X +0x19, //C-Y +0x1a, //C-Z +0x1b, //C-[ +0x1c, //C-\ +0x1d, //C-] +0x1e, //C-^ +0x1f, //C-_ +' ', // +'!', //! +'"', //" +'#', //# +'$', //$ +'%', //% +'&', //& +'\'', //' +'(', //( +')', //) +'*', //* +'+', //+ +',', //, +'-', //- +'.', //. +'/', /// +0x80, //0 +0x81, //1 +0x82, //2 +0x83, //3 +0x84, //4 +0x85, //5 +0x86, //6 +0x87, //7 +0x88, //8 +0x89, //9 +':', //: +';', //; +'<', //< +'=', //= +'>', //> +'?', //? +'@', //@ +'A', //A +'B', //B +'C', //C +'D', //D +'E', //E +'F', //F +'G', //G +'H', //H +'I', //I +'J', //J +'K', //K +'L', //L +'M', //M +'N', //N +'O', //O +'P', //P +'Q', //Q +'R', //R +'S', //S +'T', //T +'U', //U +'V', //V +'W', //W +'X', //X +'Y', //Y +'Z', //Z +'[', //[ +'\\', //\ +']', //] +'^', //^ +'_', //_ +'`', //` +'A', //a +'B', //b +'C', //c +'D', //d +'E', //e +'F', //f +'G', //g +'H', //h +'I', //i +'J', //j +'K', //k +'L', //l +'M', //m +'N', //n +'O', //o +'P', //p +'Q', //q +'R', //r +'S', //s +'T', //t +'U', //u +'V', //v +'W', //w +'X', //x +'Y', //y +'Z', //z +'{', //{ +'|', //| +'}', //} +'~', //~ +'', // +0x80, //C-M-@ +0x81, //C-M-A +0x82, //C-M-B +0x83, //C-M-C +0x84, //C-M-D +0x85, //C-M-E +0x86, //C-M-F +0x87, //C-M-G +0x88, //C-M-H +0x89, //C-M-I +0x8a, //C-M-J +0x8b, //C-M-K +0x8c, //C-M-L +0x8d, //C-M-M +0x8e, //C-M-N +0x8f, //C-M-O +0x90, //C-M-P +0x91, //C-M-Q +0x92, //C-M-R +0x93, //C-M-S +0x94, //C-M-T +0x95, //C-M-U +0x96, //C-M-V +0x97, //C-M-W +0x98, //C-M-X +0x99, //C-M-Y +0x9a, //C-M-Z +0x9b, //C-M-[ +0x9c, //C-M-\ +0x9d, //C-M-] +0x9e, //C-M-^ +0x9f, //C-M-_ +' ', //  +'¡', //¡ +'¢', //¢ +'£', //£ +'¤', //¤ +'¥', //¥ +'¦', //¦ +'§', //§ +'¨', //¨ +'©', //© +'A', //ª +'«', //« +'¬', //¬ +'­', //­ +'®', //® +'¯', //¯ +'O', //° +'±', //± +'²', //² +'³', //³ +'´', //´ +'µ', //µ +'P', //¶ +'·', //· +'¸', //¸ +'¹', //¹ +'O', //º +'»', //» +'¼', //¼ +'½', //½ +'¾', //¾ +'¿', //¿ +'A', //À +'A', //Á +'A', //Â +'A', //Ã +'A', //Ä +'A', //Å +'A', //Æ +'C', //Ç +'E', //È +'E', //É +'E', //Ê +'E', //Ë +'I', //Ì +'I', //Í +'I', //Î +'I', //Ï +'D', //Ð +'N', //Ñ +'O', //Ò +'O', //Ó +'O', //Ô +'O', //Õ +'O', //Ö +'×', //× +'O', //Ø +'U', //Ù +'U', //Ú +'U', //Û +'U', //Ü +'Y', //Ý +'T', //Þ +'S', //ß +'A', //à +'A', //á +'A', //â +'A', //ã +'A', //ä +'A', //å +'A', //æ +'C', //ç +'E', //è +'E', //é +'E', //ê +'E', //ë +'I', //ì +'I', //í +'I', //î +'I', //ï +'D', //ð +'N', //ñ +'O', //ò +'O', //ó +'O', //ô +'O', //õ +'O', //ö +'÷', //÷ +'O', //ø +'U', //ù +'U', //ú +'U', //û +'U', //ü +'Y', //ý +'T', //þ +'Y', //ÿ +}; + + + + + +static void hackString ( QString &s ) +{ + int len = s.length(); + const QChar* uc = s.unicode(); + for ( int i = 0; i < len; i++ ) { + if ( !uc++->row() ) + s[i] = collationHack[s[i].cell()]; + } +} + +QString buildSortKey( const QString & s ) +{ + QString res = s; + hackString( res ); + return res; +} + +QString buildSortKey( const QString & s1, const QString & s2 ) +{ + QString res = s1 + QChar( '\0' ) + s2; + hackString( res ); + return res; +} + +QString buildSortKey( const QString & s1, const QString & s2, + const QString & s3 ) +{ + QString res = s1 + QChar( '\0' ) + s2 + QChar( '\0' ) + s3; + hackString( res ); + return res; +} + +static inline QChar coll( QChar u ) +{ + return u.row() ? u : QChar(collationHack[ u.cell() ]); +} + + +int compare( const QString & s1, const QString & s2 ) +{ + const QChar* u1 = s1.unicode(); + const QChar* u2 = s2.unicode(); + + if ( u1 == u2 ) + return 0; + if ( u1 == 0 ) + return 1; + if ( u2 == 0 ) + return -1; + int l=QMIN(s1.length(),s2.length()); + while ( l-- && coll(*u1) == coll(*u2) ) + u1++,u2++; + if ( l==-1 ) + return ( s1.length()-s2.length() ); + return u1->unicode() - u2->unicode(); +} + +QString simplifyMultiLineSpace( const QString &multiLine ) +{ + QString result; + QStringList lines = QStringList::split("\n", multiLine); + for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) { + if ( it != lines.begin() ) + result += "\n"; + result += (*it).simplifyWhiteSpace(); + } + return result; +} + +QString escapeString( const QString& plain ) +{ + QString tmp(plain); + int pos = tmp.length(); + const QChar *uc = plain.unicode(); + while ( pos-- ) { + unsigned char ch = uc[pos].latin1(); + if ( ch == '&' ) + tmp.replace( pos, 1, "&" ); + else if ( ch == '<' ) + tmp.replace( pos, 1, "<" ); + else if ( ch == '>' ) + tmp.replace( pos, 1, ">" ); + else if ( ch == '\"' ) + tmp.replace( pos, 1, """ ); + } + return tmp; +} + +QString plainString( const char* escaped, unsigned int length ) +{ + return plainString( QString::fromUtf8( escaped, length ) ); +} + +QString plainString( const QCString& string ) +{ + // We first have to pass it through a ::fromUtf8() + return plainString( string.data(), string.length() ); +} + +QString plainString( const QString& string ) +{ + QString tmp( string ); + int pos = -1; + while ( (pos = tmp.find( "&", pos +1 ) ) != -1 ) { + if ( tmp.find( "&", pos ) == pos ) + tmp.replace( pos, 5, "&" ); + else if ( tmp.find( "<", pos ) == pos ) + tmp.replace( pos, 4, "<" ); + else if( tmp.find( ">", pos ) == pos ) + tmp.replace( pos, 4, ">" ); + else if ( tmp.find( """, pos ) == pos ) + tmp.replace( pos, 6, "\"" ); + } + return tmp; +} + +} // namespace QPC diff --git a/library/backend/stringutil.h b/library/backend/stringutil.h new file mode 100644 index 0000000..e9daf70 --- a/dev/null +++ b/library/backend/stringutil.h @@ -0,0 +1,57 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included +** in the packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A +** PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + + +#ifndef QTPALMTOP_stringutil_h__ +#define QTPALMTOP_stringutil_h__ + +#include +#include "qpcglobal.h" + +namespace Qtopia +{ + +// Simplifies white space within each line but keeps the new line characters +QString QPC_EXPORT simplifyMultiLineSpace( const QString &multiLine ); + +// Creates a QString which doesn't contain any "dangerous" +// characters (i.e. <, >, &, ") +QString QPC_EXPORT escapeString( const QString& plain ); + +// Takes a UTF-8!! string and removes all the XML thingies (entities?) +// from the string and also calls fromUtf8() on it... so make sure +// to pass a QCString/const char* with UTF-8 data only +QString QPC_EXPORT plainString( const char* escaped, unsigned int length ); +QString QPC_EXPORT plainString( const QCString& string ); + +QString QPC_EXPORT plainString( const QString& string ); + + +// collation functions +int compare( const QString & s1, const QString & s2 ); +QString buildSortKey( const QString & s ); +QString buildSortKey( const QString & s1, const QString & s2 ); +QString buildSortKey( const QString & s1, const QString & s2, + const QString & s3 ); + +} + +#endif diff --git a/library/backend/task.cpp b/library/backend/task.cpp new file mode 100644 index 0000000..e7d697d --- a/dev/null +++ b/library/backend/task.cpp @@ -0,0 +1,271 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include +#include +#include +#include +#include "vobject_p.h" +#include "timeconversion.h" +#include "qfiledirect_p.h" + +#include + +using namespace Qtopia; +UidGen Task::sUidGen( UidGen::Qtopia ); + +Task::Task() : Record(), mDue( FALSE ), +mDueDate( QDate::currentDate() ), +mCompleted( FALSE ), mPriority( 3 ), mDesc() +{ +} + +Task::Task( const QMap &m ) : Record(), mDue( FALSE ), +mDueDate( QDate::currentDate() ), mCompleted( FALSE ), mPriority( 3 ), mDesc() +{ + //qDebug("Task::Task fromMap"); + //dump( m ); + for ( QMap::ConstIterator it = m.begin(); it != m.end();++it ) + switch ( (TaskFields) it.key() ) { + case HasDate: if ( *it == "1" ) mDue = TRUE; break; + case Completed: setCompleted( *it == "1" ); break; + case TaskCategory: setCategories( idsFromString( *it ) ); break; + case TaskDescription: setDescription( *it ); break; + case Priority: setPriority( (*it).toInt() ); break; + case Date: mDueDate = TimeConversion::fromString( (*it) ); break; + case TaskUid: setUid( (*it).toInt() ); break; + } +} + +Task::~Task() +{ +} + +QMap Task::toMap() const +{ + QMap m; + m.insert( HasDate, hasDueDate() ? "1" : "0" ); + m.insert( Completed, isCompleted() ? "1" : "0" ); + m.insert( TaskCategory, idsToString( categories() ) ); + m.insert( TaskDescription, description() ); + m.insert( Priority, QString::number( priority() ) ); + m.insert( Date, TimeConversion::toString( dueDate() ) ); + m.insert( TaskUid, QString::number(uid()) ); + + //qDebug("Task::toMap"); + //dump( m ); + return m; +} + +void Task::save( QString& buf ) const +{ + buf += " Completed=\""; + // qDebug( "writing %d", complete ); + buf += QString::number( (int)mCompleted ); + buf += "\""; + buf += " HasDate=\""; + // qDebug( "writing %d", ); + buf += QString::number( (int)mDue ); + buf += "\""; + buf += " Priority=\""; + // qDebug ("writing %d", prior ); + buf += QString::number( mPriority ); + buf += "\""; + buf += " Categories=\""; + buf += Qtopia::Record::idsToString( categories() ); + buf += "\""; + buf += " Description=\""; + // qDebug( "writing note %s", note.latin1() ); + buf += Qtopia::escapeString( mDesc ); + buf += "\""; + if ( mDue ) { + // qDebug("saving ymd %d %d %d", mDueDate.year(), mDueDate.month(), + // mDueDate.day() ); + buf += " DateYear=\""; + buf += QString::number( mDueDate.year() ); + buf += "\""; + buf += " DateMonth=\""; + buf += QString::number( mDueDate.month() ); + buf += "\""; + buf += " DateDay=\""; + buf += QString::number( mDueDate.day() ); + buf += "\""; + } + buf += customToXml(); + // qDebug ("writing uid %d", uid() ); + buf += " Uid=\""; + buf += QString::number( uid() ); + // terminate it in the application... + buf += "\""; +} + +bool Task::match ( const QRegExp &r ) const +{ + // match on priority, description on due date... + bool match; + match = false; + if ( QString::number( mPriority ).find( r ) > -1 ) + match = true; + else if ( mDue && mDueDate.toString().find( r ) > -1 ) + match = true; + else if ( mDesc.find( r ) > -1 ) + match = true; + return match; +} + +static inline VObject *safeAddPropValue( VObject *o, const char *prop, const QString &value ) +{ + VObject *ret = 0; + if ( o && !value.isEmpty() ) + ret = addPropValue( o, prop, value.latin1() ); + return ret; +} + +static inline VObject *safeAddProp( VObject *o, const char *prop) +{ + VObject *ret = 0; + if ( o ) + ret = addProp( o, prop ); + return ret; +} + + +static VObject *createVObject( const Task &t ) +{ + VObject *vcal = newVObject( VCCalProp ); + safeAddPropValue( vcal, VCVersionProp, "1.0" ); + VObject *task = safeAddProp( vcal, VCTodoProp ); + + if ( t.hasDueDate() ) + safeAddPropValue( task, VCDueProp, TimeConversion::toISO8601( t.dueDate() ) ); + safeAddPropValue( task, VCDescriptionProp, t.description() ); + if ( t.isCompleted() ) + safeAddPropValue( task, VCStatusProp, "COMPLETED" ); + safeAddPropValue( task, VCPriorityProp, QString::number( t.priority() ) ); + + return vcal; +} + + +static Task parseVObject( VObject *obj ) +{ + Task t; + + VObjectIterator it; + initPropIterator( &it, obj ); + while( moreIteration( &it ) ) { + VObject *o = nextVObject( &it ); + QCString name = vObjectName( o ); + QCString value = vObjectStringZValue( o ); + if ( name == VCDueProp ) { + t.setDueDate( TimeConversion::fromISO8601( value ).date(), TRUE ); + } + else if ( name == VCDescriptionProp ) { + t.setDescription( value ); + } + else if ( name == VCStatusProp ) { + if ( value == "COMPLETED" ) + t.setCompleted( TRUE ); + } + else if ( name == VCPriorityProp ) { + t.setPriority( value.toInt() ); + } +#if 0 + else { + printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) ); + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + printf(" subprop: %s = %s\n", name.data(), value.latin1() ); + } + } +#endif + } + + return t; +} + + + +void Task::writeVCalendar( const QString &filename, const QValueList &tasks) +{ + QFileDirect f( filename.utf8().data() ); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vcard write"); + return; + } + + QValueList::ConstIterator it; + for( it = tasks.begin(); it != tasks.end(); ++it ) { + VObject *obj = createVObject( *it ); + writeVObject(f.directHandle() , obj ); + cleanVObject( obj ); + } + + cleanStrTbl(); +} + +void Task::writeVCalendar( const QString &filename, const Task &task) +{ + QFileDirect f( filename.utf8().data() ); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vcard write"); + return; + } + + VObject *obj = createVObject( task ); + writeVObject(f.directHandle() , obj ); + cleanVObject( obj ); + + cleanStrTbl(); +} + + +QValueList Task::readVCalendar( const QString &filename ) +{ + VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() ); + + QValueList tasks; + + while ( obj ) { + QCString name = vObjectName( obj ); + if ( name == VCCalProp ) { + VObjectIterator nit; + initPropIterator( &nit, obj ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + if ( name == VCTodoProp ) + tasks.append( parseVObject( o ) ); + } + } else if ( name == VCTodoProp ) { + // shouldn't happen, but just to be sure + tasks.append( parseVObject( obj ) ); + } + VObject *t = obj; + obj = nextVObjectInList(obj); + cleanVObject( t ); + } + + return tasks; +} diff --git a/library/backend/task.h b/library/backend/task.h new file mode 100644 index 0000000..ffe26b0 --- a/dev/null +++ b/library/backend/task.h @@ -0,0 +1,77 @@ +/********************************************************************** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#ifndef __TASK_H__ +#define __TASK_H__ + +#include +#include + +#include +#include + +class TaskPrivate; +class QPC_EXPORT Task : public Qtopia::Record +{ +public: + Task(); + Task( const QMap &fromMap ); + ~Task(); + + QMap toMap() const; + + static void writeVCalendar( const QString &filename, const QValueList &tasks); + static void writeVCalendar( const QString &filename, const Task &task); + static QValueList readVCalendar( const QString &filename ); + + void setPriority( int priority ) { mPriority = priority; } + int priority() const { return mPriority; } + +// void setCategory( const QString& category ) +// { mCategory = category.stripWhiteSpace(); } +// const QString &category() const { return mCategory; } + + void setDescription( const QString& description ) + { mDesc = Qtopia::simplifyMultiLineSpace(description); } + const QString &description() const { return mDesc; } + + void setDueDate( const QDate& date, bool hasDue ) { mDueDate = date; mDue = hasDue; } + const QDate &dueDate() const { return mDueDate; } + bool hasDueDate() const { return mDue; } + void setHasDueDate( bool b ) { mDue = b; } + + void setCompleted( bool b ) { mCompleted = b; } + bool isCompleted() const { return mCompleted; } + + void save( QString& buf ) const; + bool match( const QRegExp &r ) const; + +private: + Qtopia::UidGen &uidGen() { return sUidGen; } + static Qtopia::UidGen sUidGen; + + bool mDue; + QDate mDueDate; + bool mCompleted; + int mPriority; + QString mDesc; + TaskPrivate *d; +}; + +#endif diff --git a/library/backend/timeconversion.cpp b/library/backend/timeconversion.cpp new file mode 100644 index 0000000..a4a2547 --- a/dev/null +++ b/library/backend/timeconversion.cpp @@ -0,0 +1,237 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include +#include "timeconversion.h" +#include +#include + +QString TimeConversion::toString( const QDate &d ) +{ + QString r = QString::number( d.day() ) + "." + + QString::number( d.month() ) + "." + + QString::number( d.year() ); + //qDebug("TimeConversion::toString %s", r.latin1()); + return r; +} + +QDate TimeConversion::fromString( const QString &datestr ) +{ + int monthPos = datestr.find('.'); + int yearPos = datestr.find('.', monthPos+1 ); + if ( monthPos == -1 || yearPos == -1 ) { + qDebug("fromString didn't find . in str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, yearPos ); + return QDate(); + } + int d = datestr.left( monthPos ).toInt(); + int m = datestr.mid( monthPos+1, yearPos - monthPos - 1 ).toInt(); + int y = datestr.mid( yearPos+1 ).toInt(); + QDate date ( y,m,d ); + //qDebug("TimeConversion::fromString ymd = %s => %d %d %d; mpos = %d ypos = %d", datestr.latin1(), y, m, d, monthPos, yearPos); + return date; +} + +time_t TimeConversion::toUTC( const QDateTime& dt ) +{ + time_t tmp; + struct tm *lt; + +#if defined(_OS_WIN32) || defined (Q_OS_WIN32) || defined (Q_OS_WIN64) + _tzset(); +#else + tzset(); +#endif + + // get a tm structure from the system to get the correct tz_name + tmp = time( 0 ); + lt = localtime( &tmp ); + + lt->tm_sec = dt.time().second(); + lt->tm_min = dt.time().minute(); + lt->tm_hour = dt.time().hour(); + lt->tm_mday = dt.date().day(); + lt->tm_mon = dt.date().month() - 1; // 0-11 instead of 1-12 + lt->tm_year = dt.date().year() - 1900; // year - 1900 + //lt->tm_wday = dt.date().dayOfWeek(); ignored anyway + //lt->tm_yday = dt.date().dayOfYear(); ignored anyway + lt->tm_wday = -1; + lt->tm_yday = -1; + // tm_isdst negative -> mktime will find out about DST + lt->tm_isdst = -1; + // keep tm_zone and tm_gmtoff + tmp = mktime( lt ); + return tmp; +} + +QDateTime TimeConversion::fromUTC( time_t time ) +{ + struct tm *lt; + +#if defined(_OS_WIN32) || defined (Q_OS_WIN32) || defined (Q_OS_WIN64) + _tzset(); +#else + tzset(); +#endif + lt = localtime( &time ); + QDateTime dt; + dt.setDate( QDate( lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday ) ); + dt.setTime( QTime( lt->tm_hour, lt->tm_min, lt->tm_sec ) ); + return dt; +} + + +int TimeConversion::secsTo( const QDateTime &from, const QDateTime &to ) +{ + return toUTC( to ) - toUTC( from ); +} + +QCString TimeConversion::toISO8601( const QDate &d ) +{ + time_t tmp = toUTC( d ); + struct tm *utc = gmtime( &tmp ); + + QCString str; + str.sprintf("%04d%02d%02d", (utc->tm_year + 1900), utc->tm_mon+1, utc->tm_mday ); + return str; +} + +QCString TimeConversion::toISO8601( const QDateTime &dt ) +{ + time_t tmp = toUTC( dt ); + struct tm *utc = gmtime( &tmp ); + + QCString str; + str.sprintf("%04d%02d%02dT%02d%02d%02dZ", + (utc->tm_year + 1900), utc->tm_mon+1, utc->tm_mday, + utc->tm_hour, utc->tm_min, utc->tm_sec ); + return str; +} + +QDateTime TimeConversion::fromISO8601( const QCString &s ) +{ + +#if defined(_OS_WIN32) || defined (Q_OS_WIN32) || defined (Q_OS_WIN64) + _tzset(); +#else + tzset(); +#endif + + struct tm *thetime = new tm; + + QCString str = s.copy(); + str.replace(QRegExp("-"), "" ); + str.replace(QRegExp(":"), "" ); + str.stripWhiteSpace(); + str = str.lower(); + + int i = str.find( "t" ); + QCString date; + QCString timestr; + if ( i != -1 ) { + date = str.left( i ); + timestr = str.mid( i+1 ); + } else { + date = str; + } + +// qDebug("--- parsing ISO time---"); + thetime->tm_year = 100; + thetime->tm_mon = 0; + thetime->tm_mday = 0; + thetime->tm_hour = 0; + thetime->tm_min = 0; + thetime->tm_sec = 0; + +// qDebug("date = %s", date.data() ); + + switch( date.length() ) { + case 8: + thetime->tm_mday = date.right( 2 ).toInt(); + case 6: + thetime->tm_mon = date.mid( 4, 2 ).toInt() - 1; + case 4: + thetime->tm_year = date.left( 4 ).toInt(); + thetime->tm_year -= 1900; + break; + default: + break; + } + + int tzoff = 0; + bool inLocalTime = FALSE; + if ( timestr.find( 'z' ) == (int)timestr.length() - 1 ) + // UTC + timestr = timestr.left( timestr.length() -1 ); + else { + int plus = timestr.find( "+" ); + int minus = timestr.find( "-" ); + if ( plus != -1 || minus != -1 ) { + // have a timezone offset + plus = (plus != -1) ? plus : minus; + QCString off = timestr.mid( plus ); + timestr = timestr.left( plus ); + + int tzoffhour = 0; + int tzoffmin = 0; + switch( off.length() ) { + case 5: + tzoffmin = off.mid(3).toInt(); + case 3: + tzoffhour = off.left(3).toInt(); + default: + break; + } + tzoff = 60*tzoffhour + tzoffmin; + } else + inLocalTime = TRUE; + } + + // get the time: + switch( timestr.length() ) { + case 6: + thetime->tm_sec = timestr.mid( 4 ).toInt(); + case 4: + thetime->tm_min = timestr.mid( 2, 2 ).toInt(); + case 2: + thetime->tm_hour = timestr.left( 2 ).toInt(); + default: + break; + } + + int tzloc = 0; + time_t tmp = time( 0 ); + if ( !inLocalTime ) { + // have to get the offset between gmt and local time + struct tm *lt = localtime( &tmp ); + tzloc = mktime( lt ); + struct tm *ut = gmtime( &tmp ); + tzloc -= mktime( ut ); + } +// qDebug("time: %d %d %d, tzloc=%d, tzoff=%d", thetime->tm_hour, thetime->tm_min, thetime->tm_sec, +// tzloc, tzoff ); + + tmp = mktime( thetime ); + tmp += 60*(-tzloc + tzoff); + + delete thetime; + + return fromUTC( tmp ); +} + diff --git a/library/backend/timeconversion.h b/library/backend/timeconversion.h new file mode 100644 index 0000000..1724812 --- a/dev/null +++ b/library/backend/timeconversion.h @@ -0,0 +1,45 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef __timeconversion_h__ +#define __timeconversion_h__ + +#include +#include +#include + +#include + +class QPC_EXPORT TimeConversion +{ +public: + static QString toString( const QDate &d ); + static QDate fromString( const QString &datestr ); + + static time_t toUTC( const QDateTime& dt ); + static QDateTime fromUTC( time_t time ); + static int secsTo( const QDateTime &from, const QDateTime &to ); + + static QCString toISO8601( const QDate & ); + static QCString toISO8601( const QDateTime & ); + static QDateTime fromISO8601( const QCString & ); +}; + +#endif // __timeconversion_h__ diff --git a/library/backend/vcc.y b/library/backend/vcc.y new file mode 100644 index 0000000..0225982 --- a/dev/null +++ b/library/backend/vcc.y @@ -0,0 +1,1199 @@ +%{ + +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vcc.c + * doc: Parser for vCard and vCalendar. Note that this code is + * generated by a yacc parser generator. Generally it should not + * be edited by hand. The real source is vcc.y. The #line directives + * can be commented out here to make it easier to trace through + * in a debugger. However, if a bug is found it should + * be fixed in vcc.y and this file regenerated. + */ + + +/* debugging utilities */ +#if __DEBUG +#define DBG_(x) printf x +#else +#define DBG_(x) +#endif + +/**** External Functions ****/ + +/* assign local name to parser variables and functions so that + we can use more than one yacc based parser. +*/ + +#if 0 +#define yyparse mime_parse +#define yylex mime_lex +#define yyerror mime_error +#define yychar mime_char +/* #define p_yyval p_mime_val */ +#undef yyval +#define yyval mime_yyval +/* #define p_yylval p_mime_lval */ +#undef yylval +#define yylval mime_yylval +#define yydebug mime_debug +#define yynerrs mime_nerrs +#define yyerrflag mime_errflag +#define yyss mime_ss +#define yyssp mime_ssp +#define yyvs mime_vs +#define yyvsp mime_vsp +#define yylhs mime_lhs +#define yylen mime_len +#define yydefred mime_defred +#define yydgoto mime_dgoto +#define yysindex mime_sindex +#define yyrindex mime_rindex +#define yygindex mime_gindex +#define yytable mime_table +#define yycheck mime_check +#define yyname mime_name +#define yyrule mime_rule +#ifdef YYPREFIX +#undef YYPREFIX +#endif +#define YYPREFIX "mime_" +#endif + + +#ifndef _NO_LINE_FOLDING +#define _SUPPORT_LINE_FOLDING 1 +#endif + +/* undef below if compile with MFC */ +/* #define INCLUDEMFC 1 */ + +#if defined(WIN32) || defined(_WIN32) +#ifdef INCLUDEMFC +#include +#endif +#endif + +#include +#ifndef __MWERKS__ +#include +#endif +#include +#include +#include + +//#ifdef PALMTOPCENTER +//#include +//#else +#include "vobject_p.h" +//#endif + +/**** Types, Constants ****/ + +#define YYDEBUG 0 /* 1 to compile in some debugging code */ +#define MAXTOKEN 256 /* maximum token (line) length */ +#define YYSTACKSIZE 100 // ~unref ? +#define MAXLEVEL 10 /* max # of nested objects parseable */ + /* (includes outermost) */ + + +/**** Global Variables ****/ +int mime_lineNum, mime_numErrors; /* yyerror() can use these */ +static VObject* vObjList; +static VObject *curProp; +static VObject *curObj; +static VObject* ObjStack[MAXLEVEL]; +static int ObjStackTop; + + +/* A helpful utility for the rest of the app. */ +#if __CPLUSPLUS__ +extern "C" { +#endif + + extern void yyerror(char *s); + +#if __CPLUSPLUS__ + }; +#endif + +int yyparse(); + +enum LexMode { + L_NORMAL, + L_VCARD, + L_VCAL, + L_VEVENT, + L_VTODO, + L_VALUES, + L_BASE64, + L_QUOTED_PRINTABLE + }; + +/**** Private Forward Declarations ****/ +static int pushVObject(const char *prop); +static VObject* popVObject(); +static void lexPopMode(int top); +static int lexWithinMode(enum LexMode mode); +static void lexPushMode(enum LexMode mode); +static void enterProps(const char *s); +static void enterAttr(const char *s1, const char *s2); +static void enterValues(const char *value); +#define mime_error yyerror +void mime_error(char *s); +void mime_error_(char *s); + +%} + +/***************************************************************************/ +/*** The grammar ****/ +/***************************************************************************/ + +%union { + char *str; + VObject *vobj; + } + +%token + EQ COLON DOT SEMICOLON SPACE HTAB LINESEP NEWLINE + BEGIN_VCARD END_VCARD BEGIN_VCAL END_VCAL + BEGIN_VEVENT END_VEVENT BEGIN_VTODO END_VTODO + ID + +/* + * NEWLINE is the token that would occur outside a vCard, + * while LINESEP is the token that would occur inside a vCard. + */ + +%token + STRING ID + +%type name value + +%type vcard vcal vobject + +%start mime + +%% + + +mime: vobjects + ; + +vobjects: vobjects vobject + { addList(&vObjList, $2); curObj = 0; } + | vobject + { addList(&vObjList, $1); curObj = 0; } + ; + +vobject: vcard + | vcal + ; + +vcard: + BEGIN_VCARD + { + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + } + items END_VCARD + { + lexPopMode(0); + $$ = popVObject(); + } + | BEGIN_VCARD + { + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + } + END_VCARD + { + lexPopMode(0); + $$ = popVObject(); + } + ; + +items: items item + | item + ; + +item: prop COLON + { + lexPushMode(L_VALUES); + } + values LINESEP + { + if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE)) + lexPopMode(0); + lexPopMode(0); + } + | error + ; + +prop: name + { + enterProps($1); + } + attr_params + | name + { + enterProps($1); + } + ; + +attr_params: attr_params attr_param + | attr_param + ; + +attr_param: SEMICOLON attr + ; + +attr: name + { + enterAttr($1,0); + } + | name EQ name + { + enterAttr($1,$3); + + } + ; + +name: ID + ; + +values: value SEMICOLON { enterValues($1); } values + | value + { enterValues($1); } + ; + +value: STRING + | + { $$ = 0; } + ; + +vcal: + BEGIN_VCAL + { if (!pushVObject(VCCalProp)) YYERROR; } + calitems + END_VCAL + { $$ = popVObject(); } + | BEGIN_VCAL + { if (!pushVObject(VCCalProp)) YYERROR; } + END_VCAL + { $$ = popVObject(); } + ; + +calitems: calitems calitem + | calitem + ; + +calitem: + eventitem + | todoitem + | items + ; + +eventitem: + BEGIN_VEVENT + { + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + } + items + END_VEVENT + { + lexPopMode(0); + popVObject(); + } + | BEGIN_VEVENT + { + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + } + END_VEVENT + { + lexPopMode(0); + popVObject(); + } + ; + +todoitem: + BEGIN_VTODO + { + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + } + items + END_VTODO + { + lexPopMode(0); + popVObject(); + } + | BEGIN_VTODO + { + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + } + END_VTODO + { + lexPopMode(0); + popVObject(); + } + ; + +%% +/*------------------------------------*/ +static int pushVObject(const char *prop) + { + VObject *newObj; + if (ObjStackTop == MAXLEVEL) + return FALSE; + + ObjStack[++ObjStackTop] = curObj; + + if (curObj) { + newObj = addProp(curObj,prop); + curObj = newObj; + } + else + curObj = newVObject(prop); + + return TRUE; + } + + +/*---------------------------------------*/ +/* This pops the recently built vCard off the stack and returns it. */ +static VObject* popVObject() + { + VObject *oldObj; + if (ObjStackTop < 0) { + yyerror("pop on empty Object Stack\n"); + return 0; + } + oldObj = curObj; + curObj = ObjStack[ObjStackTop--]; + + return oldObj; + } + + +static void enterValues(const char *value) + { + if (fieldedProp && *fieldedProp) { + if (value) { + addPropValue(curProp,*fieldedProp,value); + } + /* else this field is empty, advance to next field */ + fieldedProp++; + } + else { + if (value) { + setVObjectStringZValue_(curProp,strdup( value )); + } + } + deleteStr(value); + } + +static void enterProps(const char *s) + { + curProp = addGroup(curObj,s); + deleteStr(s); + } + +static void enterAttr(const char *s1, const char *s2) + { + const char *p1, *p2; + p1 = lookupProp_(s1); + if (s2) { + VObject *a; + p2 = lookupProp_(s2); + a = addProp(curProp,p1); + setVObjectStringZValue(a,p2); + } + else + addProp(curProp,p1); + if (qstricmp(p1,VCBase64Prop) == 0 || (s2 && qstricmp(p2,VCBase64Prop)==0)) + lexPushMode(L_BASE64); + else if (qstricmp(p1,VCQuotedPrintableProp) == 0 + || (s2 && qstricmp(p2,VCQuotedPrintableProp)==0)) + lexPushMode(L_QUOTED_PRINTABLE); + deleteStr(s1); deleteStr(s2); + } + + +#define MAX_LEX_LOOKAHEAD_0 32 +#define MAX_LEX_LOOKAHEAD 64 +#define MAX_LEX_MODE_STACK_SIZE 10 +#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop]) + +struct LexBuf { + /* input */ +#ifdef INCLUDEMFC + CFile *inputFile; +#else + FILE *inputFile; +#endif + char *inputString; + unsigned long curPos; + unsigned long inputLen; + /* lookahead buffer */ + /* -- lookahead buffer is short instead of char so that EOF + / can be represented correctly. + */ + unsigned long len; + short buf[MAX_LEX_LOOKAHEAD]; + unsigned long getPtr; + /* context stack */ + unsigned long lexModeStackTop; + enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE]; + /* token buffer */ + unsigned long maxToken; + char *strs; + unsigned long strsLen; + } lexBuf; + +static void lexPushMode(enum LexMode mode) + { + if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1)) + yyerror("lexical context stack overflow"); + else { + lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode; + } + } + +static void lexPopMode(int top) + { + /* special case of pop for ease of error recovery -- this + version will never underflow */ + if (top) + lexBuf.lexModeStackTop = 0; + else + if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--; + } + +static int lexWithinMode(enum LexMode mode) { + unsigned long i; + for (i=0;iRead(&result, 1) == 1 ? result : EOF; +#else + return fgetc(lexBuf.inputFile); +#endif + } + } + +static int lexGeta() + { + ++lexBuf.len; + return (lexBuf.buf[lexBuf.getPtr] = lexGetc_()); + } + +static int lexGeta_(int i) + { + ++lexBuf.len; + return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_()); + } + +static void lexSkipLookahead() { + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* don't skip EOF. */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + } + +static int lexLookahead() { + int c = (lexBuf.len)? + lexBuf.buf[lexBuf.getPtr]: + lexGeta(); + /* do the \r\n -> \n or \r -> \n translation here */ + if (c == '\r') { + int a = (lexBuf.len>1)? + lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]: + lexGeta_(1); + if (a == '\n') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = c = '\n'; + } + else if (c == '\n') { + int a = (lexBuf.len>1)? + lexBuf.buf[lexBuf.getPtr+1]: + lexGeta_(1); + if (a == '\r') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = '\n'; + } + return c; + } + +static int lexGetc() { + int c = lexLookahead(); + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* EOF will remain in lookahead buffer */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + return c; + } + +static void lexSkipLookaheadWord() { + if (lexBuf.strsLen <= lexBuf.len) { + lexBuf.len -= lexBuf.strsLen; + lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD; + } + } + +static void lexClearToken() + { + lexBuf.strsLen = 0; + } + +static void lexAppendc(int c) + { + lexBuf.strs[lexBuf.strsLen] = c; + /* append up to zero termination */ + if (c == 0) return; + lexBuf.strsLen++; + if (lexBuf.strsLen > lexBuf.maxToken) { + /* double the token string size */ + lexBuf.maxToken <<= 1; + lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken); + } + } + +static char* lexStr() { + return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1); + } + +static void lexSkipWhite() { + int c = lexLookahead(); + while (c == ' ' || c == '\t') { + lexSkipLookahead(); + c = lexLookahead(); + } + } + +static char* lexGetWord() { + int c; + lexSkipWhite(); + lexClearToken(); + c = lexLookahead(); + while (c != EOF && !strchr("\t\n ;:=",c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return lexStr(); + } + +static void lexPushLookaheadc(int c) { + int putptr; + /* can't putback EOF, because it never leaves lookahead buffer */ + if (c == EOF) return; + putptr = (int)lexBuf.getPtr - 1; + if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD; + lexBuf.getPtr = putptr; + lexBuf.buf[putptr] = c; + lexBuf.len += 1; + } + +static char* lexLookaheadWord() { + /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0 + / and thing bigger than that will stop the lookahead and return 0; + / leading white spaces are not recoverable. + */ + int c; + int len = 0; + int curgetptr = 0; + lexSkipWhite(); + lexClearToken(); + curgetptr = (int)lexBuf.getPtr; // remember! + while (len < (MAX_LEX_LOOKAHEAD_0)) { + c = lexGetc(); + len++; + if (c == EOF || strchr("\t\n ;:=", c)) { + lexAppendc(0); + /* restore lookahead buf. */ + lexBuf.len += len; + lexBuf.getPtr = curgetptr; + return lexStr(); + } + else + lexAppendc(c); + } + lexBuf.len += len; /* char that has been moved to lookahead buffer */ + lexBuf.getPtr = curgetptr; + return 0; + } + +#ifdef _SUPPORT_LINE_FOLDING +static void handleMoreRFC822LineBreak(int c) { + /* suport RFC 822 line break in cases like + * ADR: foo; + * morefoo; + * more foo; + */ + if (c == ';') { + int a; + lexSkipLookahead(); + /* skip white spaces */ + a = lexLookahead(); + while (a == ' ' || a == '\t') { + lexSkipLookahead(); + a = lexLookahead(); + } + if (a == '\n') { + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + /* continuation, throw away all the \n and spaces read so + * far + */ + lexSkipWhite(); + lexPushLookaheadc(';'); + } + else { + lexPushLookaheadc('\n'); + lexPushLookaheadc(';'); + } + } + else { + lexPushLookaheadc(';'); + } + } + } + +static char* lexGet1Value() { + int c; + lexSkipWhite(); + c = lexLookahead(); + lexClearToken(); + while (c != EOF && c != ';') { + if (c == '\\' ) { + int a; + lexSkipLookahead(); + a = lexLookahead(); + if ( a != ';' ) { + lexAppendc('\\'); + } else { + lexAppendc( ';' ); + lexSkipLookahead(); + } + } else if (c == '\n') { + int a; + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + lexAppendc(' '); + lexSkipLookahead(); + } + else { + lexPushLookaheadc('\n'); + break; + } + } + else { + lexAppendc(c); + lexSkipLookahead(); + } + c = lexLookahead(); + } + lexAppendc(0); + handleMoreRFC822LineBreak(c); + return c==EOF?0:lexStr(); + } +#endif + +static int match_begin_name(int end) { + char *n = lexLookaheadWord(); + int token = ID; + if (n) { + if (!qstricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD; + else if (!qstricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL; + else if (!qstricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT; + else if (!qstricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO; + deleteStr(n); + return token; + } + return 0; + } + + +#ifdef INCLUDEMFC +void initLex(const char *inputstring, unsigned long inputlen, CFile *inputfile) +#else +void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile) +#endif + { + // initialize lex mode stack + lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL; + + // iniatialize lex buffer. + lexBuf.inputString = (char*) inputstring; + lexBuf.inputLen = inputlen; + lexBuf.curPos = 0; + lexBuf.inputFile = inputfile; + + lexBuf.len = 0; + lexBuf.getPtr = 0; + + lexBuf.maxToken = MAXTOKEN; + lexBuf.strs = (char*)malloc(MAXTOKEN); + lexBuf.strsLen = 0; + + } + +static void finiLex() { + free(lexBuf.strs); + } + + +/*-----------------------------------*/ +/* This parses and converts the base64 format for binary encoding into + * a decoded buffer (allocated with new). See RFC 1521. + */ +static char * lexGetDataFromBase64() + { + unsigned long bytesLen = 0, bytesMax = 0; + int quadIx = 0, pad = 0; + unsigned long trip = 0; + unsigned char b; + int c; + unsigned char *bytes = NULL; + unsigned char *oldBytes = NULL; + + DBG_(("db: lexGetDataFromBase64\n")); + while (1) { + c = lexGetc(); + if (c == '\n') { + ++mime_lineNum; + if (lexLookahead() == '\n') { + /* a '\n' character by itself means end of data */ + break; + } + else continue; /* ignore '\n' */ + } + else { + if ((c >= 'A') && (c <= 'Z')) + b = (unsigned char)(c - 'A'); + else if ((c >= 'a') && (c <= 'z')) + b = (unsigned char)(c - 'a') + 26; + else if ((c >= '0') && (c <= '9')) + b = (unsigned char)(c - '0') + 52; + else if (c == '+') + b = 62; + else if (c == '/') + b = 63; + else if (c == '=') { + b = 0; + pad++; + } else if ((c == ' ') || (c == '\t')) { + continue; + } else { /* error condition */ + if (bytes) free(bytes); + else if (oldBytes) free(oldBytes); + // error recovery: skip until 2 adjacent newlines. + DBG_(("db: invalid character 0x%x '%c'\n", c,c)); + if (c != EOF) { + c = lexGetc(); + while (c != EOF) { + if (c == '\n' && lexLookahead() == '\n') { + ++mime_lineNum; + break; + } + c = lexGetc(); + } + } + return NULL; + } + trip = (trip << 6) | b; + if (++quadIx == 4) { + unsigned char outBytes[3]; + int numOut; + int i; + for (i = 0; i < 3; i++) { + outBytes[2-i] = (unsigned char)(trip & 0xFF); + trip >>= 8; + } + numOut = 3 - pad; + if (bytesLen + numOut > bytesMax) { + if (!bytes) { + bytesMax = 1024; + bytes = (unsigned char*)malloc((size_t)bytesMax); + } + else { + bytesMax <<= 2; + oldBytes = bytes; + bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax); + } + if (bytes == 0) { + mime_error("out of memory while processing BASE64 data\n"); + } + } + if (bytes) { + memcpy(bytes + bytesLen, outBytes, numOut); + bytesLen += numOut; + } + trip = 0; + quadIx = 0; + } + } + } /* while */ + DBG_(("db: bytesLen = %d\n", bytesLen)); + /* kludge: all this won't be necessary if we have tree form + representation */ + if (bytes) { + setValueWithSize(curProp,bytes,(unsigned int)bytesLen); + free(bytes); + } + else if (oldBytes) { + setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen); + free(oldBytes); + } + return 0; + } + +static int match_begin_end_name(int end) { + int token; + lexSkipWhite(); + if (lexLookahead() != ':') return ID; + lexSkipLookahead(); + lexSkipWhite(); + token = match_begin_name(end); + if (token == ID) { + lexPushLookaheadc(':'); + DBG_(("db: ID '%s'\n", yylval.str)); + return ID; + } + else if (token != 0) { + lexSkipLookaheadWord(); + deleteStr(yylval.str); + DBG_(("db: begin/end %d\n", token)); + return token; + } + return 0; + } + +static char* lexGetQuotedPrintable() + { + char cur; + + lexClearToken(); + do { + cur = lexGetc(); + switch (cur) { + case '=': { + int c = 0; + int next[2]; + int i; + for (i = 0; i < 2; i++) { + next[i] = lexGetc(); + if (next[i] >= '0' && next[i] <= '9') + c = c * 16 + next[i] - '0'; + else if (next[i] >= 'A' && next[i] <= 'F') + c = c * 16 + next[i] - 'A' + 10; + else + break; + } + if (i == 0) { + /* single '=' follow by LINESEP is continuation sign? */ + if (next[0] == '\n') { + ++mime_lineNum; + } + else { + lexPushLookaheadc('='); + goto EndString; + } + } + else if (i == 1) { + lexPushLookaheadc(next[1]); + lexPushLookaheadc(next[0]); + lexAppendc('='); + } else { + lexAppendc(c); + } + break; + } /* '=' */ + case '\n': { + lexPushLookaheadc('\n'); + goto EndString; + } + case (char)EOF: + break; + default: + lexAppendc(cur); + break; + } /* switch */ + } while (cur != (char)EOF); + +EndString: + lexAppendc(0); + return lexStr(); + } /* LexQuotedPrintable */ + +static int yylex() { + + int lexmode = LEXMODE(); + if (lexmode == L_VALUES) { + int c = lexGetc(); + if (c == ';') { + DBG_(("db: SEMICOLON\n")); + lexPushLookaheadc(c); + handleMoreRFC822LineBreak(c); + lexSkipLookahead(); + return SEMICOLON; + } + else if (strchr("\n",c)) { + ++mime_lineNum; + /* consume all line separator(s) adjacent to each other */ + c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + } + DBG_(("db: LINESEP\n")); + return LINESEP; + } + else { + char *p = 0; + lexPushLookaheadc(c); + if (lexWithinMode(L_BASE64)) { + /* get each char and convert to bin on the fly... */ + p = lexGetDataFromBase64(); + yylval.str = p; + return STRING; + } + else if (lexWithinMode(L_QUOTED_PRINTABLE)) { + p = lexGetQuotedPrintable(); + } + else { +#ifdef _SUPPORT_LINE_FOLDING + p = lexGet1Value(); +#else + p = lexGetStrUntil(";\n"); +#endif + } + if (p) { + DBG_(("db: STRING: '%s'\n", p)); + yylval.str = p; + return STRING; + } + else return 0; + } + } + else { + /* normal mode */ + while (1) { + int c = lexGetc(); + switch(c) { + case ':': { + /* consume all line separator(s) adjacent to each other */ + /* ignoring linesep immediately after colon. */ + c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + } + DBG_(("db: COLON\n")); + return COLON; + } + case ';': + DBG_(("db: SEMICOLON\n")); + return SEMICOLON; + case '=': + DBG_(("db: EQ\n")); + return EQ; + /* ignore whitespace in this mode */ + case '\t': + case ' ': continue; + case '\n': { + ++mime_lineNum; + continue; + } + case EOF: return 0; + break; + default: { + lexPushLookaheadc(c); + if (isalnum(c)) { + char *t = lexGetWord(); + yylval.str = t; + if (!qstricmp(t, "begin")) { + return match_begin_end_name(0); + } + else if (!qstricmp(t,"end")) { + return match_begin_end_name(1); + } + else { + DBG_(("db: ID '%s'\n", t)); + return ID; + } + } + else { + /* unknow token */ + return 0; + } + break; + } + } + } + } + return 0; + } + + +/***************************************************************************/ +/*** Public Functions ****/ +/***************************************************************************/ + +static VObject* Parse_MIMEHelper() + { + ObjStackTop = -1; + mime_numErrors = 0; + mime_lineNum = 1; + vObjList = 0; + curObj = 0; + + if (yyparse() != 0) + return 0; + + finiLex(); + return vObjList; + } + +/*--------------------------------------------*/ +DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len) + { + initLex(input, len, 0); + return Parse_MIMEHelper(); + } + + +#if INCLUDEMFC + +DLLEXPORT(VObject*) Parse_MIME_FromFile(CFile *file) + { + unsigned long startPos; + VObject *result; + + initLex(0,-1,file); + startPos = file->GetPosition(); + if (!(result = Parse_MIMEHelper())) + file->Seek(startPos, CFile::begin); + return result; + } + +#else + +VObject* Parse_MIME_FromFile(FILE *file) + { + VObject *result; + long startPos; + + initLex(0,(unsigned long)-1,file); + startPos = ftell(file); + if (!(result = Parse_MIMEHelper())) { + fseek(file,startPos,SEEK_SET); + } + return result; + } + +DLLEXPORT(VObject*) Parse_MIME_FromFileName(char *fname) + { + FILE *fp = fopen(fname,"r"); + if (fp) { + VObject* o = Parse_MIME_FromFile(fp); + fclose(fp); + return o; + } + else { + char msg[80]; + sprintf(msg, "can't open file '%s' for reading\n", fname); + mime_error_(msg); + return 0; + } + } + +#endif + +/*-------------------------------------*/ + +static MimeErrorHandler mimeErrorHandler; + +DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler me) + { + mimeErrorHandler = me; + } + +void mime_error(char *s) + { + char msg[256]; + if (mimeErrorHandler) { + sprintf(msg,"%s at line %d", s, mime_lineNum); + mimeErrorHandler(msg); + } + } + +void mime_error_(char *s) + { + if (mimeErrorHandler) { + mimeErrorHandler(s); + } + } + diff --git a/library/backend/vobject.cpp b/library/backend/vobject.cpp new file mode 100644 index 0000000..af112a7 --- a/dev/null +++ b/library/backend/vobject.cpp @@ -0,0 +1,1210 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vobject.c + * doc: vobject and APIs to construct vobject, APIs pretty print + * vobject, and convert a vobject into its textual representation. + */ + +#ifndef MWERKS +#include +#endif + +#include "vobject_p.h" +#include "qfiledirect_p.h" +#include +#include +#include +//#include + + +#define NAME_OF(o) o->id +#define VALUE_TYPE(o) o->valType +#define STRINGZ_VALUE_OF(o) o->val.strs +#define INTEGER_VALUE_OF(o) o->val.i +#define LONG_VALUE_OF(o) o->val.l +#define ANY_VALUE_OF(o) o->val.any +#define VOBJECT_VALUE_OF(o) o->val.vobj + +typedef union ValueItem { + const char *strs; + unsigned int i; + unsigned long l; + void *any; + VObject *vobj; + } ValueItem; + +struct VObject { + VObject *next; + const char *id; + VObject *prop; + unsigned short valType; + ValueItem val; + }; + +typedef struct StrItem StrItem; + +struct StrItem { + StrItem *next; + const char *s; + unsigned int refCnt; + }; + +const char** fieldedProp; + + + +/*---------------------------------------------------------------------- + The following functions involve with memory allocation: + newVObject + deleteVObject + dupStr + deleteStr + newStrItem + deleteStrItem + ----------------------------------------------------------------------*/ + +DLLEXPORT(VObject*) newVObject_(const char *id) +{ + VObject *p = (VObject*)malloc(sizeof(VObject)); + p->next = 0; + p->id = id; + p->prop = 0; + VALUE_TYPE(p) = 0; + ANY_VALUE_OF(p) = 0; + return p; +} + +DLLEXPORT(VObject*) newVObject(const char *id) +{ + return newVObject_(lookupStr(id)); +} + +DLLEXPORT(void) deleteVObject(VObject *p) +{ + unUseStr(p->id); + free(p); +} + +DLLEXPORT(char*) dupStr(const char *s, unsigned int size) +{ + char *t; + if (size == 0) { + size = strlen(s); + } + t = (char*)malloc(size+1); + if (t) { + memcpy(t,s,size); + t[size] = 0; + return t; + } + else { + return (char*)0; + } +} + +DLLEXPORT(void) deleteStr(const char *p) +{ + if (p) free((void*)p); +} + + +static StrItem* newStrItem(const char *s, StrItem *next) +{ + StrItem *p = (StrItem*)malloc(sizeof(StrItem)); + p->next = next; + p->s = s; + p->refCnt = 1; + return p; +} + +static void deleteStrItem(StrItem *p) +{ + free((void*)p); +} + + +/*---------------------------------------------------------------------- + The following function provide accesses to VObject's value. + ----------------------------------------------------------------------*/ + +DLLEXPORT(const char*) vObjectName(VObject *o) +{ + return NAME_OF(o); +} + +DLLEXPORT(void) setVObjectName(VObject *o, const char* id) +{ + NAME_OF(o) = id; +} + +DLLEXPORT(const char*) vObjectStringZValue(VObject *o) +{ + return STRINGZ_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s) +{ + STRINGZ_VALUE_OF(o) = dupStr(s,0); + VALUE_TYPE(o) = VCVT_STRINGZ; +} + +DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s) +{ + STRINGZ_VALUE_OF(o) = s; + VALUE_TYPE(o) = VCVT_STRINGZ; +} + +DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o) +{ + return INTEGER_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i) +{ + INTEGER_VALUE_OF(o) = i; + VALUE_TYPE(o) = VCVT_UINT; +} + +DLLEXPORT(unsigned long) vObjectLongValue(VObject *o) +{ + return LONG_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l) +{ + LONG_VALUE_OF(o) = l; + VALUE_TYPE(o) = VCVT_ULONG; +} + +DLLEXPORT(void*) vObjectAnyValue(VObject *o) +{ + return ANY_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t) +{ + ANY_VALUE_OF(o) = t; + VALUE_TYPE(o) = VCVT_RAW; +} + +DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o) +{ + return VOBJECT_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p) +{ + VOBJECT_VALUE_OF(o) = p; + VALUE_TYPE(o) = VCVT_VOBJECT; +} + +DLLEXPORT(int) vObjectValueType(VObject *o) +{ + return VALUE_TYPE(o); +} + + +/*---------------------------------------------------------------------- + The following functions can be used to build VObject. + ----------------------------------------------------------------------*/ + +DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p) +{ + /* circular link list pointed to tail */ + /* + o {next,id,prop,val} + V + pn {next,id,prop,val} + V + ... + p1 {next,id,prop,val} + V + pn + --> + o {next,id,prop,val} + V + pn {next,id,prop,val} + V + p {next,id,prop,val} + ... + p1 {next,id,prop,val} + V + pn + */ + + VObject *tail = o->prop; + if (tail) { + p->next = tail->next; + o->prop = tail->next = p; + } + else { + o->prop = p->next = p; + } + return p; +} + +DLLEXPORT(VObject*) addProp(VObject *o, const char *id) +{ + return addVObjectProp(o,newVObject(id)); +} + +DLLEXPORT(VObject*) addProp_(VObject *o, const char *id) +{ + return addVObjectProp(o,newVObject_(id)); +} + +DLLEXPORT(void) addList(VObject **o, VObject *p) +{ + p->next = 0; + if (*o == 0) { + *o = p; + } + else { + VObject *t = *o; + while (t->next) { + t = t->next; + } + t->next = p; + } +} + +DLLEXPORT(VObject*) nextVObjectInList(VObject *o) +{ + return o->next; +} + +DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size) +{ + VObject *sizeProp; + setVObjectAnyValue(prop, val); + sizeProp = addProp(prop,VCDataSizeProp); + setVObjectLongValue(sizeProp, size); + return prop; +} + +DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size) +{ + void *p = dupStr((const char *)val,size); + return setValueWithSize_(prop,p,p?size:0); +} + +DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o) +{ + i->start = o->prop; + i->next = 0; +} + +DLLEXPORT(void) initVObjectIterator(VObjectIterator *i, VObject *o) +{ + i->start = o->next; + i->next = 0; +} + +DLLEXPORT(int) moreIteration(VObjectIterator *i) +{ + return (i->start && (i->next==0 || i->next!=i->start)); +} + +DLLEXPORT(VObject*) nextVObject(VObjectIterator *i) +{ + if (i->start && i->next != i->start) { + if (i->next == 0) { + i->next = i->start->next; + return i->next; + } + else { + i->next = i->next->next; + return i->next; + } + } + else return (VObject*)0; +} + +DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id) +{ + VObjectIterator i; + initPropIterator(&i,o); + while (moreIteration(&i)) { + VObject *each = nextVObject(&i); + if (!qstricmp(id,each->id)) + return each; + } + return (VObject*)0; +} + +DLLEXPORT(VObject*) addGroup(VObject *o, const char *g) +{ + /* + a.b.c + --> + prop(c) + prop(VCGrouping=b) + prop(VCGrouping=a) + */ + char *dot = strrchr(g,'.'); + if (dot) { + VObject *p, *t; + char *gs, *n = dot+1; + gs = dupStr(g,0); /* so we can write to it. */ + /* used to be + * t = p = addProp_(o,lookupProp_(n)); + */ + t = p = addProp_(o,lookupProp(n)); + dot = strrchr(gs,'.'); + *dot = 0; + do { + dot = strrchr(gs,'.'); + if (dot) { + n = dot+1; + *dot=0; + } + else + n = gs; + /* property(VCGroupingProp=n); + * and the value may have VCGrouping property + */ + t = addProp(t,VCGroupingProp); + setVObjectStringZValue(t,lookupProp_(n)); + } while (n != gs); + deleteStr(gs); + return p; + } + else + return addProp_(o,lookupProp(g)); +} + +DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v) +{ + VObject *prop; + prop = addProp(o,p); + setVObjectStringZValue_(prop, strdup( v ) ); + return prop; +} + +DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v, + unsigned int size) +{ + VObject *prop; + prop = addProp(o,p); + setValueWithSize_(prop, (void*)v, size); + return prop; +} + +DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v, + unsigned int size) +{ + return addPropSizedValue_(o,p,dupStr(v,size),size); +} + + +DLLEXPORT(void) cleanVObject(VObject *o) +{ + if (o == 0) return; + if (o->prop) { + /* destroy time: cannot use the iterator here. + Have to break the cycle in the circular link + list and turns it into regular NULL-terminated + list -- since at some point of destruction, + the reference entry for the iterator to work + will not longer be valid. + */ + VObject *p; + p = o->prop->next; + o->prop->next = 0; + do { + VObject *t = p->next; + cleanVObject(p); + p = t; + } while (p); + } + switch (VALUE_TYPE(o)) { + case VCVT_STRINGZ: + case VCVT_RAW: + // assume they are all allocated by malloc. + free((char*)STRINGZ_VALUE_OF(o)); + break; + case VCVT_VOBJECT: + cleanVObject(VOBJECT_VALUE_OF(o)); + break; + } + deleteVObject(o); +} + +DLLEXPORT(void) cleanVObjects(VObject *list) +{ + while (list) { + VObject *t = list; + list = nextVObjectInList(list); + cleanVObject(t); + } +} + +/*---------------------------------------------------------------------- + The following is a String Table Facilities. + ----------------------------------------------------------------------*/ + +#define STRTBLSIZE 255 + +static StrItem *strTbl[STRTBLSIZE]; + +static unsigned int hashStr(const char *s) +{ + unsigned int h = 0; + int i; + for (i=0;s[i];i++) { + h += s[i]*i; + } + return h % STRTBLSIZE; +} + +DLLEXPORT(const char*) lookupStr(const char *s) +{ + StrItem *t; + unsigned int h = hashStr(s); + if ((t = strTbl[h]) != 0) { + do { + if (qstricmp(t->s,s) == 0) { + t->refCnt++; + return t->s; + } + t = t->next; + } while (t); + } + s = dupStr(s,0); + strTbl[h] = newStrItem(s,strTbl[h]); + return s; +} + +DLLEXPORT(void) unUseStr(const char *s) +{ + StrItem *t, *p; + unsigned int h = hashStr(s); + if ((t = strTbl[h]) != 0) { + p = t; + do { + if (qstricmp(t->s,s) == 0) { + t->refCnt--; + if (t->refCnt == 0) { + if (p == strTbl[h]) { + strTbl[h] = t->next; + } + else { + p->next = t->next; + } + deleteStr(t->s); + deleteStrItem(t); + return; + } + } + p = t; + t = t->next; + } while (t); + } +} + +DLLEXPORT(void) cleanStrTbl() +{ + int i; + for (i=0; is); + p = t; + t = t->next; + deleteStrItem(p); + } while (t); + strTbl[i] = 0; + } +} + + +struct PreDefProp { + const char *name; + const char *alias; + const char** fields; + unsigned int flags; + }; + +/* flags in PreDefProp */ +#define PD_BEGIN 0x1 +#define PD_INTERNAL 0x2 + +static const char *adrFields[] = { + VCPostalBoxProp, + VCExtAddressProp, + VCStreetAddressProp, + VCCityProp, + VCRegionProp, + VCPostalCodeProp, + VCCountryNameProp, + 0 +}; + +static const char *nameFields[] = { + VCFamilyNameProp, + VCGivenNameProp, + VCAdditionalNamesProp, + VCNamePrefixesProp, + VCNameSuffixesProp, + NULL + }; + +static const char *orgFields[] = { + VCOrgNameProp, + VCOrgUnitProp, + VCOrgUnit2Prop, + VCOrgUnit3Prop, + VCOrgUnit4Prop, + NULL + }; + +static const char *AAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCAudioContentProp, + 0 + }; + +/* ExDate -- has unamed fields */ +/* RDate -- has unamed fields */ + +static const char *DAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCDisplayStringProp, + 0 + }; + +static const char *MAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCEmailAddressProp, + VCNoteProp, + 0 + }; + +static const char *PAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCProcedureNameProp, + 0 + }; + +static struct PreDefProp propNames[] = { + { VC7bitProp, 0, 0, 0 }, + { VC8bitProp, 0, 0, 0 }, + { VCAAlarmProp, 0, AAlarmFields, 0 }, + { VCAdditionalNamesProp, 0, 0, 0 }, + { VCAdrProp, 0, adrFields, 0 }, + { VCAgentProp, 0, 0, 0 }, + { VCAIFFProp, 0, 0, 0 }, + { VCAOLProp, 0, 0, 0 }, + { VCAppleLinkProp, 0, 0, 0 }, + { VCAttachProp, 0, 0, 0 }, + { VCAttendeeProp, 0, 0, 0 }, + { VCATTMailProp, 0, 0, 0 }, + { VCAudioContentProp, 0, 0, 0 }, + { VCAVIProp, 0, 0, 0 }, + { VCBase64Prop, 0, 0, 0 }, + { VCBBSProp, 0, 0, 0 }, + { VCBirthDateProp, 0, 0, 0 }, + { VCBMPProp, 0, 0, 0 }, + { VCBodyProp, 0, 0, 0 }, + { VCBusinessRoleProp, 0, 0, 0 }, + { VCCalProp, 0, 0, PD_BEGIN }, + { VCCaptionProp, 0, 0, 0 }, + { VCCardProp, 0, 0, PD_BEGIN }, + { VCCarProp, 0, 0, 0 }, + { VCCategoriesProp, 0, 0, 0 }, + { VCCellularProp, 0, 0, 0 }, + { VCCGMProp, 0, 0, 0 }, + { VCCharSetProp, 0, 0, 0 }, + { VCCIDProp, VCContentIDProp, 0, 0 }, + { VCCISProp, 0, 0, 0 }, + { VCCityProp, 0, 0, 0 }, + { VCClassProp, 0, 0, 0 }, + { VCCommentProp, 0, 0, 0 }, + { VCCompletedProp, 0, 0, 0 }, + { VCContentIDProp, 0, 0, 0 }, + { VCCountryNameProp, 0, 0, 0 }, + { VCDAlarmProp, 0, DAlarmFields, 0 }, + { VCDataSizeProp, 0, 0, PD_INTERNAL }, + { VCDayLightProp, 0, 0, 0 }, + { VCDCreatedProp, 0, 0, 0 }, + { VCDeliveryLabelProp, 0, 0, 0 }, + { VCDescriptionProp, 0, 0, 0 }, + { VCDIBProp, 0, 0, 0 }, + { VCDisplayStringProp, 0, 0, 0 }, + { VCDomesticProp, 0, 0, 0 }, + { VCDTendProp, 0, 0, 0 }, + { VCDTstartProp, 0, 0, 0 }, + { VCDueProp, 0, 0, 0 }, + { VCEmailAddressProp, 0, 0, 0 }, + { VCEncodingProp, 0, 0, 0 }, + { VCEndProp, 0, 0, 0 }, + { VCEventProp, 0, 0, PD_BEGIN }, + { VCEWorldProp, 0, 0, 0 }, + { VCExNumProp, 0, 0, 0 }, + { VCExpDateProp, 0, 0, 0 }, + { VCExpectProp, 0, 0, 0 }, + { VCExtAddressProp, 0, 0, 0 }, + { VCFamilyNameProp, 0, 0, 0 }, + { VCFaxProp, 0, 0, 0 }, + { VCFullNameProp, 0, 0, 0 }, + { VCGeoLocationProp, 0, 0, 0 }, + { VCGeoProp, 0, 0, 0 }, + { VCGIFProp, 0, 0, 0 }, + { VCGivenNameProp, 0, 0, 0 }, + { VCGroupingProp, 0, 0, 0 }, + { VCHomeProp, 0, 0, 0 }, + { VCIBMMailProp, 0, 0, 0 }, + { VCInlineProp, 0, 0, 0 }, + { VCInternationalProp, 0, 0, 0 }, + { VCInternetProp, 0, 0, 0 }, + { VCISDNProp, 0, 0, 0 }, + { VCJPEGProp, 0, 0, 0 }, + { VCLanguageProp, 0, 0, 0 }, + { VCLastModifiedProp, 0, 0, 0 }, + { VCLastRevisedProp, 0, 0, 0 }, + { VCLocationProp, 0, 0, 0 }, + { VCLogoProp, 0, 0, 0 }, + { VCMailerProp, 0, 0, 0 }, + { VCMAlarmProp, 0, MAlarmFields, 0 }, + { VCMCIMailProp, 0, 0, 0 }, + { VCMessageProp, 0, 0, 0 }, + { VCMETProp, 0, 0, 0 }, + { VCModemProp, 0, 0, 0 }, + { VCMPEG2Prop, 0, 0, 0 }, + { VCMPEGProp, 0, 0, 0 }, + { VCMSNProp, 0, 0, 0 }, + { VCNamePrefixesProp, 0, 0, 0 }, + { VCNameProp, 0, nameFields, 0 }, + { VCNameSuffixesProp, 0, 0, 0 }, + { VCNoteProp, 0, 0, 0 }, + { VCOrgNameProp, 0, 0, 0 }, + { VCOrgProp, 0, orgFields, 0 }, + { VCOrgUnit2Prop, 0, 0, 0 }, + { VCOrgUnit3Prop, 0, 0, 0 }, + { VCOrgUnit4Prop, 0, 0, 0 }, + { VCOrgUnitProp, 0, 0, 0 }, + { VCPagerProp, 0, 0, 0 }, + { VCPAlarmProp, 0, PAlarmFields, 0 }, + { VCParcelProp, 0, 0, 0 }, + { VCPartProp, 0, 0, 0 }, + { VCPCMProp, 0, 0, 0 }, + { VCPDFProp, 0, 0, 0 }, + { VCPGPProp, 0, 0, 0 }, + { VCPhotoProp, 0, 0, 0 }, + { VCPICTProp, 0, 0, 0 }, + { VCPMBProp, 0, 0, 0 }, + { VCPostalBoxProp, 0, 0, 0 }, + { VCPostalCodeProp, 0, 0, 0 }, + { VCPostalProp, 0, 0, 0 }, + { VCPowerShareProp, 0, 0, 0 }, + { VCPreferredProp, 0, 0, 0 }, + { VCPriorityProp, 0, 0, 0 }, + { VCProcedureNameProp, 0, 0, 0 }, + { VCProdIdProp, 0, 0, 0 }, + { VCProdigyProp, 0, 0, 0 }, + { VCPronunciationProp, 0, 0, 0 }, + { VCPSProp, 0, 0, 0 }, + { VCPublicKeyProp, 0, 0, 0 }, + { VCQPProp, VCQuotedPrintableProp, 0, 0 }, + { VCQuickTimeProp, 0, 0, 0 }, + { VCQuotedPrintableProp, 0, 0, 0 }, + { VCRDateProp, 0, 0, 0 }, + { VCRegionProp, 0, 0, 0 }, + { VCRelatedToProp, 0, 0, 0 }, + { VCRepeatCountProp, 0, 0, 0 }, + { VCResourcesProp, 0, 0, 0 }, + { VCRNumProp, 0, 0, 0 }, + { VCRoleProp, 0, 0, 0 }, + { VCRRuleProp, 0, 0, 0 }, + { VCRSVPProp, 0, 0, 0 }, + { VCRunTimeProp, 0, 0, 0 }, + { VCSequenceProp, 0, 0, 0 }, + { VCSnoozeTimeProp, 0, 0, 0 }, + { VCStartProp, 0, 0, 0 }, + { VCStatusProp, 0, 0, 0 }, + { VCStreetAddressProp, 0, 0, 0 }, + { VCSubTypeProp, 0, 0, 0 }, + { VCSummaryProp, 0, 0, 0 }, + { VCTelephoneProp, 0, 0, 0 }, + { VCTIFFProp, 0, 0, 0 }, + { VCTimeZoneProp, 0, 0, 0 }, + { VCTitleProp, 0, 0, 0 }, + { VCTLXProp, 0, 0, 0 }, + { VCTodoProp, 0, 0, PD_BEGIN }, + { VCTranspProp, 0, 0, 0 }, + { VCUniqueStringProp, 0, 0, 0 }, + { VCURLProp, 0, 0, 0 }, + { VCURLValueProp, 0, 0, 0 }, + { VCValueProp, 0, 0, 0 }, + { VCVersionProp, 0, 0, 0 }, + { VCVideoProp, 0, 0, 0 }, + { VCVoiceProp, 0, 0, 0 }, + { VCWAVEProp, 0, 0, 0 }, + { VCWMFProp, 0, 0, 0 }, + { VCWorkProp, 0, 0, 0 }, + { VCX400Prop, 0, 0, 0 }, + { VCX509Prop, 0, 0, 0 }, + { VCXRuleProp, 0, 0, 0 }, + { 0,0,0,0 } + }; + + +static struct PreDefProp* lookupPropInfo(const char* str) +{ + /* brute force for now, could use a hash table here. */ + int i; + + for (i = 0; propNames[i].name; i++) + if (qstricmp(str, propNames[i].name) == 0) { + return &propNames[i]; + } + + return 0; +} + + +DLLEXPORT(const char*) lookupProp_(const char* str) +{ + int i; + + for (i = 0; propNames[i].name; i++) + if (qstricmp(str, propNames[i].name) == 0) { + const char* s; + s = propNames[i].alias?propNames[i].alias:propNames[i].name; + return lookupStr(s); + } + return lookupStr(str); +} + + +DLLEXPORT(const char*) lookupProp(const char* str) +{ + int i; + + for (i = 0; propNames[i].name; i++) + if (qstricmp(str, propNames[i].name) == 0) { + const char *s; + fieldedProp = propNames[i].fields; + s = propNames[i].alias?propNames[i].alias:propNames[i].name; + return lookupStr(s); + } + fieldedProp = 0; + return lookupStr(str); +} + + +/*---------------------------------------------------------------------- + APIs to Output text form. + ----------------------------------------------------------------------*/ +#define OFILE_REALLOC_SIZE 256 +typedef struct OFile { + FILE *fp; + char *s; + int len; + int limit; + int alloc:1; + int fail:1; + } OFile; + +#if 0 +static void appendsOFile(OFile *fp, const char *s) +{ + int slen; + if (fp->fail) return; + slen = strlen(s); + if (fp->fp) { + fwrite(s,1,slen,fp->fp); + } + else { +stuff: + if (fp->len + slen < fp->limit) { + memcpy(fp->s+fp->len,s,slen); + fp->len += slen; + return; + } + else if (fp->alloc) { + fp->limit = fp->limit + OFILE_REALLOC_SIZE; + if (OFILE_REALLOC_SIZE <= slen) fp->limit += slen; + fp->s = (char *) realloc(fp->s,fp->limit); + if (fp->s) goto stuff; + } + if (fp->alloc) + free(fp->s); + fp->s = 0; + fp->fail = 1; + } +} + +static void appendcOFile(OFile *fp, char c) +{ + if (fp->fail) return; + if (fp->fp) { + fputc(c,fp->fp); + } + else { +stuff: + if (fp->len+1 < fp->limit) { + fp->s[fp->len] = c; + fp->len++; + return; + } + else if (fp->alloc) { + fp->limit = fp->limit + OFILE_REALLOC_SIZE; + fp->s = (char *) realloc(fp->s,fp->limit); + if (fp->s) goto stuff; + } + if (fp->alloc) + free(fp->s); + fp->s = 0; + fp->fail = 1; + } +} +#else +static void appendcOFile_(OFile *fp, char c) +{ + if (fp->fail) return; + if (fp->fp) { + fputc(c,fp->fp); + } + else { +stuff: + if (fp->len+1 < fp->limit) { + fp->s[fp->len] = c; + fp->len++; + return; + } + else if (fp->alloc) { + fp->limit = fp->limit + OFILE_REALLOC_SIZE; + fp->s = (char *)realloc(fp->s,fp->limit); + if (fp->s) goto stuff; + } + if (fp->alloc) + free(fp->s); + fp->s = 0; + fp->fail = 1; + } +} + +static void appendcOFile(OFile *fp, char c) +{ + if (c == '\n') { + /* write out as */ + appendcOFile_(fp,0xd); + appendcOFile_(fp,0xa); + } + else + appendcOFile_(fp,c); +} + +static void appendsOFile(OFile *fp, const char *s) +{ + int i, slen; + slen = strlen(s); + for (i=0; ifp = ofp; + fp->s = 0; + fp->len = 0; + fp->limit = 0; + fp->alloc = 0; + fp->fail = 0; +} + +static int writeBase64(OFile *fp, unsigned char *s, long len) +{ + long cur = 0; + int i, numQuads = 0; + unsigned long trip; + unsigned char b; + char quad[5]; +#define MAXQUADS 16 + + quad[4] = 0; + + while (cur < len) { + // collect the triplet of bytes into 'trip' + trip = 0; + for (i = 0; i < 3; i++) { + b = (cur < len) ? *(s + cur) : 0; + cur++; + trip = trip << 8 | b; + } + // fill in 'quad' with the appropriate four characters + for (i = 3; i >= 0; i--) { + b = (unsigned char)(trip & 0x3F); + trip = trip >> 6; + if ((3 - i) < (cur - len)) + quad[i] = '='; // pad char + else if (b < 26) quad[i] = (char)b + 'A'; + else if (b < 52) quad[i] = (char)(b - 26) + 'a'; + else if (b < 62) quad[i] = (char)(b - 52) + '0'; + else if (b == 62) quad[i] = '+'; + else quad[i] = '/'; + } + // now output 'quad' with appropriate whitespace and line ending + appendsOFile(fp, (numQuads == 0 ? " " : "")); + appendsOFile(fp, quad); + appendsOFile(fp, ((cur >= len)?"\n" :(numQuads==MAXQUADS-1?"\n" : ""))); + numQuads = (numQuads + 1) % MAXQUADS; + } + appendcOFile(fp,'\n'); + + return 1; +} + +static void writeQPString(OFile *fp, const char *s) +{ + const char *p = s; + while (*p) { + if (*p == '\n') { + if (p[1]) appendsOFile(fp,"=0A="); + } + appendcOFile(fp,*p); + p++; + } +} + + + +static void writeVObject_(OFile *fp, VObject *o); + +static void writeValue(OFile *fp, VObject *o, unsigned long size) +{ + if (o == 0) return; + switch (VALUE_TYPE(o)) { + case VCVT_STRINGZ: { + writeQPString(fp, STRINGZ_VALUE_OF(o)); + break; + } + case VCVT_UINT: { + char buf[16]; + sprintf(buf,"%u", INTEGER_VALUE_OF(o)); + appendsOFile(fp,buf); + break; + } + case VCVT_ULONG: { + char buf[16]; + sprintf(buf,"%lu", LONG_VALUE_OF(o)); + appendsOFile(fp,buf); + break; + } + case VCVT_RAW: { + appendcOFile(fp,'\n'); + writeBase64(fp,(unsigned char*)(ANY_VALUE_OF(o)),size); + break; + } + case VCVT_VOBJECT: + appendcOFile(fp,'\n'); + writeVObject_(fp,VOBJECT_VALUE_OF(o)); + break; + } +} + +static void writeAttrValue(OFile *fp, VObject *o) +{ + if (NAME_OF(o)) { + struct PreDefProp *pi; + pi = lookupPropInfo(NAME_OF(o)); + if (pi && ((pi->flags & PD_INTERNAL) != 0)) return; + appendcOFile(fp,';'); + appendsOFile(fp,NAME_OF(o)); + } + else + appendcOFile(fp,';'); + if (VALUE_TYPE(o)) { + appendcOFile(fp,'='); + writeValue(fp,o,0); + } +} + +static void writeGroup(OFile *fp, VObject *o) +{ + char buf1[256]; + char buf2[256]; + strcpy(buf1,NAME_OF(o)); + while ((o=isAPropertyOf(o,VCGroupingProp)) != 0) { + strcpy(buf2,STRINGZ_VALUE_OF(o)); + strcat(buf2,"."); + strcat(buf2,buf1); + strcpy(buf1,buf2); + } + appendsOFile(fp,buf1); +} + +static int inList(const char **list, const char *s) +{ + if (list == 0) return 0; + while (*list) { + if (qstricmp(*list,s) == 0) return 1; + list++; + } + return 0; +} + +static void writeProp(OFile *fp, VObject *o) +{ + if (NAME_OF(o)) { + struct PreDefProp *pi; + VObjectIterator t; + const char **fields_ = 0; + pi = lookupPropInfo(NAME_OF(o)); + if (pi && ((pi->flags & PD_BEGIN) != 0)) { + writeVObject_(fp,o); + return; + } + if (isAPropertyOf(o,VCGroupingProp)) + writeGroup(fp,o); + else + appendsOFile(fp,NAME_OF(o)); + if (pi) fields_ = pi->fields; + initPropIterator(&t,o); + while (moreIteration(&t)) { + const char *s; + VObject *eachProp = nextVObject(&t); + s = NAME_OF(eachProp); + if (qstricmp(VCGroupingProp,s) && !inList(fields_,s)) + writeAttrValue(fp,eachProp); + } + if (fields_) { + int i = 0, n = 0; + const char** fields = fields_; + /* output prop as fields */ + appendcOFile(fp,':'); + while (*fields) { + VObject *t = isAPropertyOf(o,*fields); + i++; + if (t) n = i; + fields++; + } + fields = fields_; + for (i=0;iflags & PD_BEGIN) != 0)) { + VObjectIterator t; + const char *begin = NAME_OF(o); + appendsOFile(fp,"BEGIN:"); + appendsOFile(fp,begin); + appendcOFile(fp,'\n'); + initPropIterator(&t,o); + while (moreIteration(&t)) { + VObject *eachProp = nextVObject(&t); + writeProp(fp, eachProp); + } + appendsOFile(fp,"END:"); + appendsOFile(fp,begin); + appendsOFile(fp,"\n\n"); + } + } +} + +void writeVObject(FILE *fp, VObject *o) +{ + OFile ofp; + // ##### + //_setmode(_fileno(fp), _O_BINARY); + initOFile(&ofp,fp); + writeVObject_(&ofp,o); +} + +DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o) +{ + QFileDirect f( fname); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vobject write %s", fname); + return; + } + + writeVObject( f.directHandle(),o ); +} + +DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list) +{ + QFileDirect f( fname); + if ( !f.open( IO_WriteOnly ) ) { + qWarning("Unable to open vobject write %s", fname); + return; + } + + while (list) { + writeVObject(f.directHandle(),list); + list = nextVObjectInList(list); + } +} + +// end of source file vobject.c diff --git a/library/backend/vobject_p.h b/library/backend/vobject_p.h new file mode 100644 index 0000000..b6a2c0a --- a/dev/null +++ b/library/backend/vobject_p.h @@ -0,0 +1,401 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + +The vCard/vCalendar C interface is implemented in the set +of files as follows: + +vcc.y, yacc source, and vcc.c, the yacc output you will use +implements the core parser + +vobject.c implements an API that insulates the caller from +the parser and changes in the vCard/vCalendar BNF + +port.h defines compilation environment dependent stuff + +vcc.h and vobject.h are header files for their .c counterparts + +vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions +which you may find useful. + +test.c is a standalone test driver that exercises some of +the features of the APIs provided. Invoke test.exe on a +VCARD/VCALENDAR input text file and you will see the pretty +print output of the internal representation (this pretty print +output should give you a good idea of how the internal +representation looks like -- there is one such output in the +following too). Also, a file with the .out suffix is generated +to show that the internal representation can be written back +in the original text format. + +For more information on this API see the readme.txt file +which accompanied this distribution. + + Also visit: + + http://www.versit.com + http://www.ralden.com + +*/ + + +#ifndef __VOBJECT_H__ +#define __VOBJECT_H__ 1 + +#include + +#define vCardClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCard" +#define vCalendarClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCalendar" + +/* The above strings vCardClipboardFormat and vCalendarClipboardFormat +are globally unique IDs which can be used to generate clipboard format +ID's as per the requirements of a specific platform. For example, in +Windows they are used as the parameter in a call to RegisterClipboardFormat. +For example: + + CLIPFORMAT foo = RegisterClipboardFormat(vCardClipboardFormat); + +*/ + +#define vCardMimeType "text/x-vCard" +#define vCalendarMimeType "text/x-vCalendar" + +#undef DLLEXPORT +#include +#if defined(Q_WS_WIN) +#define DLLEXPORT(t) __declspec(dllexport) t +#else +#define DLLEXPORT(t) t +#endif + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#include +#include + + +#define VC7bitProp "7BIT" +#define VC8bitProp "8BIT" +#define VCAAlarmProp "AALARM" +#define VCAdditionalNamesProp "ADDN" +#define VCAdrProp "ADR" +#define VCAgentProp "AGENT" +#define VCAIFFProp "AIFF" +#define VCAOLProp "AOL" +#define VCAppleLinkProp "APPLELINK" +#define VCAttachProp "ATTACH" +#define VCAttendeeProp "ATTENDEE" +#define VCATTMailProp "ATTMAIL" +#define VCAudioContentProp "AUDIOCONTENT" +#define VCAVIProp "AVI" +#define VCBase64Prop "BASE64" +#define VCBBSProp "BBS" +#define VCBirthDateProp "BDAY" +#define VCBMPProp "BMP" +#define VCBodyProp "BODY" +#define VCBusinessRoleProp "ROLE" +#define VCCalProp "VCALENDAR" +#define VCCaptionProp "CAP" +#define VCCardProp "VCARD" +#define VCCarProp "CAR" +#define VCCategoriesProp "CATEGORIES" +#define VCCellularProp "CELL" +#define VCCGMProp "CGM" +#define VCCharSetProp "CS" +#define VCCIDProp "CID" +#define VCCISProp "CIS" +#define VCCityProp "L" +#define VCClassProp "CLASS" +#define VCCommentProp "NOTE" +#define VCCompletedProp "COMPLETED" +#define VCContentIDProp "CONTENT-ID" +#define VCCountryNameProp "C" +#define VCDAlarmProp "DALARM" +#define VCDataSizeProp "DATASIZE" +#define VCDayLightProp "DAYLIGHT" +#define VCDCreatedProp "DCREATED" +#define VCDeliveryLabelProp "LABEL" +#define VCDescriptionProp "DESCRIPTION" +#define VCDIBProp "DIB" +#define VCDisplayStringProp "DISPLAYSTRING" +#define VCDomesticProp "DOM" +#define VCDTendProp "DTEND" +#define VCDTstartProp "DTSTART" +#define VCDueProp "DUE" +#define VCEmailAddressProp "EMAIL" +#define VCEncodingProp "ENCODING" +#define VCEndProp "END" +#define VCEventProp "VEVENT" +#define VCEWorldProp "EWORLD" +#define VCExNumProp "EXNUM" +#define VCExpDateProp "EXDATE" +#define VCExpectProp "EXPECT" +#define VCExtAddressProp "EXT ADD" +#define VCFamilyNameProp "F" +#define VCFaxProp "FAX" +#define VCFullNameProp "FN" +#define VCGeoProp "GEO" +#define VCGeoLocationProp "GEO" +#define VCGIFProp "GIF" +#define VCGivenNameProp "G" +#define VCGroupingProp "Grouping" +#define VCHomeProp "HOME" +#define VCIBMMailProp "IBMMail" +#define VCInlineProp "INLINE" +#define VCInternationalProp "INTL" +#define VCInternetProp "INTERNET" +#define VCISDNProp "ISDN" +#define VCJPEGProp "JPEG" +#define VCLanguageProp "LANG" +#define VCLastModifiedProp "LAST-MODIFIED" +#define VCLastRevisedProp "REV" +#define VCLocationProp "LOCATION" +#define VCLogoProp "LOGO" +#define VCMailerProp "MAILER" +#define VCMAlarmProp "MALARM" +#define VCMCIMailProp "MCIMAIL" +#define VCMessageProp "MSG" +#define VCMETProp "MET" +#define VCModemProp "MODEM" +#define VCMPEG2Prop "MPEG2" +#define VCMPEGProp "MPEG" +#define VCMSNProp "MSN" +#define VCNamePrefixesProp "NPRE" +#define VCNameProp "N" +#define VCNameSuffixesProp "NSUF" +#define VCNoteProp "NOTE" +#define VCOrgNameProp "ORGNAME" +#define VCOrgProp "ORG" +#define VCOrgUnit2Prop "OUN2" +#define VCOrgUnit3Prop "OUN3" +#define VCOrgUnit4Prop "OUN4" +#define VCOrgUnitProp "OUN" +#define VCPagerProp "PAGER" +#define VCPAlarmProp "PALARM" +#define VCParcelProp "PARCEL" +#define VCPartProp "PART" +#define VCPCMProp "PCM" +#define VCPDFProp "PDF" +#define VCPGPProp "PGP" +#define VCPhotoProp "PHOTO" +#define VCPICTProp "PICT" +#define VCPMBProp "PMB" +#define VCPostalBoxProp "BOX" +#define VCPostalCodeProp "PC" +#define VCPostalProp "POSTAL" +#define VCPowerShareProp "POWERSHARE" +#define VCPreferredProp "PREF" +#define VCPriorityProp "PRIORITY" +#define VCProcedureNameProp "PROCEDURENAME" +#define VCProdIdProp "PRODID" +#define VCProdigyProp "PRODIGY" +#define VCPronunciationProp "SOUND" +#define VCPSProp "PS" +#define VCPublicKeyProp "KEY" +#define VCQPProp "QP" +#define VCQuickTimeProp "QTIME" +#define VCQuotedPrintableProp "QUOTED-PRINTABLE" +#define VCRDateProp "RDATE" +#define VCRegionProp "R" +#define VCRelatedToProp "RELATED-TO" +#define VCRepeatCountProp "REPEATCOUNT" +#define VCResourcesProp "RESOURCES" +#define VCRNumProp "RNUM" +#define VCRoleProp "ROLE" +#define VCRRuleProp "RRULE" +#define VCRSVPProp "RSVP" +#define VCRunTimeProp "RUNTIME" +#define VCSequenceProp "SEQUENCE" +#define VCSnoozeTimeProp "SNOOZETIME" +#define VCStartProp "START" +#define VCStatusProp "STATUS" +#define VCStreetAddressProp "STREET" +#define VCSubTypeProp "SUBTYPE" +#define VCSummaryProp "SUMMARY" +#define VCTelephoneProp "TEL" +#define VCTIFFProp "TIFF" +#define VCTimeZoneProp "TZ" +#define VCTitleProp "TITLE" +#define VCTLXProp "TLX" +#define VCTodoProp "VTODO" +#define VCTranspProp "TRANSP" +#define VCUniqueStringProp "UID" +#define VCURLProp "URL" +#define VCURLValueProp "URLVAL" +#define VCValueProp "VALUE" +#define VCVersionProp "VERSION" +#define VCVideoProp "VIDEO" +#define VCVoiceProp "VOICE" +#define VCWAVEProp "WAVE" +#define VCWMFProp "WMF" +#define VCWorkProp "WORK" +#define VCX400Prop "X400" +#define VCX509Prop "X509" +#define VCXRuleProp "XRULE" + + +typedef struct VObject VObject; + +typedef struct VObjectIterator { + VObject* start; + VObject* next; + } VObjectIterator; + +extern DLLEXPORT(VObject*) newVObject(const char *id); +extern DLLEXPORT(void) deleteVObject(VObject *p); +extern DLLEXPORT(char*) dupStr(const char *s, unsigned int size); +extern DLLEXPORT(void) deleteStr(const char *p); +extern DLLEXPORT(void) unUseStr(const char *s); + +extern DLLEXPORT(void) setVObjectName(VObject *o, const char* id); +extern DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s); +extern DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s); +extern DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i); +extern DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l); +extern DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t); +extern DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size); +extern DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size); + +extern DLLEXPORT(const char*) vObjectName(VObject *o); +extern DLLEXPORT(const char*) vObjectStringZValue(VObject *o); +extern DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o); +extern DLLEXPORT(unsigned long) vObjectLongValue(VObject *o); +extern DLLEXPORT(void*) vObjectAnyValue(VObject *o); +extern DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o); +extern DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p); + +extern DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p); +extern DLLEXPORT(VObject*) addProp(VObject *o, const char *id); +extern DLLEXPORT(VObject*) addProp_(VObject *o, const char *id); +extern DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v); +extern DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v, unsigned int size); +extern DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v, unsigned int size); +extern DLLEXPORT(VObject*) addGroup(VObject *o, const char *g); +extern DLLEXPORT(void) addList(VObject **o, VObject *p); + +extern DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id); + +extern DLLEXPORT(VObject*) nextVObjectInList(VObject *o); +extern DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o); +extern DLLEXPORT(int) moreIteration(VObjectIterator *i); +extern DLLEXPORT(VObject*) nextVObject(VObjectIterator *i); + +extern DLLEXPORT(const char*) lookupStr(const char *s); +extern DLLEXPORT(void) cleanStrTbl(); + +extern DLLEXPORT(void) cleanVObject(VObject *o); +extern DLLEXPORT(void) cleanVObjects(VObject *list); + +extern DLLEXPORT(const char*) lookupProp(const char* str); +extern DLLEXPORT(const char*) lookupProp_(const char* str); + +extern DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o); +extern DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list); + +extern DLLEXPORT(int) vObjectValueType(VObject *o); + +/* return type of vObjectValueType: */ +#define VCVT_NOVALUE 0 + /* if the VObject has no value associated with it. */ +#define VCVT_STRINGZ 1 + /* if the VObject has value set by setVObjectStringZValue. */ +#define VCVT_UINT 2 + /* if the VObject has value set by setVObjectIntegerValue. */ +#define VCVT_ULONG 3 + /* if the VObject has value set by setVObjectLongValue. */ +#define VCVT_RAW 4 + /* if the VObject has value set by setVObjectAnyValue. */ +#define VCVT_VOBJECT 5 + /* if the VObject has value set by setVObjectVObjectValue. */ + +extern const char** fieldedProp; + +/*************************************************** + * The methods below are implemented in vcc.c (generated from vcc.y ) + ***************************************************/ + +/* NOTE regarding printVObject and writeVObject + +The functions below are not exported from the DLL because they +take a FILE* as a parameter, which cannot be passed across a DLL +interface (at least that is my experience). Instead you can use +their companion functions which take file names or pointers +to memory. However, if you are linking this code into +your build directly then you may find them a more convenient API +and you can go ahead and use them. If you try to use them with +the DLL LIB you will get a link error. +*/ +extern void writeVObject(FILE *fp, VObject *o); + + + +typedef void (*MimeErrorHandler)(char *); + +extern DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler); + +extern DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len); +extern DLLEXPORT(VObject*) Parse_MIME_FromFileName(char* fname); + + +/* NOTE regarding Parse_MIME_FromFile +The function above, Parse_MIME_FromFile, comes in two flavors, +neither of which is exported from the DLL. Each version takes +a CFile or FILE* as a parameter, neither of which can be +passed across a DLL interface (at least that is my experience). +If you are linking this code into your build directly then +you may find them a more convenient API that the other flavors +that take a file name. If you use them with the DLL LIB you +will get a link error. +*/ + + +#if INCLUDEMFC +extern VObject* Parse_MIME_FromFile(CFile *file); +#else +extern VObject* Parse_MIME_FromFile(FILE *file); +#endif + +#endif /* __VOBJECT_H__ */ + + -- cgit v0.9.0.2