author | zecke <zecke> | 2003-04-16 10:57:59 (UTC) |
---|---|---|
committer | zecke <zecke> | 2003-04-16 10:57:59 (UTC) |
commit | eeb29547890a2d162de66d7d5b98d3840a7e2d79 (patch) (side-by-side diff) | |
tree | 973c6de7aae2fd5ba368efb87fa24234f0e12aa3 | |
parent | dfa2d294b3af09bf3757a3c2294a925093e72fc6 (diff) | |
download | opie-eeb29547890a2d162de66d7d5b98d3840a7e2d79.zip opie-eeb29547890a2d162de66d7d5b98d3840a7e2d79.tar.gz opie-eeb29547890a2d162de66d7d5b98d3840a7e2d79.tar.bz2 |
Fix API docu
-rw-r--r-- | library/applnk.cpp | 16 | ||||
-rw-r--r-- | library/backend/categories.cpp | 9 | ||||
-rw-r--r-- | library/backend/categories.h | 2 | ||||
-rw-r--r-- | library/backend/event.cpp | 4 | ||||
-rw-r--r-- | library/backend/palmtoprecord.h | 12 | ||||
-rw-r--r-- | library/datebookdb.cpp | 12 | ||||
-rw-r--r-- | library/finddialog.cpp | 7 | ||||
-rw-r--r-- | library/global.cpp | 28 | ||||
-rw-r--r-- | library/qpeapplication.cpp | 3 | ||||
-rw-r--r-- | library/storage.cpp | 6 |
10 files changed, 65 insertions, 34 deletions
diff --git a/library/applnk.cpp b/library/applnk.cpp index 35822dd..00030e8 100644 --- a/library/applnk.cpp +++ b/library/applnk.cpp @@ -1,1481 +1,1497 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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. ** **********************************************************************/ #define QTOPIA_INTERNAL_MIMEEXT #define QTOPIA_INTERNAL_PRELOADACCESS #define QTOPIA_INTERNAL_APPLNKASSIGN #include "applnk.h" #include <qpe/qpeapplication.h> #include <qpe/categories.h> #include <qpe/categoryselect.h> #include <qpe/qcopenvelope_qws.h> #include <qpe/global.h> #include <qpe/mimetype.h> #include <qpe/config.h> #include <qpe/storage.h> #include <qpe/resource.h> #include <qdict.h> #include <qdir.h> #include <qregexp.h> #include <qgfx_qws.h> #include <stdlib.h> int AppLnk::lastId = 5000; static int smallSize = 14; static int bigSize = 32; static QString safeFileName(const QString& n) { QString safename=n; safename.replace(QRegExp("[^0-9A-Za-z.]"),"_"); safename.replace(QRegExp("^[^A-Za-z]*"),""); if ( safename.isEmpty() ) safename = "_"; return safename; } static bool prepareDirectories(const QString& lf) { if ( !QFile::exists(lf) ) { // May need to create directories QFileInfo fi(lf); if ( system(("mkdir -p "+fi.dirPath(TRUE))) ) return FALSE; } return TRUE; } class AppLnkPrivate { public: /* the size of the Pixmap */ enum Size {Normal = 0, Big }; AppLnkPrivate() { /* we want one normal and one big item */ QPixmap pix; mPixmaps.insert(0, pix ); mPixmaps.insert(1, pix); } QStringList mCatList; // always correct QArray<int> mCat; // cached value; correct if not empty QMap<int, QPixmap> mPixmaps; void updateCatListFromArray() { Categories cat( 0 ); cat.load( categoryFileName() ); // we need to update the names for the mCat... to mCatList mCatList.clear(); for (uint i = 0; i < mCat.count(); i++ ) mCatList << cat.label("Document View", mCat[i] ); } void setCatArrayDirty() { mCat.resize(0); } void ensureCatArray() { if ( mCat.count() > 0 || mCatList.count()==0 ) return; Categories cat( 0 ); cat.load( categoryFileName() ); mCat.resize( mCatList.count() ); int i; QStringList::ConstIterator it; for ( i = 0, it = mCatList.begin(); it != mCatList.end(); ++it, i++ ) { bool number; int id = (*it).toInt( &number ); if ( !number ) { id = cat.id( "Document View", *it ); if ( id == 0 ) id = cat.addCategory( "Document View", *it ); } mCat[i] = id; } } }; /*! \class AppLnk applnk.h \brief The AppLnk class represents an application available on the system. Every Qtopia application \e app has a corresponding \e app.desktop file. When one of these files is read its data is stored as an AppLnk object. The AppLnk class introduces some Qtopia-specific concepts, and provides a variety of functions, as described in the following sections. \tableofcontents \target Types \section1 Types Every AppLnk object has a \e type. For applications, games and settings the type is \c Application; for documents the type is the document's MIME type. \target files-and-links \section1 Files and Links When you create an AppLnk (or more likely, a \link doclnk.html DocLnk\endlink), you don't deal directly with filenames in the filesystem. Instead you do this: \code DocLnk d; d.setType("text/plain"); d.setName("My Nicely Named Document / Whatever"); // Yes, "/" is legal. \endcode At this point, the file() and linkFile() are unknown. Normally this is uninteresting, and the names become automatically known, and more importantly, becomes reserved, when you ask what they are: \code QString fn = d.file(); \endcode This invents a filename, and creates the file on disk (an empty reservation file) to prevent the name being used by another application. In some circumstances, you don't want to create the file if it doesn't already exist (e.g. in the Document tab, some of the \link doclnk.html DocLnk\endlink objects represented by icons are DocLnk's created just for that view - they don't have corresponding \c .desktop files. To avoid littering empty reservation files around, we check in a few places to see whether the file really needs to exist). \section1 Functionality AppLnk objects are created by calling the constructor with the name of a \e .desktop file. The object can be checked for validity using isValid(). The following functions are used to set or retrieve information about the application: \table \header \i Get Function \i Set Function \i Short Description \row \i \l name() \i \l setName() \i application's name \row \i \l pixmap() \i \e none \i application's icon \row \i \l bigPixmap() \i \e none \i application's large icon \row \i \e none \i setIcon() \i sets the icon's filename \row \i \l type() \i \l setType() \i see \link #Types Types\endlink above \row \i \l rotation() \i \e none \i 0, 90, 180 or 270 degrees \row \i \l comment() \i \l setComment() \i text for the Details dialog \row \i \l exec() \i \l setExec() \i executable's filename \row \i \l file() \i \e none \i document's filename \row \i \l linkFile() \i \l setLinkFile() \i \e .desktop filename \row \i \l mimeTypes() \i \e none \i the mime types the application can view or edit \row \i \l categories() \i \l setCategories() \i \e{see the function descriptions} \row \i \l fileKnown() \i \e none \i see \link #files-and-links Files and Links\endlink above \row \i \l linkFileKnown() \i \e none \i see \link #files-and-links Files and Links\endlink above \row \i \l property() \i \l setProperty() \i any AppLnk property can be retrieved or set (if writeable) using these \endtable To save an AppLnk to disk use writeLink(). To execute the application that the AppLnk object refers to, use execute(). AppLnk's can be deleted from disk using removeLinkFile(). To remove both the link and the application's executable use removeFiles(). Icon sizes can be globally changed (but only for AppLnk objects created after the calls) with setSmallIconSize() and setBigIconSize(). \ingroup qtopiaemb */ /*! Sets the size used for small icons to \a small pixels. Only affects AppLnk objects created after the call. \sa smallIconSize() setIcon() */ void AppLnk::setSmallIconSize(int small) { smallSize = small; } /*! Returns the size used for small icons. \sa setSmallIconSize() setIcon() */ int AppLnk::smallIconSize() { return smallSize; } /*! Sets the size used for large icons to \a big pixels. Only affects AppLnk objects created after the call. \sa bigIconSize() setIcon() */ void AppLnk::setBigIconSize(int big) { bigSize = big; } /*! Returns the size used for large icons. \sa setBigIconSize() setIcon() */ int AppLnk::bigIconSize() { return bigSize; } /*! \fn QString AppLnk::name() const Returns the Name property. This is the user-visible name for the document or application, not the filename. See \link #files-and-links Files and Links\endlink. \sa setName() */ /*! \fn QString AppLnk::exec() const Returns the Exec property. This is the name of the executable program associated with the AppLnk. \sa setExec() */ /*! \fn QString AppLnk::rotation() const Returns the Rotation property. The value is 0, 90, 180 or 270 degrees. */ /*! \fn QString AppLnk::comment() const Returns the Comment property. \sa setComment() */ /*! \fn QStringList AppLnk::mimeTypes() const Returns the MimeTypes property. This is the list of MIME types that the application can view or edit. */ /*! \fn const QArray<int>& AppLnk::categories() const Returns the Categories property. See the CategoryWidget for more details. \sa setCategories() */ const QArray<int>& AppLnk::categories() const { d->ensureCatArray(); return d->mCat; } /*! \fn int AppLnk::id() const Returns the id of the AppLnk. If the AppLnk is not in an AppLnkSet, this value is 0, otherwise it is a value that is unique for the duration of the current process. \sa AppLnkSet::find() */ /*! \fn bool AppLnk::isValid() const Returns TRUE if this AppLnk is valid; otherwise returns FALSE. */ +/*! + \fn bool AppLnk::fileKnown() const + + If the with the AppLnk associated file is not equal to QString::null +*/ +/*! + \fn bool AppLnk::linkFileKnown()const + + The filename of the AppLnk +*/ +/*! + \fn void AppLnk::setRotation( const QString& ) + + The default rotation of the associated application. This + function is included inline for binary compatible issues +*/ /*! Creates an invalid AppLnk. \sa isValid() */ AppLnk::AppLnk() { mId = 0; d = new AppLnkPrivate(); } /*! Loads \a file (e.g. \e app.desktop) as an AppLnk. \sa writeLink() */ AppLnk::AppLnk( const QString &file ) { QStringList sl; d = new AppLnkPrivate(); if ( !file.isNull() ) { Config config( file, Config::File ); if ( config.isValid() ) { config.setGroup( "Desktop Entry" ); mName = config.readEntry( "Name", file ); mExec = config.readEntry( "Exec" ); mType = config.readEntry( "Type", QString::null ); mIconFile = config.readEntry( "Icon", QString::null ); mRotation = config.readEntry( "Rotation", "" ); mComment = config.readEntry( "Comment", QString::null ); // MIME types are case-insensitive. mMimeTypes = config.readListEntry( "MimeType", ';' ); for (QStringList::Iterator it=mMimeTypes.begin(); it!=mMimeTypes.end(); ++it) *it = (*it).lower(); mMimeTypeIcons = config.readListEntry( "MimeTypeIcons", ';' ); mLinkFile = file; mFile = config.readEntry("File", QString::null); if ( !mExec. isEmpty ( )) { mFile = QString::null; } else if ( mFile[0] != '/' ) { int slash = file.findRev('/'); if ( slash >= 0 ) { mFile = file.left(slash) + '/' + mFile; } } d->mCatList = config.readListEntry("Categories", ';'); if ( d->mCatList[0].toInt() < -1 ) { // numeric cats in file! convert to text Categories cat( 0 ); cat.load( categoryFileName() ); d->mCat.resize( d->mCatList.count() ); int i; QStringList::ConstIterator it; for ( i = 0, it = d->mCatList.begin(); it != d->mCatList.end(); ++it, i++ ) { bool number; int id = (*it).toInt( &number ); if ( !number ) { // convert from text id = cat.id( "Document View", *it ); if ( id == 0 ) id = cat.addCategory( "Document View", *it ); } d->mCat[i] = id; } d->updateCatListFromArray(); } } } mId = 0; } AppLnk& AppLnk::operator=(const AppLnk ©) { if ( this == © ) return *this; if ( mId ) qWarning("Deleting AppLnk that is in an AppLnkSet"); if ( d ) delete d; mName = copy.mName; /* remove for Qtopia 3.0 -zecke */ mPixmap = copy.mPixmap; mBigPixmap = copy.mBigPixmap; mExec = copy.mExec; mType = copy.mType; mRotation = copy.mRotation; mComment = copy.mComment; mFile = copy.mFile; mLinkFile = copy.mLinkFile; mIconFile = copy.mIconFile; mMimeTypes = copy.mMimeTypes; mMimeTypeIcons = copy.mMimeTypeIcons; mId = 0; d = new AppLnkPrivate(); d->mCat = copy.d->mCat; d->mCatList = copy.d->mCatList; d->mPixmaps = copy.d->mPixmaps; return *this; } /*! protected internally to share code should I document that at all? I don't know the TT style for that */ const QPixmap& AppLnk::pixmap( int pos, int size ) const { if ( d->mPixmaps[pos].isNull() ) { AppLnk* that = (AppLnk*)this; if ( mIconFile.isEmpty() ) { MimeType mt(type()); that->d->mPixmaps[pos] = mt.pixmap(); if ( that->d->mPixmaps[pos].isNull() ) that->d->mPixmaps[pos].convertFromImage( Resource::loadImage("UnknownDocument") .smoothScale( size, size ) ); return that->d->mPixmaps[pos]; } QImage unscaledIcon = Resource::loadImage( that->mIconFile ); if ( unscaledIcon.isNull() ) { qDebug( "Cannot find icon: %s", that->mIconFile.latin1() ); that->d->mPixmaps[pos].convertFromImage( Resource::loadImage("UnknownDocument") .smoothScale( size, size ) ); } else { that->d->mPixmaps[0].convertFromImage( unscaledIcon.smoothScale( smallSize, smallSize ) ); that->d->mPixmaps[1].convertFromImage( unscaledIcon.smoothScale( bigSize, bigSize ) ); } return that->d->mPixmaps[pos]; } return d->mPixmaps[pos]; } /*! Returns a small pixmap associated with the application. \sa bigPixmap() setIcon() */ const QPixmap& AppLnk::pixmap() const { if ( d->mPixmaps[0].isNull() ) { return pixmap(AppLnkPrivate::Normal, smallSize ); } return d->mPixmaps[0]; } /*! Returns a large pixmap associated with the application. \sa pixmap() setIcon() */ const QPixmap& AppLnk::bigPixmap() const { if ( d->mPixmaps[1].isNull() ) { return pixmap( AppLnkPrivate::Big, bigSize ); } return d->mPixmaps[1]; } /*! Returns the type of the AppLnk. For applications, games and settings the type is \c Application; for documents the type is the document's MIME type. */ QString AppLnk::type() const { if ( mType.isNull() ) { AppLnk* that = (AppLnk*)this; QString f = file(); if ( !f.isNull() ) { MimeType mt(f); that->mType = mt.id(); return that->mType; } } return mType; } /*! Returns the file associated with the AppLnk. \sa exec() name() */ QString AppLnk::file() const { if ( mExec.isEmpty ( ) && mFile.isNull() ) { AppLnk* that = (AppLnk*)this; QString ext = MimeType(mType).extension(); if ( !ext.isEmpty() ) ext = "." + ext; if ( !mLinkFile.isEmpty() ) { that->mFile = mLinkFile.right(8)==".desktop" // 8 = strlen(".desktop") ? mLinkFile.left(mLinkFile.length()-8) : mLinkFile; qDebug("mFile now == %s", mFile.latin1()); } else if ( mType.contains('/') ) { that->mFile = QString(getenv("HOME"))+"/Documents/"+mType+"/"+safeFileName(that->mName); /* * A file with the same name or a .desktop file already exists */ if ( QFile::exists(that->mFile+ext) || QFile::exists(that->mFile+".desktop") ) { int n=1; QString nn; while (QFile::exists((nn=(that->mFile+"_"+QString::number(n)))+ext) || QFile::exists(nn+".desktop")) n++; that->mFile = nn; } that->mLinkFile = that->mFile+".desktop"; that->mFile += ext; } prepareDirectories(that->mFile); if ( !that->mFile.isEmpty() ) { QFile f(that->mFile); if ( !f.open(IO_WriteOnly) ) that->mFile = QString::null; return that->mFile; } } return mFile; } /*! Returns the desktop file corresponding to this AppLnk. \sa file() exec() name() */ QString AppLnk::linkFile() const { if ( mLinkFile.isNull() ) { AppLnk* that = (AppLnk*)this; if ( type().contains('/') ) { StorageInfo storage; const FileSystem *fs = storage.fileSystemOf( that->mFile ); /* tmpfs + and ramfs are available too but not removable * either we fix storage or add this */ if ( fs && ( fs->isRemovable() || fs->disk() == "/dev/mtdblock6" || fs->disk() == "tmpfs") ) { that->mLinkFile = fs->path(); } else that->mLinkFile = getenv( "HOME" ); that->mLinkFile += "/Documents/"+type()+"/"+safeFileName(that->mName); /* the desktop file exists make sure we don't point to the same file */ if ( QFile::exists(that->mLinkFile+".desktop") ) { AppLnk lnk( that->mLinkFile + ".desktop" ); /* the linked is different */ if(that->file() != lnk.file() ) { int n = 1; QString nn; while (QFile::exists((nn=that->mLinkFile+"_"+QString::number(n))+".desktop")) { n++; /* just to be sure */ AppLnk lnk(nn ); if (lnk.file() == that->file() ) break; } that->mLinkFile = nn; } } that->mLinkFile += ".desktop"; storeLink(); } return that->mLinkFile; } return mLinkFile; } /*! Copies \a copy. */ AppLnk::AppLnk( const AppLnk © ) { mName = copy.mName; mPixmap = copy.mPixmap; mBigPixmap = copy.mBigPixmap; mExec = copy.mExec; mType = copy.mType; mRotation = copy.mRotation; mComment = copy.mComment; mFile = copy.mFile; mLinkFile = copy.mLinkFile; mIconFile = copy.mIconFile; mMimeTypes = copy.mMimeTypes; mMimeTypeIcons = copy.mMimeTypeIcons; mId = 0; d = new AppLnkPrivate(); d->mCat = copy.d->mCat; d->mCatList = copy.d->mCatList; d->mPixmaps = copy.d->mPixmaps; } /*! Destroys the AppLnk. Note that if the AppLnk is currently a member of an AppLnkSet, this will produce a run-time warning. \sa AppLnkSet::add() AppLnkSet::remove() */ AppLnk::~AppLnk() { if ( mId ) qWarning("Deleting AppLnk that is in an AppLnkSet"); if ( d ) delete d; } /*! \overload Executes the application associated with this AppLnk. \sa exec() */ void AppLnk::execute() const { execute(QStringList()); } /*! Executes the application associated with this AppLnk, with \a args as arguments. \sa exec() */ void AppLnk::execute(const QStringList& args) const { #ifdef Q_WS_QWS if ( !mRotation.isEmpty() ) { // ######## this will only work in the server int rot = QPEApplication::defaultRotation(); rot = (rot+mRotation.toInt())%360; QCString old = getenv("QWS_DISPLAY"); setenv("QWS_DISPLAY", QString("Transformed:Rot%1:0").arg(rot), 1); invoke(args); setenv("QWS_DISPLAY", old.data(), 1); } else #endif invoke(args); } /*! Invokes the application associated with this AppLnk, with \a args as arguments. Rotation is not taken into account by this function, so you should not call it directly. \sa execute() */ void AppLnk::invoke(const QStringList& args) const { Global::execute( exec(), args[0] ); } /*! Sets the Exec property to \a exec. \sa exec() name() */ void AppLnk::setExec( const QString& exec ) { mExec = exec; } #if 0 // this was inlined for better BC /*! Sets the Rotation property to \a rot. \sa rotation() */ void AppLnk::setRotation ( const QString &rot ) { mRotation = rot; } #endif /*! Sets the Name property to \a docname. \sa name() */ void AppLnk::setName( const QString& docname ) { mName = docname; } /*! Sets the File property to \a filename. \sa file() name() */ void AppLnk::setFile( const QString& filename ) { mFile = filename; } /*! Sets the LinkFile property to \a filename. \sa linkFile() */ void AppLnk::setLinkFile( const QString& filename ) { mLinkFile = filename; } /*! Sets the Comment property to \a comment. This text is displayed in the 'Details Dialog', for example if the user uses the 'press-and-hold' gesture. \sa comment() */ void AppLnk::setComment( const QString& comment ) { mComment = comment; } /*! Sets the Type property to \a type. For applications, games and settings the type should be \c Application; for documents the type should be the document's MIME type. \sa type() */ void AppLnk::setType( const QString& type ) { mType = type; } /*! \fn QString AppLnk::icon() const Returns the Icon property. \sa setIcon() */ /*! Sets the Icon property to \a iconname. This is the filename from which the pixmap() and bigPixmap() are obtained. \sa icon() setSmallIconSize() setBigIconSize() */ void AppLnk::setIcon( const QString& iconname ) { mIconFile = iconname; QImage unscaledIcon = Resource::loadImage( mIconFile ); d->mPixmaps[0].convertFromImage( unscaledIcon.smoothScale( smallSize, smallSize ) ); d->mPixmaps[1].convertFromImage( unscaledIcon.smoothScale( bigSize, bigSize ) ); } /*! Sets the Categories property to \a c. See the CategoryWidget for more details. \sa categories() */ void AppLnk::setCategories( const QArray<int>& c ) { d->mCat = c; d->updateCatListFromArray(); } /*! \fn QStringList AppLnk::mimeTypeIcons() const Returns the MimeTypeIcons property of the AppLnk. */ /*! Attempts to ensure that the link file for this AppLnk exists, including creating any required directories. Returns TRUE if successful; otherwise returns FALSE. You should not need to use this function. */ bool AppLnk::ensureLinkExists() const { QString lf = linkFile(); return prepareDirectories(lf); } /*! Commits the AppLnk to disk. Returns TRUE if the operation succeeded; otherwise returns FALSE. In addition, the "linkChanged(QString)" message is sent to the "QPE/System" \link qcop.html QCop\endlink channel. */ bool AppLnk::writeLink() const { // Only re-writes settable parts QString lf = linkFile(); if ( !ensureLinkExists() ) return FALSE; storeLink(); return TRUE; } /*! \internal */ void AppLnk::storeLink() const { Config config( mLinkFile, Config::File ); config.setGroup("Desktop Entry"); config.writeEntry("Name",mName); if ( !mIconFile.isNull() ) config.writeEntry("Icon",mIconFile); config.writeEntry("Type",type()); if(!rotation().isEmpty()) config.writeEntry("Rotation",rotation()); else config.removeEntry("Rotation"); if ( !mComment.isNull() ) config.writeEntry("Comment",mComment); QString f = file(); int i = 0; while ( i < (int)f.length() && i < (int)mLinkFile.length() && f[i] == mLinkFile[i] ) i++; while ( i && f[i] != '/' ) i--; // simple case where in the same directory if ( mLinkFile.find( '/', i + 1 ) < 0 ) f = f.mid(i+1); // ### could do relative ie ../../otherDocs/file.doc config.writeEntry("File",f); config.writeEntry( "Categories", d->mCatList, ';' ); #ifndef QT_NO_COP QCopEnvelope e("QPE/System", "linkChanged(QString)"); e << mLinkFile; #endif } /*! Sets the property named \a key to \a value. \sa property() */ void AppLnk::setProperty(const QString& key, const QString& value) { if ( ensureLinkExists() ) { Config cfg(linkFile(), Config::File); cfg.writeEntry(key,value); } } /*! Returns the property named \a key. \sa setProperty() */ QString AppLnk::property(const QString& key) const { QString lf = linkFile(); if ( !QFile::exists(lf) ) return QString::null; Config cfg(lf, Config::File); return cfg.readEntry(key); } bool AppLnk::isPreloaded() const { // Preload information is stored in the Launcher config in v1.5. Config cfg("Launcher"); cfg.setGroup("Preload"); QStringList apps = cfg.readListEntry("Apps",','); if (apps.contains(exec())) return true; return false; } void AppLnk::setPreloaded(bool yesNo) { // Preload information is stored in the Launcher config in v1.5. Config cfg("Launcher"); cfg.setGroup("Preload"); QStringList apps = cfg.readListEntry("Apps", ','); if (apps.contains(exec()) && !yesNo) apps.remove(exec()); else if (yesNo && !apps.contains(exec())) apps.append(exec()); cfg.writeEntry("Apps", apps, ','); } /*! Deletes both the linkFile() and the file() associated with this AppLnk. \sa removeLinkFile() */ void AppLnk::removeFiles() { bool valid = isValid(); if ( !valid || !linkFileKnown() || QFile::remove(linkFile()) ) { if ( QFile::remove(file()) ) { #ifndef QT_NO_COP QCopEnvelope e("QPE/System", "linkChanged(QString)"); if ( linkFileKnown() ) e << linkFile(); else e << file(); #endif } else if ( valid ) { // restore link writeLink(); } } } /*! Deletes the linkFile(), leaving any file() untouched. \sa removeFiles() */ void AppLnk::removeLinkFile() { if ( isValid() && linkFileKnown() && QFile::remove(linkFile()) ) { #ifndef QT_NO_COP QCopEnvelope e("QPE/System", "linkChanged(QString)"); e << linkFile(); #endif } } class AppLnkSetPrivate { public: AppLnkSetPrivate() { typPix.setAutoDelete(TRUE); typPixBig.setAutoDelete(TRUE); typName.setAutoDelete(TRUE); } QDict<QPixmap> typPix; QDict<QPixmap> typPixBig; QDict<QString> typName; }; /*! \class AppLnkSet applnk.h \brief The AppLnkSet class is a set of AppLnk objects. */ /*! \fn QStringList AppLnkSet::types() const Returns the list of \link applnk.html#Types types\endlink in the set. For applications, games and settings the type is \c Application; for documents the type is the document's MIME type. \sa AppLnk::type(), typeName(), typePixmap(), typeBigPixmap() */ /*! \fn const QList<AppLnk>& AppLnkSet::children() const Returns the members of the set. */ /*! Constructs an empty AppLnkSet. */ AppLnkSet::AppLnkSet() : d(new AppLnkSetPrivate) { } /*! Constructs an AppLnkSet that contains AppLnk objects representing all the files in the given \a directory (and any subdirectories recursively). \omit The directories may contain ".directory" files which override any AppLnk::type() values for AppLnk objects found in the directory. This allows simple localization of application types. \endomit */ AppLnkSet::AppLnkSet( const QString &directory ) : d(new AppLnkSetPrivate) { QDir dir( directory ); mFile = directory; findChildren(directory,QString::null,QString::null); } /*! Detaches all AppLnk objects from the set. The set become empty and the caller becomes responsible for deleting the AppLnk objects. */ void AppLnkSet::detachChildren() { QListIterator<AppLnk> it( mApps ); for ( ; it.current(); ) { AppLnk* a = *it; ++it; a->mId = 0; } mApps.clear(); } /*! Destroys the set, deleting all the AppLnk objects it contains. \sa detachChildren() */ AppLnkSet::~AppLnkSet() { QListIterator<AppLnk> it( mApps ); for ( ; it.current(); ) { AppLnk* a = *it; ++it; a->mId = 0; delete a; } delete d; } void AppLnkSet::findChildren(const QString &dr, const QString& typ, const QString& typName, int depth) { depth++; if ( depth > 10 ) return; QDir dir( dr ); QString typNameLocal = typName; if ( dir.exists( ".directory" ) ) { Config config( dr + "/.directory", Config::File ); config.setGroup( "Desktop Entry" ); typNameLocal = config.readEntry( "Name", typNameLocal ); if ( !typ.isEmpty() ) { QString iconFile = config.readEntry( "Icon", "AppsIcon" ); QImage unscaledIcon = Resource::loadImage( iconFile ); QPixmap pm, bpm; pm.convertFromImage( unscaledIcon.smoothScale( smallSize, smallSize ) ); bpm.convertFromImage( unscaledIcon.smoothScale( bigSize, bigSize ) ); d->typPix.insert(typ, new QPixmap(pm)); d->typPixBig.insert(typ, new QPixmap(bpm)); d->typName.insert(typ, new QString(typNameLocal)); } } const QFileInfoList *list = dir.entryInfoList(); if ( list ) { QFileInfo* fi; bool cadded=FALSE; for ( QFileInfoListIterator it(*list); (fi=*it); ++it ) { QString bn = fi->fileName(); if ( bn[0] != '.' && bn != "CVS" ) { if ( fi->isDir() ) { QString c = typ.isNull() ? bn : typ+"/"+bn; QString d = typNameLocal.isNull() ? bn : typNameLocal+"/"+bn; findChildren(fi->filePath(), c, d, depth ); } else { if ( fi->extension(FALSE) == "desktop" ) { AppLnk* app = new AppLnk( fi->filePath() ); #ifdef QT_NO_QWS_MULTIPROCESS if ( !Global::isBuiltinCommand( app->exec() ) ) delete app; else #endif { if ( !typ.isEmpty() ) { if ( !cadded ) { typs.append(typ); cadded = TRUE; } app->setType(typ); } add(app); } } } } } } } /*! Adds AppLnk \a f to the set. The set takes responsibility for deleting \a f. \sa remove() */ void AppLnkSet::add( AppLnk *f ) { if ( f->mId == 0 ) { AppLnk::lastId++; f->mId = AppLnk::lastId; mApps.append( f ); } else { qWarning("Attempt to add an AppLnk twice"); } } /*! Removes AppLnk \a f to the set. The caller becomes responsible for deleting \a f. Returns TRUE if \a f was in the set; otherwise returns FALSE. \sa add() */ bool AppLnkSet::remove( AppLnk *f ) { if ( mApps.remove( f ) ) { f->mId = 0; return TRUE; } return FALSE; } /*! Returns the localized name for type \a t. For applications, games and settings the type is \c Application; for documents the type is the document's MIME type. */ QString AppLnkSet::typeName( const QString& t ) const { QString *st = d->typName.find(t); return st ? *st : QString::null; } /*! Returns the small pixmap associated with type \a t. For applications, games and settings the type is \c Application; for documents the type is the document's MIME type. */ QPixmap AppLnkSet::typePixmap( const QString& t ) const { QPixmap *pm = d->typPix.find(t); return pm ? *pm : QPixmap(); } /*! Returns the large pixmap associated with type \a t. For applications, games and settings the type is \c Application; for documents the type is the document's MIME type. */ QPixmap AppLnkSet::typeBigPixmap( const QString& t ) const { QPixmap *pm = d->typPixBig.find(t); return pm ? *pm : QPixmap(); } /*! Returns the AppLnk with the given \a id. */ const AppLnk *AppLnkSet::find( int id ) const { QListIterator<AppLnk> it( children() ); for ( ; it.current(); ++it ) { const AppLnk *app = it.current(); if ( app->id() == id ) return app; } return 0; } /*! Returns the AppLnk with the given \a exec attribute. */ const AppLnk *AppLnkSet::findExec( const QString& exec ) const { QListIterator<AppLnk> it( children() ); for ( ; it.current(); ++it ) { const AppLnk *app = it.current(); if ( app->exec() == exec ) return app; } return 0; } /*! \class DocLnkSet applnk.h \brief The DocLnkSet class is a set of DocLnk objects. */ /*! \fn const QList<DocLnk>& DocLnkSet::children() const Returns the members of the set. */ /*! Constructs an empty DocLnkSet. \sa appendFrom() */ DocLnkSet::DocLnkSet() { } /*! Constructs a DocLnkSet that contains DocLnk objects representing all the files in the \a directory (and any subdirectories, recursively). If \a mimefilter is not null, only documents with a MIME type matching \a mimefilter are selected. The value may contain multiple wild-card patterns separated by ";", such as \c{*o/mpeg;audio/x-wav}. See also \link applnk.html#files-and-links Files and Links\endlink. */ DocLnkSet::DocLnkSet( const QString &directory, const QString& mimefilter ) : AppLnkSet() { QDir dir( directory ); mFile = dir.dirName(); QDict<void> reference; QStringList subFilter = QStringList::split(";", mimefilter); QValueList<QRegExp> mimeFilters; for( QStringList::Iterator it = subFilter.begin(); it != subFilter.end(); ++ it ) mimeFilters.append( QRegExp(*it, FALSE, TRUE) ); findChildren(directory, mimeFilters, reference); const QList<DocLnk> &list = children(); for ( QListIterator<DocLnk> it( list ); it.current(); ++it ) { reference.remove( (*it)->file() ); } for ( QDictIterator<void> dit(reference); dit.current(); ++dit ) { if ( dit.current() == (void*)2 ) { // Unreferenced, make an unwritten link DocLnk* dl = new DocLnk; QFileInfo fi( dit.currentKey() ); dl->setFile(fi.filePath()); dl->setName(fi.baseName()); // #### default to current path? // dl->setCategories( ... ); bool match = mimefilter.isNull(); if ( !match ) for( QValueList<QRegExp>::Iterator it = mimeFilters.begin(); it != mimeFilters.end() && !match; ++ it ) if ( (*it).match(dl->type()) >= 0 ) match = TRUE; if ( match /* && dl->type() != "application/octet-stream" */ && !!dl->exec() ) add(dl); else delete dl; } } } // other becomes empty /*! Transfers all DocLnk objects from \a other to this set. \a other becomes empty. */ void DocLnkSet::appendFrom( DocLnkSet& other ) { if ( &other == this ) return; QListIterator<AppLnk> it( other.mApps ); for ( ; it.current(); ) { mApps.append(*it); ++it; } other.mApps.clear(); } void DocLnkSet::findChildren(const QString &dr, const QValueList<QRegExp> &mimeFilters, QDict<void> &reference, int depth) { depth++; if ( depth > 10 ) return; QDir dir( dr ); /* Opie got a different approach * I guess it's geek vs. consumer * in this case to be discussed */ if ( dir.exists( ".Qtopia-ignore" ) ) return; const QFileInfoList *list = dir.entryInfoList(); if ( list ) { QFileInfo* fi; for ( QFileInfoListIterator it(*list); (fi=*it); ++it ) { QString bn = fi->fileName(); if ( bn[0] != '.' ) { if ( fi->isDir() ) { if ( bn != "CVS" && bn != "Qtopia" && bn != "QtPalmtop" ) findChildren(fi->filePath(), mimeFilters, reference, depth); } else { if ( fi->extension(FALSE) == "desktop" ) { DocLnk* dl = new DocLnk( fi->filePath() ); QFileInfo fi2(dl->file()); bool match = FALSE; if ( !fi2.exists() ) { dir.remove( dl->file() ); } if ( mimeFilters.count() == 0 ) { add( dl ); match = TRUE; } else { for( QValueList<QRegExp>::ConstIterator it = mimeFilters.begin(); it != mimeFilters.end(); ++ it ) { if ( (*it).match(dl->type()) >= 0 ) { add(dl); match = TRUE; } } } if ( !match ) delete dl; } else { if ( !reference.find(fi->fileName()) ) reference.insert(fi->filePath(), (void*)2); } } } } } } /*! \class DocLnk applnk.h \brief The DocLnk class represents loaded document references. */ /*! \fn DocLnk::DocLnk( const DocLnk &o ) Copies \a o. */ /*! Constructs a DocLnk from a valid .desktop \a file or a new .desktop \a file for other files. */ DocLnk::DocLnk( const QString &file ) : AppLnk(file) { init(file); } /*! Constructs a DocLnk from a valid .desktop \a file or a new .desktop \a file for other files. If \a may_be_desktopfile is TRUE, then an attempt is made to read \a file as a .desktop file; if that fails it is read as a normal file. */ DocLnk::DocLnk( const QString &file, bool may_be_desktopfile ) : AppLnk(may_be_desktopfile ? file : QString::null) { init(file); } void DocLnk::init(const QString &file) { if ( isValid() ) { #ifndef FORCED_DIR_STRUCTURE_WAY if ( mType.isNull() ) // try to infer it #endif { int s0 = file.findRev('/'); if ( s0 > 0 ) { int s1 = file.findRev('/',s0-1); if ( s1 > 0 ) { int s2 = file.findRev('/',s1-1); if ( s2 > 0 ) { mType = file.mid(s2+1,s0-s2-1); } } } } } else if ( QFile::exists(file) ) { QString n = file; n.replace(QRegExp(".*/"),""); n.replace(QRegExp("\\..*"),""); setName( n ); setFile( file ); } MimeType mt(mType); if( mt.application() ) mExec = mt.application()->exec(); } /*! Constructs an invalid DocLnk. */ DocLnk::DocLnk() { } /*! Destroys the DocLnk. Just like AppLnk objects, a run-time error occurs if the DocLnk is a member of a DocLnkSet (or AppLnkSet). */ DocLnk::~DocLnk() { } /*! \reimp */ QString DocLnk::exec() const { MimeType mt(type()); const AppLnk* app = mt.application(); if ( app ) return app->exec(); else return QString::null; } /*! \reimp */ void DocLnk::invoke(const QStringList& args) const { MimeType mt(type()); const AppLnk* app = mt.application(); if ( app ) { QStringList a = args; if ( linkFileKnown() && QFile::exists( linkFile() ) ) a.append(linkFile()); else a.append(file()); app->execute(a); } } diff --git a/library/backend/categories.cpp b/library/backend/categories.cpp index 2e84089..cce9f38 100644 --- a/library/backend/categories.cpp +++ b/library/backend/categories.cpp @@ -1,936 +1,931 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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 <qtopia/private/categories.h> #include <qtopia/stringutil.h> #include <qfile.h> #include <qcstring.h> #include <qtextstream.h> using namespace Qtopia; /*********************************************************** * * CategoryGroup * **********************************************************/ #ifdef PALMTOPCENTER UidGen CategoryGroup::sUidGen( UidGen::PalmtopCenter ); #else UidGen CategoryGroup::sUidGen( UidGen::Qtopia ); #endif /*! \class CategoryGroup categories.h \brief Helper class that is used by Categories CategoryGroup is a group of categories that is associated with an application or global set. Mainly it defines a map of ids to category labels and category labels to ids. Lookups can be done with labels or unique idenifiers. \ingroup qtopiaemb \ingroup qtopiadesktop \warning Categories API will likely change between Qtopia 1.5 and Qtopia 3 \sa Categories::appGroupMap(), Categories::globalGroup() */ /*! Add \a label and return the UID. If failure, then 0 is returned. Note that All and Unfiled are reserved labels. \internal */ int CategoryGroup::add( const QString &label ) { if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") ) return 0; QMap<QString,int>::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; } /*! \internal */ bool CategoryGroup::add( int uid, const QString &label ) { if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") ) return FALSE; QMap<QString,int>::ConstIterator labelIt = mLabelIdMap.find( label ); if ( labelIt != mLabelIdMap.end() ) return FALSE; QMap<int,QString>::ConstIterator idIt = mIdLabelMap.find( uid ); if ( idIt != mIdLabelMap.end() ) return FALSE; insert( uid, label ); return TRUE; } /*! Returns TRUE if \a label was removed from the group, FALSE if not. \internal */ bool CategoryGroup::remove( const QString &label ) { QMap<QString,int>::Iterator findIt = mLabelIdMap.find( label ); if ( findIt == mLabelIdMap.end() ) return FALSE; mIdLabelMap.remove( *findIt ); mLabelIdMap.remove( findIt ); return TRUE; } /*! Returns TRUE if \a uid was removed from the group, FALSE if not. \internal */ bool CategoryGroup::remove( int uid ) { QMap<int,QString>::Iterator idIt = mIdLabelMap.find( uid ); if ( idIt == mIdLabelMap.end() ) return FALSE; mLabelIdMap.remove( *idIt ); mIdLabelMap.remove( idIt ); return TRUE; } /*! \internal */ bool CategoryGroup::rename( int uid, const QString &newLabel ) { if ( newLabel == QObject::tr("All") || newLabel == QObject::tr("Unfiled") ) return FALSE; QMap<int, QString>::Iterator idIt = mIdLabelMap.find( uid ); if ( idIt == mIdLabelMap.end() ) return FALSE; mLabelIdMap.remove( *idIt ); mLabelIdMap[newLabel] = uid; *idIt = newLabel; return TRUE; } /*! \internal */ bool CategoryGroup::rename( const QString &oldLabel, const QString &newLabel ) { return rename( id(oldLabel), newLabel ); } /*! Returns TRUE if \a uid is stored in this group, FALSE if not. */ bool CategoryGroup::contains(int uid) const { return ( mIdLabelMap.find( uid ) != mIdLabelMap.end() ); } /*! Returns TRUE if \a label is stored in this group, FALSE if not. */ bool CategoryGroup::contains(const QString &label) const { return ( mLabelIdMap.find( label ) != mLabelIdMap.end() ); } /*! Returns label associated with the \a uid or QString::null if not found */ const QString &CategoryGroup::label(int uid) const { QMap<int,QString>::ConstIterator idIt = mIdLabelMap.find( uid ); if ( idIt == mIdLabelMap.end() ) return QString::null; return *idIt; } /*! Returns the uid associated with \a label or 0 if not found */ int CategoryGroup::id(const QString &label) const { QMap<QString,int>::ConstIterator labelIt = mLabelIdMap.find( label ); if ( labelIt == mLabelIdMap.end() ) return 0; return *labelIt; } /*! Returns a list of all labels stored in this group. */ QStringList CategoryGroup::labels() const { QStringList labels; for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin(); it != mIdLabelMap.end(); ++it ) labels += *it; // ### I don't think this is the place for this... // labels.sort(); return labels; } /*! Returns a list of all labels associated with the \a catids */ QStringList CategoryGroup::labels(const QArray<int> &catids ) const { QStringList labels; if ( catids.count() == 0 ) return labels; for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin(); it != mIdLabelMap.end(); ++it ) if ( catids.find( it.key() ) != -1 ) labels += *it; return labels; } /*********************************************************** * * Categories * **********************************************************/ /*! \class Categories categories.h \brief The Categories class is a database that groups categories and maps ids to names. The Categories class is the low level Categories accessor class. To add a category menu and filter for your application, see CategoryMenu. The Categories class allows the developer to add, remove, and rename categories. Categories can be created for an individual application such as Todo List or to be used for all applications. Categories that can be used by all applications are called global categories. Each PalmtopRecord subclass stores categories as an QArray<int> using PalmtopRecord::setCategories() and PalmtopRecord::categories(). This allows each record to be assigned to multiple categories. This also allows the user to rename a category and for it to update automatically in all records. This class provides several methods to convert between a category id and it's associated string such as id(), ids(), label() and labels(). A helper class called CategoryGroup is used to access categories of a single application group, such as Todo List. Global categories can also be accessed via CategoryGroup objects. See appGroupMap() and globalGroup() for the appropriate accessor methods. Categories are stored in an xml file in the Settings directory (Categories.xml). A global function called categoryFileName() will return to appropriate QString file location to be passed to load() and save() for the master categories database. \ingroup qtopiaemb \ingroup qtopiadesktop \warning Categories API will likely change between Qtopia 1.5 and Qtopia 3 \sa CategoryGroup, CategoryMenu */ /*! Add the category name as long as it doesn't already exist locally or globally. The \a uid is assigned to the category if successfully added. Return \a uid if added, 0 if conflicts (error). \internal */ 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; } /*! Add the category name as long as it doesn't already exist locally or globally. Return UID if added, 0 if conflicts (error). */ 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; } /*! \internal */ int Categories::addGlobalCategory( const QString &catname, int uid ) { mGlobalCats.add( uid, catname ); emit categoryAdded( *this, QString::null, uid ); return uid; } /*! Add the global category \a catname while checking that it doesn't already exist globally. Return UID if added, 0 if conflicts. \sa addCategory() */ 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 \a catname from the application group. If it is not found in the application group and \a checkGlobal is TRUE, 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 ); } /*! Removes the \a uid from the application group \a appname. Returns TRUE if success, FALSE if not found. */ 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; } /*! Removes the global category \a catname. Returns TRUE if success, FALSE if not found. */ 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; } /*! Removes the global category \a uid. Returns TRUE if success, FALSE if not found. */ 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 \a app. If \a includeGlobal 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; if ( appIt != mAppCats.end() ) cats += (*appIt).labels(); //else qDebug("Categories::labels didn't find app %s", app.latin1() ); if ( includeGlobal ) cats += mGlobalCats.labels(); cats.sort(); 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; } return cats; } /*! Returns the label associated with the id */ 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 ); } /*! Returns a single string associated with \a catids for display in a combobox or any area that requires one string. If \a 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 set, the just the first string is returned. */ QString Categories::displaySingle( const QString &app, const QArray<int> &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(); + else r = strs.first(); return r; } /*! Returns all ids associated with the application CategoryGroup \a app and the passed in \a labels in that group. */ QArray<int> Categories::ids( const QString &app, const QStringList &labels) const { QArray<int> results; QStringList::ConstIterator it; int i; for ( i=0, it=labels.begin(); it!=labels.end(); i++, ++it ) { int value = id( app, *it ); if ( value != 0 ) { int tmp = results.size(); results.resize( tmp + 1 ); results[ tmp ] = value; } } return results; } /*! Returns the id associated with the app. If the id is not found in the application CategoryGroup, then it searches the global CategoryGroup. If it is not found it either, 0 is returned. */ 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 \a appname or \a oldName is not found, or if \a newName conflicts with an existing category in the CategoryGroup. It will first search the CategoryGroup associated with \a appname and if not found it will try to replace in global CategoryGroup. */ 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 ); } /*! Return TRUE if renaming succeeded; FALSE if \a appname or \a oldName is not found, or if \a newName conflicts with an existing category in the CategoryGroup. This function will only rename categories found in the global CategoryGroup. */ 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; } /*! Changes the grouping of a category. If a category was global and \a global is set to TRUE, then the \a catname will be moved to the \a appname group. */ 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 ); } /*! Returns TRUE if the \a catname is in the global CategoryGroup, FALSE if not. */ bool Categories::isGlobal( const QString &catname ) const { return mGlobalCats.contains( catname ); } /*! Returns true if the \a catname is associated with any CategoryGroup, including global. */ bool Categories::exists( const QString &catname ) const { if ( isGlobal(catname) ) return TRUE; for ( QMap<QString, CategoryGroup>::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt ) if ( exists( appsIt.key(), catname ) ) return TRUE; return FALSE; } /*! Returns TRUE if the \a catname is associated with the \a appname CategoryGroup, FALSE if not found. */ 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 ); } /*! Saves the Categories database to the \a fname. See categoryFileName() for the default file name string used for the shared category database. Returns FALSE if there is error writing the file or TRUE on success. */ bool Categories::save( const QString &fname ) const { QString strNewFile = fname + ".new"; QFile f( strNewFile ); QString out; int total_written; if ( !f.open( IO_WriteOnly|IO_Raw ) ) { qWarning("Unable to write to %s", fname.latin1()); return FALSE; } out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; out += "<!DOCTYPE CategoryList>\n"; out += "<Categories>\n"; for ( QMap<int, QString>::ConstIterator git = mGlobalCats.idMap().begin(); git != mGlobalCats.idMap().end(); ++git ) out += "<Category id=\"" + QString::number(git.key()) + "\"" + " name=\"" + escapeString(*git) + "\" />\n"; for ( QMap<QString, CategoryGroup>::ConstIterator appsIt=mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt ) { const QString &app = appsIt.key(); const QMap<int, QString> &appcats = (*appsIt).idMap(); for ( QMap<int, QString>::ConstIterator appcatit = appcats.begin(); appcatit != appcats.end(); ++appcatit ) out += "<Category id=\"" + QString::number(appcatit.key()) + "\"" + " app=\"" + escapeString(app) + "\"" + " name=\"" + escapeString(*appcatit) + "\" />\n"; } out += "</Categories>\n"; QCString cstr = out.utf8(); total_written = f.writeBlock( cstr.data(), cstr.length() ); if ( total_written != int(cstr.length()) ) { f.close(); QFile::remove( strNewFile ); return FALSE; } f.close(); #ifdef Q_OS_WIN32 QFile::remove( fname ); #endif if ( ::rename( strNewFile.latin1(), fname.latin1() ) < 0 ) { qWarning( "problem renaming file %s to %s", strNewFile.latin1(), fname.latin1()); // remove the tmp file... QFile::remove( strNewFile ); } return TRUE; } /*! Loads the Categories database using \a fname. See categoryFileName() for the default file name string used for the shared category database. Returns FALSE if there is error reading the file or TRUE on success. */ bool Categories::load( const QString &fname ) { QFile file( fname ); if ( !file.open( IO_ReadOnly ) ) { qWarning("Unable to open %s", fname.latin1()); addGlobalCategory(tr("Business")); addGlobalCategory(tr("Personal")); save(fname); 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( "<Category ", i)) != -1 ) { i += 10; name = QString::null; app = QString::null; while ( 1 ) { // skip white space while ( i < len && (uc[i] == ' ' || uc[i] == '\n' || uc[i] == '\r') ) i++; // if at the end, then done if ( i >= 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; } /*! Clear the categories in memory. Equivelent to creating an empty Categories object. */ void Categories::clear() { mGlobalCats.clear(); mAppCats.clear(); } /*! Dump the contents to standard out. Used for debugging only. */ void Categories::dump() const { qDebug("\tglobal categories = %s", mGlobalCats.labels().join(", ").latin1() ); for ( QMap<QString, CategoryGroup>::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 ); } /*! \fn Categories &Categories::operator= ( const Categories &c ) Performs deep copy. */ -/*! \fn QStringList Categories::labels( const QString & app, const QArray<int> &catids ) const - - Returns list of labels associated with the application and catids -*/ - /*! \fn QStringList Categories::globalCategories() const Returns list of all global category labels */ /*! \fn const QMap<QString, CategoryGroup> &Categories::appGroupMap() const Returns a map of application names to CategoryGroup. The CategoryGroup class defines a map of ids to category labels and category labels to ids. */ /*! \fn const CategoryGroup &Categories::globalGroup() const Returns the global CategoryGroup. The CategoryGroup class defines a map of ids to category labels and category labels to ids. */ /*! \fn void Categories::categoryAdded( const Categories &cats, const QString &appname, int uid) Emitted if a category is added. \a cats is a const reference to this object \a appname is the CategoryGroup application name that the category was added to or QString::null if it was global \a uid is the unique identifier associated with the added category -*/ +*/ /*! \fn void Categories::categoryRemoved( const Categories &cats, const QString &appname, int uid) Emitted if removed category is removed. \a cats is a const reference to this object \a appname is the CategoryGroup application name that the category was removed from or QString::null if it was the global CategoryGroup \a uid is the unique identifier associated with the removed category */ /*! \fn void Categories::categoryRenamed( const Categories &cats, const QString &appname, int uid) Emitted if \a uid in the \a appname CategoryGroup is renamed in \a cats object. \a cats is a const reference to this object \a appname is the CategoryGroup application name that the category was renamed in or QString::null if it was the global CategoryGroup \a uid is the unique identifier associated with the renamed category */ /*! \fn Categories::Categories( QObject *parent=0, const char *name = 0 ) Constructor for an empty Categories object. */ /*! \fn Categories::Categories( const Categories ©From ) Deep copy constructor */ /*! \fn Categories::~Categories() Empty destructor. Call save() before destruction if there are changes that need to be saved. */ /*! \fn CategoryGroup::clear() \internal */ /*! \fn const QMap<int, QString> &CategoryGroup::idMap() const Returns a const reference to the id to label QMap */ /*! \fn CategoryGroup::CategoryGroup() \internal */ /*! \fn CategoryGroup::CategoryGroup(const CategoryGroup &c) \internal */ diff --git a/library/backend/categories.h b/library/backend/categories.h index ba65ee3..d5b3669 100644 --- a/library/backend/categories.h +++ b/library/backend/categories.h @@ -1,226 +1,226 @@ /********************************************************************** ** 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 <qstring.h> #include <qstringlist.h> #include <qmap.h> #include <qlistview.h> #include <qarray.h> #include "qpcglobal.h" #include "palmtopuidgen.h" class CategoryGroup; #if defined(QPC_TEMPLATEDLL) // MOC_SKIP_BEGIN template class QPC_EXPORT QMap<int, QString>; template class QPC_EXPORT QMap<QString, int>; 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; QStringList labels( const QArray<int> &catids ) const; const QMap<int, QString> &idMap() const { return mIdLabelMap; } private: void insert( int uid, const QString &label ); QMap<int, QString> mIdLabelMap; QMap<QString, int> mLabelIdMap; static Qtopia::UidGen &uidGen() { return sUidGen; } static Qtopia::UidGen sUidGen; }; -/** Map from application name to categories */ +/* 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<int> ids( const QString &app, const QStringList &labels) 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; 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<int> &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<QString, CategoryGroup> &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/event.cpp b/library/backend/event.cpp index d906f19..4c24ab3 100644 --- a/library/backend/event.cpp +++ b/library/backend/event.cpp @@ -1,1354 +1,1354 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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 <qtopia/timeconversion.h> #include <qtopia/stringutil.h> #include <qtopia/private/recordfields.h> #include <qbuffer.h> #include <time.h> #include "vobject_p.h" #include <stdio.h> 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<int>( r.days ) ) + "\""; if ( r.position != 0 ) buf += " rposition=\"" + QString::number( r.position ) + "\""; buf += " rfreq=\"" + QString::number( r.frequency ) + "\""; buf += " rhasenddate=\"" + QString::number( static_cast<int>( 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 ); /*! \class Event event.h \brief The Event class holds the data of a calendar event. This data includes descriptive data of the event and schedualing information. \ingroup qtopiaemb \ingroup qtopiadesktop */ /*! \class Event::RepeatPattern \class The Event::RepeatPattern class is internal. \internal */ /*! \enum Event::Days \internal */ /*! \enum Event::Type \internal */ /*! \enum Event::SoundTypeChoice This enum type defines what kind of sound is made when an alarm occurs for an event. The currently defined types are: <ul> <li>\c Silent - No sound is produced. <li>\c Loud - A loud sound is produced. </ul> */ /*! \fn bool Event::operator<( const Event & ) const \internal */ /*! \fn bool Event::operator<=( const Event & ) const \internal */ /*! \fn bool Event::operator!=( const Event & ) const \internal */ /*! \fn bool Event::operator>( const Event & ) const \internal */ /*! \fn bool Event::operator>=( const Event & ) const \internal */ /*! \enum Event::RepeatType This enum defines how a event will repeat, if at all. <ul> <li>\c NoRepeat - Event does not repeat. <li>\c Daily - Event occurs every n days. <li>\c Weekly - Event occurs every n weeks. <li>\c MonthlyDay - Event occurs every n months. Event will always occur in the same week and same day of week as the first event. <li>\c MonthlyDate - Event occurs every n months. Event will always occur on the same day of the month as the first event. <li>\c Yearly - Event occurs every n years. </ul> */ /*! \fn bool Event::isAllDay() const Returns TRUE if the event is an all day event. Otherwise returns FALSE. */ /*! \fn void Event::setAllDay(bool allday) If \a allday is TRUE, will set the event to be an all day event. Otherwise sets the event to not be an all day event. \warning This function may affect the start and end times of the event. */ /*! - \fn QDateTime Event::start() const + \fn QDateTime Event::start(bool) const Returns the start date and time of the first occurance of the event. */ /*! - \fn QDateTime Event::end() const + \fn QDateTime Event::end(bool) const Returns the end date and time of the first occurance of the event. */ /*! \fn time_t Event::startTime() const \internal */ /*! \fn time_t Event::endTime() const \internal */ /*! \fn void Event::setAlarm(int delay, SoundTypeChoice s) Sets the alarm delay of the event to \a delay and the sound type of the alarm to \a s. */ /*! \fn void Event::clearAlarm() Clears the alarm for the event. */ /*! \fn int Event::alarmDelay() const Returns the delay in minutes between the alarm for an event and the start of the event. */ /*! \fn Event::RepeatType Event::repeatType() const Returns the repeat pattern type for the event. \sa frequency() */ /*! \fn int Event::weekOffset() const Returns the number of weeks from the start of the month that this event occurs. */ /*! \fn QDate Event::repeatTill() const Returns the date that the event will continue to repeat until. If the event repeats forever the value returned is undefined. \sa repeatForever() */ /*! \fn bool Event::repeatForever() const Returns FALSE if there is a date set for the event to continue until. Otherwise returns TRUE. */ /*! \fn bool Event::doRepeat() const \internal */ /*! \fn bool Event::repeatOnWeekDay(int day) const Returns TRUE if the event has a RepeatType of Weekly and is set to occur on \a day each week. Otherwise returns FALSE. \sa QDate::dayName() */ /*! \fn void Event::setRepeatOnWeekDay(int day, bool enable) If \a enable is TRUE then sets the event to occur on \a day each week. Otherwise sets the event not to occur on \a day. \warning this function is only relavent for a event with RepeatType of Weekly. \sa QDate::dayName() */ /*! \fn int Event::frequency() const Returns how often the event repeats. \sa repeatType() */ /*! \fn void Event::setRepeatType(RepeatType t) Sets the repeat pattern type of the event to \a t. \sa setFrequency() */ /*! \fn void Event::setFrequency(int n) Sets how often the event occurs with in its repeat pattern. \sa setRepeatType() */ /*! \fn void Event::setRepeatTill(const QDate &d) Sets the event to repeat until \a d. */ /*! \fn void Event::setRepeatForever(bool enable) If \a enable is TRUE, sets the event to repeat forever. Otherwise sets the event to stop repeating at some date. \warning This function may affect the specific date the event will repeat till. */ /*! \fn bool Event::match(const QRegExp &r) const Returns TRUE if the event matches the regular expression \a r. Otherwise returns FALSE. */ /*! \fn char Event::day(int) \internal */ /*! Creates a new, empty event. */ Event::Event() : Record() { startUTC = endUTC = time( 0 ); typ = Normal; hAlarm = FALSE; hRepeat = FALSE; aMinutes = 0; aSound = Silent; pattern.type = NoRepeat; pattern.frequency = -1; } /*! \internal */ Event::Event( const QMap<int, QString> &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() ); } /*! Destroys an event. */ Event::~Event() { } /*! \internal */ 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; } /*! \internal */ 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; } /*! \internal */ int Event::dayOfWeek( char day ) { int dayOfWeek = 1; char i = Event::MON; while ( !( i & day ) && i <= Event::SUN ) { i <<= 1; ++dayOfWeek; } return dayOfWeek; } /*! \internal */ int Event::monthDiff( const QDate& first, const QDate& second ) { return ( second.year() - first.year() ) * 12 + second.month() - first.month(); } /*! \internal */ QMap<int, QString> Event::toMap() const { QMap<int, QString> m; if ( !description().isEmpty() ) m.insert( DatebookDescription, description() ); if ( !location().isEmpty() ) m.insert ( Location, location() ); if ( categories().count() ) m.insert ( DatebookCategory, idsToString( categories() ) ); if ( !timeZone().isEmpty() ) m.insert ( TimeZone, timeZone() ); if ( !notes().isEmpty() ) 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<int>( 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<int>( repeatPattern().hasEndDate ) ) ); m.insert ( RepeatPatternEndDate, QString::number( repeatPattern().endDateUTC ) ); m.insert( DatebookUid, QString::number( uid()) ); return m; } /*! \internal */ void Event::setRepeat( const RepeatPattern &p ) { setRepeat( p.type != NoRepeat, p ); } /*! Sets the description of the event to \a s. */ void Event::setDescription( const QString &s ) { descript = s; } /*! Sets the location of the event to \a s. */ void Event::setLocation( const QString &s ) { locat = s; } // void Event::setCategory( const QString &s ) // { // categ = s; // } /*! \internal */ void Event::setType( Type t ) { typ = t; } /*! Sets the start date and time of the first or only occurance of this event to the date and time \a d. \a d should be in local time. */ void Event::setStart( const QDateTime &d ) { startUTC = TimeConversion::toUTC( d ); } /*! \internal */ void Event::setStart( time_t time ) { startUTC = time; } /*! Sets the end date and time of the first or only occurance of this event to the date and time \a d. \a d should be in local time. */ void Event::setEnd( const QDateTime &d ) { endUTC = TimeConversion::toUTC( d ); } /*! \internal */ void Event::setEnd( time_t time ) { endUTC = time; } /*! \internal */ void Event::setTimeZone( const QString &z ) { tz = z; } /*! \internal */ void Event::setAlarm( bool b, int minutes, SoundTypeChoice s ) { hAlarm = b; aMinutes = minutes; aSound = s; } /*! \internal */ void Event::setRepeat( bool b, const RepeatPattern &p ) { hRepeat = b; pattern = p; } /*! Sets the notes for the event to \a n. */ void Event::setNotes( const QString &n ) { note = n; } /*! Returns the description of the event. */ const QString &Event::description() const { return descript; } /*! Returns the location of the event. */ const QString &Event::location() const { return locat; } // QString Event::category() const // { // return categ; // } /*! \internal */ Event::Type Event::type() const { return typ; } /* QDateTime Event::start() const { return start( TRUE ); } */ /*! \internal */ QDateTime Event::start( bool actual ) const { QDateTime dt = TimeConversion::fromUTC( startUTC ); if ( actual && typ == AllDay ) { QTime t = dt.time(); t.setHMS( 0, 0, 0 ); dt.setTime( t ); } return dt; } /* QDateTime Event::end() const { return end( TRUE ); } */ /*! \internal */ QDateTime Event::end( bool actual ) const { /* small work around... */ if ( actual && typ == AllDay ) { return QDateTime( TimeConversion::fromUTC( startUTC ).date(), QTime(23, 59, 59 ) ); } return TimeConversion::fromUTC( endUTC ); } /*! \internal */ const QString &Event::timeZone() const { return tz; } /*! \internal */ bool Event::hasAlarm() const { return hAlarm; } /*! \internal */ int Event::alarmTime() const { return aMinutes; } /*! Returns the sound type for the alarm of this event. */ Event::SoundTypeChoice Event::alarmSound() const { return aSound; } /*! \internal */ bool Event::hasRepeat() const { return doRepeat(); } /*! \internal */ const Event::RepeatPattern &Event::repeatPattern() const { return pattern; } /*! \internal */ Event::RepeatPattern &Event::repeatPattern() { return pattern; } /*! Returns the notes for the event. */ const QString &Event::notes() const { return note; } /*! \internal */ bool Event::operator==( const Event &e ) const { if ( uid() && e.uid() == uid() ) return TRUE; 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 ); } /*! \internal Appends the contact information to \a buf. */ 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(); } /*! \internal */ 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 EffectiveEvent \brief The EffectiveEvent class the data for a single occurance of an event. This class describes the event for a single occurance of it. For example if an Event occurs every week, the effective event might represent the third occurance of this Event. \ingroup qtopiaemb \ingroup qtopiadesktop \warning This class will be phased out in Qtopia 3.x */ /*! \enum EffectiveEvent::Position \internal */ /*! \fn EffectiveEvent &EffectiveEvent::operator=(const EffectiveEvent &) \internal */ 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; }; /*! \internal */ EffectiveEvent::EffectiveEvent() { mDate = QDate::currentDate(); mStart = mEnd = QTime::currentTime(); d = 0; } /*! \internal */ 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; } /*! \internal */ EffectiveEvent::~EffectiveEvent() { delete d; } /*! \internal */ 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(); // } /*! Returns the description of the event for this effective event. */ const QString &EffectiveEvent::description( ) const { return mEvent.description(); } /*! \internal */ const QString &EffectiveEvent::location( ) const { return mEvent.location(); } /*! \internal */ const QString &EffectiveEvent::notes() const { return mEvent.notes(); } /*! Returns the event associated with this effective event. */ const Event &EffectiveEvent::event() const { return mEvent; } /*! \internal */ const QTime &EffectiveEvent::end() const { return mEnd; } /*! \internal */ const QTime &EffectiveEvent::start() const { return mStart; } /*! Returns the date the effective event occurs on. */ const QDate &EffectiveEvent::date() const { return mDate; } /*! \internal */ int EffectiveEvent::length() const { return (mEnd.hour() * 60 - mStart.hour() * 60) + QABS(mStart.minute() - mEnd.minute() ); } /*! \internal */ void EffectiveEvent::setDate( const QDate &dt ) { mDate = dt; } /*! \internal */ void EffectiveEvent::setStart( const QTime &start ) { mStart = start; } /*! \internal */ void EffectiveEvent::setEnd( const QTime &end ) { mEnd = end; } /*! \internal */ void EffectiveEvent::setEvent( Event e ) { mEvent = e; } /*! \internal */ bool EffectiveEvent::operator<( const EffectiveEvent &e ) const { if ( mDate < e.date() ) return TRUE; if ( mDate == e.date() ) return ( mStart < e.start() ); else return FALSE; } /*! \internal */ bool EffectiveEvent::operator<=( const EffectiveEvent &e ) const { return (mDate <= e.date() ); } /*! \internal */ bool EffectiveEvent::operator==( const EffectiveEvent &e ) const { return ( mDate == e.date() && mStart == e.start() && mEnd == e.end() && mEvent == e.event() ); } /*! \internal */ bool EffectiveEvent::operator!=( const EffectiveEvent &e ) const { return !(*this == e); } /*! \internal */ bool EffectiveEvent::operator>( const EffectiveEvent &e ) const { return !(*this <= e ); } /*! \internal */ bool EffectiveEvent::operator>=(const EffectiveEvent &e) const { return !(*this < e); } /*! \internal */ 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; } /*! \internal */ 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(); } /*! \internal */ 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(); } /*! \internal */ 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; } /* * Until we support vCal/iCal right * we will make DTSTART and other things * be floating in the sense of * RFC 2445 */ namespace { /* * Convert QDateTime to iso8601 but take * local time and do not use the Z at the end * */ QCString toISOLocal( const QDateTime& dt ) { QCString str; /* * year month day T Hour Minute Second * 4 2 2 2 2 2 digits */ str.sprintf("%04d%02d%02dT%02d%02d%02d", dt.date().year(), dt.date().month(), dt.date().day(), dt.time().hour(), dt.time().minute(), dt.time().second() ); qWarning("Str ist %s", str.data() ); return str; } }; static VObject *createVObject( const Event &e ) { VObject *vcal = newVObject( VCCalProp ); safeAddPropValue( vcal, VCVersionProp, "1.0" ); VObject *event = safeAddProp( vcal, VCEventProp ); safeAddPropValue( event, VCDTstartProp, toISOLocal( e.start() ) ); safeAddPropValue( event, VCDTendProp, toISOLocal( 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, toISOLocal( 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; } /*! Writes the list of \a events as a set of VCards to the file \a filename. */ void Event::writeVCalendar( const QString &filename, const QValueList<Event> &events) { QFileDirect f( filename.utf8().data() ); if ( !f.open( IO_WriteOnly ) ) { qWarning("Unable to open vcard write"); return; } QValueList<Event>::ConstIterator it; for( it = events.begin(); it != events.end(); ++it ) { VObject *obj = createVObject( *it ); writeVObject( f.directHandle() , obj ); cleanVObject( obj ); } cleanStrTbl(); } /*! Writes \a event as a VCard to the file \a filename. */ 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(); } /*! Returns the set of events read as VCards from the file \a filename. */ QValueList<Event> Event::readVCalendar( const QString &filename ) { VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() ); QValueList<Event> 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/palmtoprecord.h b/library/backend/palmtoprecord.h index 72f7d1c..15cdd6a 100644 --- a/library/backend/palmtoprecord.h +++ b/library/backend/palmtoprecord.h @@ -1,95 +1,107 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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 <qglobal.h> #include "qpcglobal.h" #include "palmtopuidgen.h" #include <qarray.h> #include <qmap.h> #if defined(QPC_TEMPLATEDLL) // MOC_SKIP_BEGIN QPC_TEMPLATEEXTERN template class QPC_EXPORT QMap<QString, QString>; // MOC_SKIP_END #endif class QRegExp; +/** + * @short Qtopia namespace + * The namespace of Qtopia + */ namespace Qtopia { class RecordPrivate; +/** + * @short The base class of all Records + * + * The base class for Records in Qtopia + * @see Task + * @see Event + * @see Contact + */ 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<int> &v ) { mCats = v; mCats.sort(); } void setCategories( int single ); const QArray<int> &categories() const { return mCats; } void reassignCategoryId( int oldId, int newId ) { int index = mCats.find( oldId ); if ( index >= 0 ) mCats[index] = newId; } 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<int> &ids ); // convenience methods provided for loading and saving to xml static QArray<int> idsFromString( const QString &str ); // for debugging static void dump( const QMap<int, QString> &map ); protected: virtual UidGen &uidGen() = 0; virtual QString customToXml() const; private: int mUid; QArray<int> mCats; QMap<QString, QString> customMap; RecordPrivate *d; }; } #endif diff --git a/library/datebookdb.cpp b/library/datebookdb.cpp index 0fedfa8..2f33255 100644 --- a/library/datebookdb.cpp +++ b/library/datebookdb.cpp @@ -1,1136 +1,1136 @@ /********************************************************************** ** 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 <qasciidict.h> #include <qfile.h> #include <qmessagebox.h> #include <qstring.h> #include <qtextcodec.h> #include <qtextstream.h> #include <qtl.h> #include <qpe/alarmserver.h> #include <qpe/global.h> #include "datebookdb.h" #include <qpe/stringutil.h> #include <qpe/timeconversion.h> #include <errno.h> #include <stdlib.h> class DateBookDBPrivate { public: bool clean; // indcate whether we need to write to disk... }; // Helper functions static QString dateBookJournalFile() { QString str = getenv("HOME"); return QString( str +"/.caljournal" ); } static QString dateBookFilename() { return Global::applicationFileName("datebook","datebook.xml"); } /* Calculating the next event of a recuring event is actually computationally inexpensive, esp. compared to checking each day individually. There are bad worse cases for say the 29th of february or the 31st of some other months. However these are still bounded */ bool nextOccurance(const Event &e, const QDate &from, QDateTime &next) { // easy checks, first are we too far in the future or too far in the past? QDate tmpDate; int freq = e.repeatPattern().frequency; int diff, diff2, a; int iday, imonth, iyear; int dayOfWeek = 0; int firstOfWeek = 0; int weekOfMonth; - + if (e.repeatPattern().hasEndDate && e.repeatPattern().endDate() < from) return FALSE; if (e.start() >= from) { next = e.start(); return TRUE; } switch ( e.repeatPattern().type ) { case Event::Weekly: /* weekly is just daily by 7 */ /* first convert the repeatPattern.Days() mask to the next day of week valid after from */ dayOfWeek = from.dayOfWeek(); dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */ /* this is done in case freq > 1 and from in week not for this round */ // firstOfWeek = 0; this is already done at decl. while(!((1 << firstOfWeek) & e.repeatPattern().days)) firstOfWeek++; /* there is at least one 'day', or there would be no event */ while(!((1 << (dayOfWeek % 7)) & e.repeatPattern().days)) dayOfWeek++; dayOfWeek = dayOfWeek % 7; /* the actual day of week */ dayOfWeek -= e.start().date().dayOfWeek() -1; firstOfWeek = firstOfWeek % 7; /* the actual first of week */ firstOfWeek -= e.start().date().dayOfWeek() -1; // dayOfWeek may be negitive now // day of week is number of days to add to start day freq *= 7; // FALL-THROUGH !!!!! case Event::Daily: // the add is for the possible fall through from weekly */ if(e.start().date().addDays(dayOfWeek) > from) { /* first week exception */ next = QDateTime(e.start().date().addDays(dayOfWeek), e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; } /* if from is middle of a non-week */ diff = e.start().date().addDays(dayOfWeek).daysTo(from) % freq; diff2 = e.start().date().addDays(firstOfWeek).daysTo(from) % freq; if(diff != 0) diff = freq - diff; if(diff2 != 0) diff2 = freq - diff2; diff = QMIN(diff, diff2); next = QDateTime(from.addDays(diff), e.start().time()); if ( (next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate ) return FALSE; return TRUE; case Event::MonthlyDay: iday = from.day(); iyear = from.year(); imonth = from.month(); /* find equivelent day of month for this month */ dayOfWeek = e.start().date().dayOfWeek(); weekOfMonth = (e.start().date().day() - 1) / 7; /* work out when the next valid month is */ a = from.year() - e.start().date().year(); a *= 12; a = a + (imonth - e.start().date().month()); /* a is e.start()monthsFrom(from); */ if(a % freq) { a = freq - (a % freq); imonth = from.month() + a; if (imonth > 12) { imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; } } /* imonth is now the first month after or on from that matches the frequency given */ /* find for this month */ tmpDate = QDate( iyear, imonth, 1 ); iday = 1; iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; iday += 7 * weekOfMonth; while (iday > tmpDate.daysInMonth()) { imonth += freq; if (imonth > 12) { imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; } tmpDate = QDate( iyear, imonth, 1 ); /* these loops could go for a while, check end case now */ if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; iday = 1; iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; iday += 7 * weekOfMonth; } tmpDate = QDate(iyear, imonth, iday); if (tmpDate >= from) { next = QDateTime(tmpDate, e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; } /* need to find the next iteration */ do { imonth += freq; if (imonth > 12) { imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; } tmpDate = QDate( iyear, imonth, 1 ); /* these loops could go for a while, check end case now */ if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; iday = 1; iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; iday += 7 * weekOfMonth; } while (iday > tmpDate.daysInMonth()); tmpDate = QDate(iyear, imonth, iday); next = QDateTime(tmpDate, e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; case Event::MonthlyDate: iday = e.start().date().day(); iyear = from.year(); imonth = from.month(); a = from.year() - e.start().date().year(); a *= 12; a = a + (imonth - e.start().date().month()); /* a is e.start()monthsFrom(from); */ if(a % freq) { a = freq - (a % freq); imonth = from.month() + a; if (imonth > 12) { imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; } } /* imonth is now the first month after or on from that matches the frequencey given */ /* this could go for a while, worse case, 4*12 iterations, probably */ while(!QDate::isValid(iyear, imonth, iday) ) { imonth += freq; if (imonth > 12) { imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; } /* these loops could go for a while, check end case now */ if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; } if(QDate(iyear, imonth, iday) >= from) { /* done */ next = QDateTime(QDate(iyear, imonth, iday), e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; } /* ok, need to cycle */ imonth += freq; imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; while(!QDate::isValid(iyear, imonth, iday) ) { imonth += freq; imonth--; iyear += imonth / 12; imonth = imonth % 12; imonth++; if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; } next = QDateTime(QDate(iyear, imonth, iday), e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; case Event::Yearly: iday = e.start().date().day(); imonth = e.start().date().month(); iyear = from.year(); // after all, we want to start in this year diff = 1; if(imonth == 2 && iday > 28) { /* leap year, and it counts, calculate actual frequency */ if(freq % 4) if (freq % 2) freq = freq * 4; else freq = freq * 2; /* else divides by 4 already, leave freq alone */ diff = 4; } a = from.year() - e.start().date().year(); if(a % freq) { a = freq - (a % freq); iyear = iyear + a; } /* under the assumption we won't hit one of the special not-leap years twice */ if(!QDate::isValid(iyear, imonth, iday)) { /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */ iyear += freq; } if(QDate(iyear, imonth, iday) >= from) { next = QDateTime(QDate(iyear, imonth, iday), e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; } /* iyear == from.year(), need to advance again */ iyear += freq; /* under the assumption we won't hit one of the special not-leap years twice */ if(!QDate::isValid(iyear, imonth, iday)) { /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */ iyear += freq; } next = QDateTime(QDate(iyear, imonth, iday), e.start().time()); if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) return FALSE; return TRUE; default: return FALSE; } } static bool nextAlarm( const Event &ev, QDateTime& when, int& warn) { QDateTime now = QDateTime::currentDateTime(); if ( ev.hasRepeat() ) { QDateTime ralarm; if (nextOccurance(ev, now.date(), ralarm)) { ralarm = ralarm.addSecs(-ev.alarmTime()*60); if ( ralarm > now ) { when = ralarm; warn = ev.alarmTime(); } else if ( nextOccurance(ev, now.date().addDays(1), ralarm) ) { ralarm = ralarm.addSecs( -ev.alarmTime()*60 ); if ( ralarm > now ) { when = ralarm; warn = ev.alarmTime(); } } } } else { warn = ev.alarmTime(); when = ev.start().addSecs( -ev.alarmTime()*60 ); } return when > now; } static void addEventAlarm( const Event &ev ) { QDateTime when; int warn; if ( nextAlarm(ev,when,warn) ) AlarmServer::addAlarm( when, "QPE/Application/datebook", "alarm(QDateTime,int)", warn ); } static void delEventAlarm( const Event &ev ) { QDateTime when; int warn; if ( nextAlarm(ev,when,warn) ) AlarmServer::deleteAlarm( when, "QPE/Application/datebook", "alarm(QDateTime,int)", warn ); } DateBookDB::DateBookDB() { init(); } DateBookDB::~DateBookDB() { save(); eventList.clear(); repeatEvents.clear(); } //#### Why is this code duplicated in getEffectiveEvents ????? //#### Addendum. Don't use this function, lets faze it out if we can. QValueList<Event> DateBookDB::getEvents( const QDate &from, const QDate &to ) { QValueList<Event> tmpList; tmpList = getNonRepeatingEvents( from, to ); // check for repeating events... for (QValueList<Event>::ConstIterator it = repeatEvents.begin(); it != repeatEvents.end(); ++it) { QDate itDate = from; QDateTime due; /* create a false end date, to short circuit on hard MonthlyDay recurences */ Event dummy_event = *it; Event::RepeatPattern r = dummy_event.repeatPattern(); if ( !r.hasEndDate || r.endDate() > to ) { r.setEndDate( to ); r.hasEndDate = TRUE; } dummy_event.setRepeat(TRUE, r); while (nextOccurance(dummy_event, itDate, due)) { if (due.date() > to) break; Event newEvent = *it; newEvent.setStart(due); newEvent.setEnd(due.addSecs((*it).start().secsTo((*it).end()))); tmpList.append(newEvent); itDate = due.date().addDays(1); /* the next event */ } } qHeapSort(tmpList); return tmpList; } QValueList<Event> DateBookDB::getEvents( const QDateTime &start ) { QValueList<Event> day = getEvents(start.date(),start.date()); QValueListConstIterator<Event> it; QDateTime dtTmp; QValueList<Event> tmpList; for (it = day.begin(); it != day.end(); ++it ) { dtTmp = (*it).start(TRUE); if ( dtTmp == start ) tmpList.append( *it ); } return tmpList; } //#### Why is this code duplicated in getEvents ????? QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDate &from, const QDate &to ) { QValueList<EffectiveEvent> tmpList; QValueListIterator<Event> it; EffectiveEvent effEv; QDateTime dtTmp, dtEnd; for (it = eventList.begin(); it != eventList.end(); ++it ) { - if (!(*it).isValidUid()) + if (!(*it).isValidUid()) (*it).assignUid(); // FIXME: Hack to restore cleared uids dtTmp = (*it).start(TRUE); dtEnd = (*it).end(TRUE); if ( dtTmp.date() >= from && dtTmp.date() <= to ) { Event tmpEv = *it; effEv.setEvent(tmpEv); effEv.setDate( dtTmp.date() ); effEv.setStart( dtTmp.time() ); if ( dtTmp.date() != dtEnd.date() ) effEv.setEnd( QTime(23, 59, 0) ); else effEv.setEnd( dtEnd.time() ); tmpList.append( effEv ); } // we must also check for end date information... if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) { QDateTime dt = dtTmp.addDays( 1 ); dt.setTime( QTime(0, 0, 0) ); QDateTime dtStop; if ( dtEnd > to ) { dtStop = to; } else dtStop = dtEnd; while ( dt <= dtStop ) { Event tmpEv = *it; effEv.setEvent( tmpEv ); effEv.setDate( dt.date() ); if ( dt >= from ) { effEv.setStart( QTime(0, 0, 0) ); if ( dt.date() == dtEnd.date() ) effEv.setEnd( dtEnd.time() ); else effEv.setEnd( QTime(23, 59, 59) ); tmpList.append( effEv ); } dt = dt.addDays( 1 ); } } } // check for repeating events... QDateTime repeat; for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { - if (!(*it).isValidUid()) + if (!(*it).isValidUid()) (*it).assignUid(); // FIXME: Hack to restore cleared uids /* create a false end date, to short circuit on hard MonthlyDay recurences */ Event dummy_event = *it; int duration = (*it).start().date().daysTo( (*it).end().date() ); QDate itDate = from.addDays(-duration); Event::RepeatPattern r = dummy_event.repeatPattern(); if ( !r.hasEndDate || r.endDate() > to ) { r.setEndDate( to ); r.hasEndDate = TRUE; } dummy_event.setRepeat(TRUE, r); while (nextOccurance(dummy_event, itDate, repeat)) { if(repeat.date() > to) break; effEv.setDate( repeat.date() ); if ((*it).type() == Event::AllDay) { effEv.setStart( QTime(0,0,0) ); effEv.setEnd( QTime(23,59,59) ); } else { /* we only occur by days, not hours/minutes/seconds. Hence the actual end and start times will be the same for every repeated event. For multi day events this is fixed up later if on wronge day span */ effEv.setStart( (*it).start().time() ); effEv.setEnd( (*it).end().time() ); } if ( duration != 0 ) { // multi-day repeating events QDate sub_it = QMAX( repeat.date(), from ); QDate startDate = repeat.date(); QDate endDate = startDate.addDays( duration ); while ( sub_it <= endDate && sub_it <= to ) { EffectiveEvent tmpEffEv = effEv; Event tmpEv = *it; tmpEffEv.setEvent( tmpEv ); if ( sub_it != startDate ) tmpEffEv.setStart( QTime(0,0,0) ); if ( sub_it != endDate ) tmpEffEv.setEnd( QTime(23,59,59) ); tmpEffEv.setDate( sub_it ); tmpEffEv.setEffectiveDates( startDate, endDate ); tmpList.append( tmpEffEv ); sub_it = sub_it.addDays( 1 ); } itDate = endDate; } else { Event tmpEv = *it; effEv.setEvent( tmpEv ); tmpList.append( effEv ); itDate = repeat.date().addDays( 1 ); } } } qHeapSort( tmpList ); return tmpList; } QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDateTime &dt) { QValueList<EffectiveEvent> day = getEffectiveEvents(dt.date(), dt.date()); QValueListConstIterator<EffectiveEvent> it; QValueList<EffectiveEvent> tmpList; QDateTime dtTmp; for (it = day.begin(); it != day.end(); ++it ) { dtTmp = QDateTime( (*it).date(), (*it).start() ); // at the moment we don't have second granularity, be nice about that.. if ( QABS(dt.secsTo(dtTmp)) < 60 ) tmpList.append( *it ); } return tmpList; } void DateBookDB::addEvent( const Event &ev, bool doalarm ) { // write to the journal... saveJournalEntry( ev, ACTION_ADD, -1, false ); addJFEvent( ev, doalarm ); d->clean = false; } void DateBookDB::addJFEvent( const Event &ev, bool doalarm ) { if ( doalarm && ev.hasAlarm() ) addEventAlarm( ev ); if ( ev.hasRepeat() ) repeatEvents.append( ev ); else eventList.append( ev ); } void DateBookDB::editEvent( const Event &old, Event &editedEv ) { int oldIndex=0; bool oldHadRepeat = old.hasRepeat(); Event orig; // write to the journal... if ( oldHadRepeat ) { if ( origRepeat( old, orig ) ) // should work always... oldIndex = repeatEvents.findIndex( orig ); } else oldIndex = eventList.findIndex( old ); saveJournalEntry( editedEv, ACTION_REPLACE, oldIndex, oldHadRepeat ); // Delete old event if ( old.hasAlarm() ) delEventAlarm( old ); if ( oldHadRepeat ) { - if ( editedEv.hasRepeat() ) { // This mean that origRepeat was run above and + if ( editedEv.hasRepeat() ) { // This mean that origRepeat was run above and // orig is initialized // assumption, when someone edits a repeating event, they // want to change them all, maybe not perfect, but it works // for the moment... repeatEvents.remove( orig ); } else removeRepeat( old ); } else { QValueList<Event>::Iterator it = eventList.find( old ); if ( it != eventList.end() ) eventList.remove( it ); } // Add new event if ( editedEv.hasAlarm() ) addEventAlarm( editedEv ); if ( editedEv.hasRepeat() ) repeatEvents.append( editedEv ); else eventList.append( editedEv ); d->clean = false; } void DateBookDB::removeEvent( const Event &ev ) { // write to the journal... saveJournalEntry( ev, ACTION_REMOVE, -1, false ); removeJFEvent( ev ); d->clean = false; } void DateBookDB::removeJFEvent( const Event&ev ) { if ( ev.hasAlarm() ) delEventAlarm( ev ); if ( ev.hasRepeat() ) { removeRepeat( ev ); } else { QValueList<Event>::Iterator it = eventList.find( ev ); if ( it != eventList.end() ) eventList.remove( it ); } } // also handles journaling... void DateBookDB::loadFile( const QString &strFile ) { - + QFile f( strFile ); if ( !f.open( IO_ReadOnly ) ) return; enum Attribute { FDescription = 0, FLocation, FCategories, FUid, FType, FAlarm, FSound, FRType, FRWeekdays, FRPosition, FRFreq, FRHasEndDate, FREndDate, FRStart, FREnd, FNote, FCreated, FAction, FActionKey, FJournalOrigHadRepeat }; QAsciiDict<int> dict( 97 ); dict.setAutoDelete( TRUE ); dict.insert( "description", new int(FDescription) ); dict.insert( "location", new int(FLocation) ); dict.insert( "categories", new int(FCategories) ); dict.insert( "uid", new int(FUid) ); dict.insert( "type", new int(FType) ); dict.insert( "alarm", new int(FAlarm) ); dict.insert( "sound", new int(FSound) ); dict.insert( "rtype", new int(FRType) ); dict.insert( "rweekdays", new int(FRWeekdays) ); dict.insert( "rposition", new int(FRPosition) ); dict.insert( "rfreq", new int(FRFreq) ); dict.insert( "rhasenddate", new int(FRHasEndDate) ); dict.insert( "enddt", new int(FREndDate) ); dict.insert( "start", new int(FRStart) ); dict.insert( "end", new int(FREnd) ); dict.insert( "note", new int(FNote) ); dict.insert( "created", new int(FCreated) ); dict.insert( "action", new int(FAction) ); dict.insert( "actionkey", new int(FActionKey) ); dict.insert( "actionorig", new int (FJournalOrigHadRepeat) ); - + QByteArray ba = f.readAll(); char* dt = ba.data(); int len = ba.size(); int currentAction, journalKey, origHadRepeat; // should be bool, but we need tri-state(not being used) int i = 0; char *point; // hack to get rid of segfaults after reading </DATEBOOK> while ( (dt+i != 0) && (( point = strstr( dt+i, "<event " ) ) != 0 )) { i = point - dt; // if we are reading in events in the general case, // we are just adding them, so let the actions represent that... currentAction = ACTION_ADD; journalKey = -1; origHadRepeat = -1; // some temporary variables for dates and times ... //int startY = 0, startM = 0, startD = 0, starth = 0, startm = 0, starts = 0; //int endY = 0, endM = 0, endD = 0, endh = 0, endm = 0, ends = 0; //int enddtY = 0, enddtM = 0, enddtD = 0; // ... for the alarm settings ... int alarmTime = -1; Event::SoundTypeChoice alarmSound = Event::Silent; // ... and for the recurrence Event::RepeatPattern rp; Event e; i += 7; while( 1 ) { while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') ) ++i; if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') ) break; // we have another attribute, read it. int j = i; while ( j < len && dt[j] != '=' ) ++j; char *attr = dt+i; dt[j] = '\0'; i = ++j; // skip = while ( i < len && dt[i] != '"' ) ++i; j = ++i; bool haveAmp = FALSE; bool haveUtf = FALSE; while ( j < len && dt[j] != '"' ) { if ( dt[j] == '&' ) haveAmp = TRUE; if ( ((unsigned char)dt[j]) > 0x7f ) haveUtf = TRUE; ++j; } if ( i == j ) { // leave out empty attributes i = j + 1; continue; } QString value = haveUtf ? QString::fromUtf8( dt+i, j-i ) : QString::fromLatin1( dt+i, j-i ); if ( haveAmp ) value = Qtopia::plainString( value ); i = j + 1; //qDebug("attr='%s' value='%s'", attr.data(), value.latin1() ); int * find = dict[ attr ]; #if 1 if ( !find ) { // custom field e.setCustomField(attr, value); continue; } switch( *find ) { case FDescription: e.setDescription( value ); break; case FLocation: e.setLocation( value ); break; case FCategories: e.setCategories( Qtopia::Record::idsFromString( value ) ); break; case FUid: e.setUid( value.toInt() ); break; case FType: if ( value == "AllDay" ) e.setType( Event::AllDay ); else e.setType( Event::Normal ); break; case FAlarm: alarmTime = value.toInt(); break; case FSound: alarmSound = value == "loud" ? Event::Loud : Event::Silent; break; // recurrence stuff case FRType: if ( value == "Daily" ) rp.type = Event::Daily; else if ( value == "Weekly" ) rp.type = Event::Weekly; else if ( value == "MonthlyDay" ) rp.type = Event::MonthlyDay; else if ( value == "MonthlyDate" ) rp.type = Event::MonthlyDate; else if ( value == "Yearly" ) rp.type = Event::Yearly; else rp.type = Event::NoRepeat; break; case FRWeekdays: // QtopiaDesktop 1.6 sometimes creates 'rweekdays="0"' // when it goes mad. This causes datebook to crash.. (se) if ( value.toInt() != 0 ) rp.days = value.toInt(); else rp.days = 1; break; case FRPosition: rp.position = value.toInt(); break; case FRFreq: rp.frequency = value.toInt(); break; case FRHasEndDate: rp.hasEndDate = value.toInt(); break; case FREndDate: { rp.endDateUTC = (time_t) value.toLong(); break; } case FRStart: { e.setStart( (time_t) value.toLong() ); break; } case FREnd: { e.setEnd( (time_t) value.toLong() ); break; } case FNote: e.setNotes( value ); break; case FCreated: rp.createTime = value.toInt(); break; case FAction: currentAction = value.toInt(); break; case FActionKey: journalKey = value.toInt(); break; case FJournalOrigHadRepeat: origHadRepeat = value.toInt(); break; default: qDebug( "huh??? missing enum? -- attr.: %s", attr ); break; } #endif } // "post processing" (dates, times, alarm, recurrence) // start date/time e.setRepeat( rp.type != Event::NoRepeat, rp ); if ( alarmTime != -1 ) e.setAlarm( TRUE, alarmTime, alarmSound ); // now do our action based on the current action... switch ( currentAction ) { case ACTION_ADD: addJFEvent( e ); break; case ACTION_REMOVE: removeJFEvent( e ); break; case ACTION_REPLACE: // be a little bit careful, // in case of a messed up journal... if ( journalKey > -1 && origHadRepeat > -1 ) { // get the original from proper list... if ( origHadRepeat ) removeJFEvent( *(repeatEvents.at(journalKey)) ); else removeJFEvent( *(eventList.at(journalKey)) ); addJFEvent( e ); } break; default: break; } } f.close(); } void DateBookDB::init() { d = new DateBookDBPrivate; d->clean = false; QString str = dateBookFilename(); if ( str.isNull() ) { QMessageBox::warning( 0, QObject::tr("Out of Space"), QObject::tr("Unable to create start up files\n" "Please free up some space\n" "before entering data") ); } // continuing along, we call this datebook filename again, // because they may fix it before continuing, though it seems // pretty unlikely... loadFile( dateBookFilename() ); if ( QFile::exists( dateBookJournalFile() ) ) { // merge the journal loadFile( dateBookJournalFile() ); // save in our changes and remove the journal... save(); } d->clean = true; } bool DateBookDB::save() { if ( d->clean == true ) return true; QValueListIterator<Event> it; int total_written; QString strFileNew = dateBookFilename() + ".new"; QFile f( strFileNew ); if ( !f.open( IO_WriteOnly|IO_Raw ) ) return FALSE; QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n"; buf += "<events>\n"; QCString str = buf.utf8(); total_written = f.writeBlock( str.data(), str.length() ); if ( total_written != int(str.length()) ) { f.close(); QFile::remove( strFileNew ); return false; } for ( it = eventList.begin(); it != eventList.end(); ++it ) { buf = "<event"; (*it).save( buf ); buf += " />\n"; str = buf.utf8(); total_written = f.writeBlock( str.data(), str.length() ); if ( total_written != int(str.length()) ) { f.close(); QFile::remove( strFileNew ); return false; } } for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { buf = "<event"; (*it).save( buf ); buf += " />\n"; str = buf.utf8(); total_written = f.writeBlock( str.data(), str.length() ); if ( total_written != int(str.length()) ) { f.close(); QFile::remove( strFileNew ); return false; } } buf = "</events>\n</DATEBOOK>\n"; str = buf.utf8(); total_written = f.writeBlock( str.data(), str.length() ); if ( total_written != int(str.length()) ) { f.close(); QFile::remove( strFileNew ); return false; } f.close(); // now rename... I like to use the systemcall if ( ::rename( strFileNew, dateBookFilename() ) < 0 ) { qWarning( "problem renaming file %s to %s errno %d", strFileNew.latin1(), dateBookFilename().latin1(), errno ); // remove the file, otherwise it will just stick around... QFile::remove( strFileNew ); } // may as well remove the journal file... QFile::remove( dateBookJournalFile() ); d->clean = true; return true; } void DateBookDB::reload() { QValueList<Event>::Iterator it = eventList.begin(); for ( ; it != eventList.end(); ++it ) { if ( (*it).hasAlarm() ) delEventAlarm( *it ); if ( (*it).hasRepeat() ) removeRepeat( *it ); } eventList.clear(); repeatEvents.clear(); // should be a NOP init(); } bool DateBookDB::removeRepeat( const Event &ev ) { time_t removeMe = ev.repeatPattern().createTime; QValueListIterator<Event> it; for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { if ( removeMe == (*it).repeatPattern().createTime ) { repeatEvents.remove( *it ); // best break, or we are going into undefined territory! return TRUE; } } return FALSE; } bool DateBookDB::origRepeat( const Event &ev, Event &orig ) const { time_t removeMe = ev.repeatPattern().createTime; QValueListConstIterator<Event> it; for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { if ( removeMe == (*it).repeatPattern().createTime ) { orig = (*it); return TRUE; } } return FALSE; } void DateBookDB::saveJournalEntry( const Event &ev, journal_action action ) { saveJournalEntry( ev, action, -1, false ); } bool DateBookDB::saveJournalEntry( const Event &evOld, journal_action action, int key, bool origHadRepeat ) { bool status = false; Event ev = evOld; // write our log based on the action QFile f( dateBookJournalFile() ); if ( !f.open( IO_WriteOnly|IO_Append ) ) return false; QString buf = "<event"; ev.save( buf ); buf += " action="; buf += "\"" + QString::number(action) + "\""; buf += " actionkey=\"" + QString::number(key) + "\""; buf += " actionorig=\"" + QString::number(origHadRepeat) +"\""; buf += " />\n"; QString str = buf.utf8(); status = ( f.writeBlock( str.data(), str.length() ) == int(str.length()) ); f.close(); return status; } QValueList<Event> DateBookDB::getRawRepeats() const { return repeatEvents; } QValueList<Event> DateBookDB::getNonRepeatingEvents( const QDate &from, const QDate &to ) const { QValueListConstIterator<Event> it; QDateTime dtTmp, dtEnd; QValueList<Event> tmpList; for (it = eventList.begin(); it != eventList.end(); ++it ) { dtTmp = (*it).start(TRUE); dtEnd = (*it).end(TRUE); if ( dtTmp.date() >= from && dtTmp.date() <= to ) { Event e = *it; if ( dtTmp.date() != dtEnd.date() ) e.setEnd( QDateTime(dtTmp.date(), QTime(23, 59, 0)) ); tmpList.append( e ); } // we must also check for end date information... if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) { QDateTime dt = dtTmp.addDays( 1 ); dt.setTime( QTime(0, 0, 0) ); QDateTime dtStop; if ( dtEnd > to ) { dtStop = to; } else dtStop = dtEnd; while ( dt <= dtStop ) { Event ev = *it; if ( dt >= from ) { ev.setStart( QDateTime(dt.date(), QTime(0, 0, 0)) ); if ( dt.date() == dtEnd.date() ) ev.setEnd( QDateTime(dt.date(), dtEnd.time()) ); else ev.setEnd( QDateTime(dt.date(), QTime(23, 59, 0)) ); tmpList.append( ev ); } dt = dt.addDays( 1 ); } } } return tmpList; } diff --git a/library/finddialog.cpp b/library/finddialog.cpp index d9f430a..ddf41a7 100644 --- a/library/finddialog.cpp +++ b/library/finddialog.cpp @@ -1,78 +1,85 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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. ** **********************************************************************/ // WARNING: Do *NOT* define this yourself. The SL5xxx from SHARP does NOT // have this class. #define QTOPIA_INTERNAL_FD #include "finddialog.h" #include "findwidget_p.h" #include <qlayout.h> #include <qpushbutton.h> +/*! + \class FindDialog finddialog.h + \brief A simple FindDialog + + A find dialog. FIXME!!!! + +*/ FindDialog::FindDialog( const QString &appName, QWidget *parent, const char *name, bool modal ) : QDialog( parent, name, modal ) { setCaption( tr("Find") ); QVBoxLayout *vb; vb = new QVBoxLayout( this ); fw = new FindWidget( appName, this, "Find Widget" ); vb->addWidget( fw ); QObject::connect( fw, SIGNAL(signalFindClicked(const QString&, bool,bool,int)), this, SIGNAL(signalFindClicked(const QString&, bool,bool,int)) ); QObject::connect( fw, SIGNAL(signalFindClicked(const QString&,const QDate&, bool,bool,int)), this, SIGNAL(signalFindClicked(const QString&, const QDate&,bool,bool,int)) ); d = 0; } FindDialog::~FindDialog() { } QString FindDialog::findText() const { return fw->findText(); } void FindDialog::setUseDate( bool show ) { fw->setUseDate( show ); } void FindDialog::setDate( const QDate &dt ) { fw->setDate( dt ); } void FindDialog::slotNotFound() { fw->slotNotFound(); } void FindDialog::slotWrapAround() { fw->slotWrapAround(); } diff --git a/library/global.cpp b/library/global.cpp index 68a3a75..ce39751 100644 --- a/library/global.cpp +++ b/library/global.cpp @@ -1,849 +1,849 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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. ** **********************************************************************/ #define QTOPIA_INTERNAL_LANGLIST #include <qpe/qpedebug.h> #include <qpe/global.h> #include <qpe/qdawg.h> #include <qpe/qpeapplication.h> #include <qpe/resource.h> #include <qpe/storage.h> #include <qpe/applnk.h> #include <qpe/qcopenvelope_qws.h> #include <qfile.h> #include <qlabel.h> #include <qtimer.h> #include <qmap.h> #include <qdict.h> #include <qdir.h> #include <qmessagebox.h> #include <qregexp.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <qwindowsystem_qws.h> // for qwsServer #include <qdatetime.h> #include <qfile.h> namespace { // checks if the storage should be searched bool checkStorage(const QString &path ){ // this is a small Config replacement cause config is too limited -zecke QFile file(path ); if(!file.open(IO_ReadOnly ) ) return true; QByteArray array = file.readAll(); QStringList list = QStringList::split('\n', QString( array ) ); for(QStringList::Iterator it = list.begin(); it != list.end(); ++it ){ if( (*it).startsWith("autocheck = 0" ) ){ return false; }else if( (*it).startsWith("autocheck = 1" ) ){ return true; } } return true; } } //#include "quickexec_p.h" class Emitter : public QObject { Q_OBJECT public: Emitter( QWidget* receiver, const QString& document ) { connect(this, SIGNAL(setDocument(const QString&)), receiver, SLOT(setDocument(const QString&))); emit setDocument(document); disconnect(this, SIGNAL(setDocument(const QString&)), receiver, SLOT(setDocument(const QString&))); } signals: void setDocument(const QString&); }; class StartingAppList : public QObject { Q_OBJECT public: static void add( const QString& name ); static bool isStarting( const QString name ); private slots: void handleNewChannel( const QString &); private: StartingAppList( QObject *parent=0, const char* name=0 ) ; QDict<QTime> dict; static StartingAppList *appl; }; StartingAppList* StartingAppList::appl = 0; StartingAppList::StartingAppList( QObject *parent, const char* name ) :QObject( parent, name ) { #if QT_VERSION >= 232 && defined(QWS) connect( qwsServer, SIGNAL( newChannel(const QString&)), this, SLOT( handleNewChannel(const QString&)) ); -#endif +#endif dict.setAutoDelete( TRUE ); } void StartingAppList::add( const QString& name ) { #if QT_VERSION >= 232 && !defined(QT_NO_COP) if ( !appl ) appl = new StartingAppList; QTime *t = new QTime; t->start(); appl->dict.insert( "QPE/Application/" + name, t ); #endif } bool StartingAppList::isStarting( const QString name ) { #if QT_VERSION >= 232 && !defined(QT_NO_COP) if ( appl ) { QTime *t = appl->dict.find( "QPE/Application/" + name ); if ( !t ) return FALSE; if ( t->elapsed() > 10000 ) { // timeout in case of crash or something appl->dict.remove( "QPE/Application/" + name ); return FALSE; } return TRUE; } #endif return FALSE; } void StartingAppList::handleNewChannel( const QString & name ) { #if QT_VERSION >= 232 && !defined(QT_NO_COP) dict.remove( name ); #endif } static bool docDirCreated = FALSE; static QDawg* fixed_dawg = 0; static QDict<QDawg> *named_dawg = 0; static QString qpeDir() { QString dir = getenv("OPIEDIR"); if ( dir.isEmpty() ) dir = ".."; return dir; } static QString dictDir() { return qpeDir() + "/etc/dict"; } /*! \class Global global.h \brief The Global class provides application-wide global functions. The Global functions are grouped as follows: \tableofcontents \section1 User Interface The statusMessage() function provides short-duration messages to the user. The showInputMethod() function shows the current input method, and hideInputMethod() hides the input method. \section1 Document related The findDocuments() function creates a set of \link doclnk.html DocLnk\endlink objects in a particular folder. \section1 Filesystem related Global provides an applicationFileName() function that returns the full path of an application-specific file. The execute() function runs an application. \section1 Word list related A list of words relevant to the current locale is maintained by the system. The list is held in a \link qdawg.html DAWG\endlink (implemented by the QDawg class). This list is used, for example, by the pickboard input method. The global QDawg is returned by fixedDawg(); this cannot be updated. An updatable copy of the global QDawg is returned by addedDawg(). Applications may have their own word lists stored in \l{QDawg}s which are returned by dawg(). Use addWords() to add words to the updateable copy of the global QDawg or to named application \l{QDawg}s. \section1 Quoting The shellQuote() function quotes a string suitable for passing to a shell. The stringQuote() function backslash escapes '\' and '"' characters. \section1 Hardware The writeHWClock() function sets the hardware clock to the system clock's date and time. \ingroup qtopiaemb */ /*! \internal */ Global::Global() { } /*! Returns the unchangeable QDawg that contains general words for the current locale. \sa addedDawg() */ const QDawg& Global::fixedDawg() { if ( !fixed_dawg ) { if ( !docDirCreated ) createDocDir(); fixed_dawg = new QDawg; QString dawgfilename = dictDir() + "/dawg"; QString words_lang; QStringList langs = Global::languageList(); for (QStringList::ConstIterator it = langs.begin(); it!=langs.end(); ++it) { QString lang = *it; words_lang = dictDir() + "/words." + lang; QString dawgfilename_lang = dawgfilename + "." + lang; if ( QFile::exists(dawgfilename_lang) || QFile::exists(words_lang) ) { dawgfilename = dawgfilename_lang; break; } } QFile dawgfile(dawgfilename); if ( !dawgfile.exists() ) { QString fn = dictDir() + "/words"; if ( QFile::exists(words_lang) ) fn = words_lang; QFile in(fn); if ( in.open(IO_ReadOnly) ) { fixed_dawg->createFromWords(&in); dawgfile.open(IO_WriteOnly); fixed_dawg->write(&dawgfile); dawgfile.close(); } } else { fixed_dawg->readFile(dawgfilename); } } return *fixed_dawg; } /*! Returns the changeable QDawg that contains general words for the current locale. \sa fixedDawg() */ const QDawg& Global::addedDawg() { return dawg("local"); } /*! Returns the QDawg with the given \a name. This is an application-specific word list. \a name should not contain "/". */ const QDawg& Global::dawg(const QString& name) { createDocDir(); if ( !named_dawg ) named_dawg = new QDict<QDawg>; QDawg* r = named_dawg->find(name); if ( !r ) { r = new QDawg; named_dawg->insert(name,r); QString dawgfilename = applicationFileName("Dictionary", name ) + ".dawg"; QFile dawgfile(dawgfilename); if ( dawgfile.open(IO_ReadOnly) ) r->readFile(dawgfilename); } return *r; } /*! \overload Adds \a wordlist to the addedDawg(). Note that the addition of words persists between program executions (they are saved in the dictionary files), so you should confirm the words with the user before adding them. */ void Global::addWords(const QStringList& wordlist) { addWords("local",wordlist); } /*! \overload Adds \a wordlist to the addedDawg(). Note that the addition of words persists between program executions (they are saved in the dictionary files), so you should confirm the words with the user before adding them. */ void Global::addWords(const QString& dictname, const QStringList& wordlist) { QDawg& d = (QDawg&)dawg(dictname); QStringList all = d.allWords() + wordlist; d.createFromWords(all); QString dawgfilename = applicationFileName("Dictionary", dictname) + ".dawg"; QFile dawgfile(dawgfilename); if ( dawgfile.open(IO_WriteOnly) ) { d.write(&dawgfile); dawgfile.close(); } // #### Re-read the dawg here if we use mmap(). // #### Signal other processes to re-read. } /*! Returns the full path for the application called \a appname, with the given \a filename. Returns QString::null if there was a problem creating the directory tree for \a appname. If \a filename contains "/", it is the caller's responsibility to ensure that those directories exist. */ QString Global::applicationFileName(const QString& appname, const QString& filename) { QDir d; QString r = getenv("HOME"); r += "/Applications/"; if ( !QFile::exists( r ) ) if ( d.mkdir(r) == false ) return QString::null; r += appname; if ( !QFile::exists( r ) ) if ( d.mkdir(r) == false ) return QString::null; r += "/"; r += filename; return r; } /*! \internal */ void Global::createDocDir() { if ( !docDirCreated ) { docDirCreated = TRUE; mkdir( QPEApplication::documentDir().latin1(), 0755 ); } } /*! Displays a status \a message to the user. This usually appears in the taskbar for a short amount of time, then disappears. */ void Global::statusMessage(const QString& message) { -#if!defined(QT_NO_COP) +#if !defined(QT_NO_COP) QCopEnvelope e( "QPE/TaskBar", "message(QString)" ); e << message; #endif } /*! \internal */ void Global::applyStyle() { #if !defined(QT_NO_COP) QCopChannel::send( "QPE/System", "applyStyle()" ); #else ((QPEApplication *)qApp)->applyStyle(); // apply without needing QCop for floppy version #endif } /*! \internal */ QWidget *Global::shutdown( bool ) { #if !defined(QT_NO_COP) QCopChannel::send( "QPE/System", "shutdown()" ); #endif return 0; } /*! \internal */ QWidget *Global::restart( bool ) { #if !defined(QT_NO_COP) QCopChannel::send( "QPE/System", "restart()" ); #endif return 0; } /*! Explicitly show the current input method. Input methods are indicated in the taskbar by a small icon. If the input method is activated (shown) then it takes up some proportion of the bottom of the screen, to allow the user to interact (input characters) with it. \sa hideInputMethod() */ void Global::showInputMethod() { #if !defined(QT_NO_COP) QCopChannel::send( "QPE/TaskBar", "showInputMethod()" ); #endif } /*! Explicitly hide the current input method. The current input method is still indicated in the taskbar, but no longer takes up screen space, and can no longer be interacted with. \sa showInputMethod() */ void Global::hideInputMethod() { #if !defined(QT_NO_COP) QCopChannel::send( "QPE/TaskBar", "hideInputMethod()" ); #endif } /*! \internal */ bool Global::isBuiltinCommand( const QString &name ) { if(!builtin) return FALSE; // yes, it can happen for (int i = 0; builtin[i].file; i++) { if ( builtin[i].file == name ) { return TRUE; } } return FALSE; } Global::Command* Global::builtin=0; QGuardedPtr<QWidget> *Global::running=0; /*! \class Global::Command \brief The Global::Command class is internal. \internal */ /*! \internal */ void Global::setBuiltinCommands( Command* list ) { if ( running ) delete [] running; builtin = list; int count = 0; if (!builtin) return; while ( builtin[count].file ) count++; running = new QGuardedPtr<QWidget> [ count ]; } /*! \internal */ void Global::setDocument( QWidget* receiver, const QString& document ) { Emitter emitter(receiver,document); } /*! \internal */ bool Global::terminateBuiltin( const QString& n ) { if (!builtin) return FALSE; for (int i = 0; builtin[i].file; i++) { if ( builtin[i].file == n ) { delete running[i]; return TRUE; } } return FALSE; } /*! \internal */ void Global::terminate( const AppLnk* app ) { //if ( terminateBuiltin(app->exec()) ) return; // maybe? haven't tried this #ifndef QT_NO_COP QCString channel = "QPE/Application/" + app->exec().utf8(); if ( QCopChannel::isRegistered(channel) ) { QCopEnvelope e(channel, "quit()"); } #endif } /*! Low-level function to run command \a c. \warning Do not use this function. Use execute instead. \sa execute() */ void Global::invoke(const QString &c) { // Convert the command line in to a list of arguments QStringList list = QStringList::split(QRegExp(" *"),c); #if !defined(QT_NO_COP) QString ap=list[0]; // see if the application is already running // XXX should lock file /tmp/qcop-msg-ap if ( QCopChannel::isRegistered( ("QPE/Application/" + ap).latin1() ) ) { // If the channel is already register, the app is already running, so show it. { QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "raise()" ); } - + QCopEnvelope e("QPE/System", "notBusy(QString)" ); e << ap; return; } // XXX should unlock file /tmp/qcop-msg-ap //see if it is being started if ( StartingAppList::isStarting( ap ) ) { QCopEnvelope e("QPE/System", "notBusy(QString)" ); e << ap; return; } #endif #ifdef QT_NO_QWS_MULTIPROCESS QMessageBox::warning( 0, "Error", "Could not find the application " + c, "Ok", 0, 0, 0, 1 ); #else QStrList slist; unsigned int j; for ( j = 0; j < list.count(); j++ ) slist.append( list[j].utf8() ); const char **args = new (const char *)[slist.count() + 1]; for ( j = 0; j < slist.count(); j++ ) args[j] = slist.at(j); args[j] = NULL; #if !defined(QT_NO_COP) // an attempt to show a wait... // more logic should be used, but this will be fine for the moment... QCopEnvelope ( "QPE/System", "busy()" ); #endif #ifdef HAVE_QUICKEXEC QString libexe = qpeDir()+"/binlib/lib"+args[0] + ".so"; qDebug("libfile = %s", libexe.latin1() ); if ( QFile::exists( libexe ) ) { qDebug("calling quickexec %s", libexe.latin1() ); quickexecv( libexe.utf8().data(), (const char **)args ); } else #endif - { - bool success = false; + { + bool success = false; int pfd [2]; if ( ::pipe ( pfd ) < 0 ) pfd [0] = pfd [1] = -1; - + pid_t pid = ::fork ( ); - + if ( pid == 0 ) { // child for ( int fd = 3; fd < 100; fd++ ) { if ( fd != pfd [1] ) ::close ( fd ); } ::setpgid ( ::getpid ( ), ::getppid ( )); // Closing of fd[1] indicates that the execvp succeeded! if ( pfd [1] >= 0 ) ::fcntl ( pfd [1], F_SETFD, FD_CLOEXEC ); - + // Try bindir first, so that foo/bar works too ::execv ( qpeDir ( ) + "/bin/" + args [0], (char * const *) args ); ::execvp ( args [0], (char * const *) args ); - + char resultByte = 1; if ( pfd [1] >= 0 ) ::write ( pfd [1], &resultByte, 1 ); - ::_exit ( -1 ); + ::_exit ( -1 ); } else if ( pid > 0 ) { success = true; - - if ( pfd [1] >= 0 ) + + if ( pfd [1] >= 0 ) ::close ( pfd [1] ); if ( pfd [0] >= 0 ) { while ( true ) { char resultByte; int n = ::read ( pfd [0], &resultByte, 1 ); if ( n == 1 ) { success = false; break; } if (( n == -1 ) && (( errno == ECHILD ) || ( errno == EINTR ))) continue; - + break; // success } ::close ( pfd [0] ); } } - if ( success ) + if ( success ) StartingAppList::add( list[0] ); else QMessageBox::warning( 0, "Error", "Could not start the application " + c, "Ok", 0, 0, 0, 1 ); } #endif //QT_NO_QWS_MULTIPROCESS } /*! Executes the application identfied by \a c, passing \a document if it isn't null. Note that a better approach might be to send a QCop message to the application's QPE/Application/\e{appname} channel. */ void Global::execute( const QString &c, const QString& document ) { if ( qApp->type() != QApplication::GuiServer ) { // ask the server to do the work #if !defined(QT_NO_COP) if ( document.isNull() ) { QCopEnvelope e( "QPE/System", "execute(QString)" ); e << c; } else { QCopEnvelope e( "QPE/System", "execute(QString,QString)" ); e << c << document; } #endif return; } // Attempt to execute the app using a builtin class for the app first // else try and find it in the bin directory if (builtin) { for (int i = 0; builtin[i].file; i++) { if ( builtin[i].file == c ) { if ( running[i] ) { if ( !document.isNull() && builtin[i].documentary ) setDocument(running[i], document); running[i]->raise(); running[i]->show(); running[i]->setActiveWindow(); } else { running[i] = builtin[i].func( builtin[i].maximized ); } #ifndef QT_NO_COP QCopEnvelope e("QPE/System", "notBusy(QString)" ); e << c; // that was quick ;-) #endif return; } } } //Global::invoke(c, document); // Convert the command line in to a list of arguments QStringList list = QStringList::split(QRegExp(" *"),c); #if !defined(QT_NO_COP) QString ap=list[0]; qDebug("executing %s", ap.latin1() ); /* if need be, sending a qcop message will result in an invoke, see preceeding function */ invoke( ap ); //{ QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "raise()" ); } if ( !document.isEmpty() ) { QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "setDocument(QString)" ); env << document; } #endif } /*! Returns the string \a s with the characters '\', '"', and '$' quoted by a preceeding '\'. \sa stringQuote() */ QString Global::shellQuote(const QString& s) { QString r="\""; for (int i=0; i<(int)s.length(); i++) { char c = s[i].latin1(); switch (c) { case '\\': case '"': case '$': r+="\\"; } r += s[i]; } r += "\""; return r; } /*! Returns the string \a s with the characters '\' and '"' quoted by a preceeding '\'. \sa shellQuote() */ QString Global::stringQuote(const QString& s) { QString r="\""; for (int i=0; i<(int)s.length(); i++) { char c = s[i].latin1(); switch (c) { case '\\': case '"': r+="\\"; } r += s[i]; } r += "\""; return r; } /*! Finds all documents on the system's document directories which match the filter \a mimefilter, and appends the resulting DocLnk objects to \a folder. */ void Global::findDocuments(DocLnkSet* folder, const QString &mimefilter) { QString homedocs = QString(getenv("HOME")) + "/Documents"; DocLnkSet d(homedocs,mimefilter); folder->appendFrom(d); /** let's do intellegint way of searching these files * a) the user don't want to check mediums global * b) the user wants to check but use the global options for it * c) the user wants to check it but not this medium * d) the user wants to check and this medium as well * * In all cases we need to apply a different mimefilter to * the medium. * a) mimefilter.isEmpty() we need to apply the responding filter * either the global or the one on the medium * * b) mimefilter is set to an application we need to find out if the * mimetypes are included in the mime mask of the medium */ StorageInfo storage; const QList<FileSystem> &fs = storage.fileSystems(); QListIterator<FileSystem> it ( fs ); for ( ; it.current(); ++it ) { if ( (*it)->isRemovable() ) { // let's find out if we should search on it // this is a candidate look at the cf and see if we should search on it QString path = (*it)->path(); if( !checkStorage((*it)->path() + "/.opiestorage.cf" ) ) continue; DocLnkSet ide( path, mimefilter ); folder->appendFrom(ide); } else if ( (*it)->disk() == "/dev/mtdblock6" || (*it)->disk() == "tmpfs" ) { QString path = (*it)->path() + "/Documents"; DocLnkSet ide( path, mimefilter ); folder->appendFrom(ide); } } } QStringList Global::languageList() { QString lang = getenv("LANG"); QStringList langs; langs.append(lang); int i = lang.find("."); if ( i > 0 ) lang = lang.left( i ); i = lang.find( "_" ); if ( i > 0 ) langs.append(lang.left(i)); return langs; } QStringList Global::helpPath() { QStringList path; QStringList langs = Global::languageList(); for (QStringList::ConstIterator it = langs.fromLast(); it!=langs.end(); --it) { QString lang = *it; if ( !lang.isEmpty() ) path += QPEApplication::qpeDir() + "/help/" + lang + "/html"; } path += QPEApplication::qpeDir() + "/pics"; path += QPEApplication::qpeDir() + "/help/html"; path += QPEApplication::qpeDir() + "/docs"; return path; } #include "global.moc" diff --git a/library/qpeapplication.cpp b/library/qpeapplication.cpp index c562f6c..2ef60d5 100644 --- a/library/qpeapplication.cpp +++ b/library/qpeapplication.cpp @@ -1,1806 +1,1807 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the 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. ** ** $Id$ ** **********************************************************************/ #define QTOPIA_INTERNAL_LANGLIST #include <stdlib.h> #include <unistd.h> #include <linux/limits.h> // needed for some toolchains (PATH_MAX) #include <qfile.h> #ifdef Q_WS_QWS #ifndef QT_NO_COP #if QT_VERSION <= 231 #define private public #define sendLocally processEvent #include "qcopenvelope_qws.h" #undef private #else #include "qcopenvelope_qws.h" #endif #endif #include <qwindowsystem_qws.h> #endif #include <qtextstream.h> #include <qpalette.h> #include <qbuffer.h> #include <qptrdict.h> #include <qregexp.h> #include <qdir.h> #include <qlabel.h> #include <qdialog.h> #include <qdragobject.h> #include <qtextcodec.h> #include <qevent.h> #include <qtooltip.h> #include <qsignal.h> #include "qpeapplication.h" #include "qpestyle.h" #include "styleinterface.h" #if QT_VERSION >= 300 #include <qstylefactory.h> #else #include <qplatinumstyle.h> #include <qwindowsstyle.h> #include <qmotifstyle.h> #include <qmotifplusstyle.h> #include "lightstyle.h" #include <qpe/qlibrary.h> #endif #include "global.h" #include "resource.h" #if QT_VERSION <= 230 && defined(QT_NO_CODECS) #include "qutfcodec.h" #endif #include "config.h" #include "network.h" #ifdef QWS #include "fontmanager.h" #endif #include "alarmserver.h" #include "applnk.h" #include "qpemenubar.h" #include "textcodecinterface.h" #include "imagecodecinterface.h" #include <unistd.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/soundcard.h> #include "qt_override_p.h" class QPEApplicationData { public: QPEApplicationData ( ) : presstimer( 0 ), presswidget( 0 ), rightpressed( false ), kbgrabbed( false ), notbusysent( false ), preloaded( false ), forceshow( false ), nomaximize( false ), keep_running( true ), qpe_main_widget( 0 ) { qcopq.setAutoDelete( TRUE ); } int presstimer; QWidget* presswidget; QPoint presspos; bool rightpressed : 1; bool kbgrabbed : 1; bool notbusysent : 1; bool preloaded : 1; bool forceshow : 1; bool nomaximize : 1; bool keep_running : 1; QString appName; struct QCopRec { QCopRec( const QCString &ch, const QCString &msg, const QByteArray &d ) : channel( ch ), message( msg ), data( d ) { } QCString channel; QCString message; QByteArray data; }; QWidget* qpe_main_widget; QList<QCopRec> qcopq; void enqueueQCop( const QCString &ch, const QCString &msg, const QByteArray &data ) { qcopq.append( new QCopRec( ch, msg, data ) ); } void sendQCopQ() { QCopRec * r; #ifndef QT_NO_COP for ( QListIterator<QCopRec> it( qcopq ); ( r = it.current() ); ++it ) QCopChannel::sendLocally( r->channel, r->message, r->data ); #endif qcopq.clear(); } static void show_mx(QWidget* mw, bool nomaximize) { if ( mw->layout() && mw->inherits("QDialog") ) { QPEApplication::showDialog((QDialog*)mw, nomaximize); } else { #ifdef Q_WS_QWS if ( !nomaximize ) mw->showMaximized(); else #endif mw->show(); } } static bool setWidgetCaptionFromAppName( QWidget* /*mw*/, const QString& /*appName*/, const QString& /*appsPath*/ ) { /* // This works but disable it for now until it is safe to apply // What is does is scan the .desktop files of all the apps for // the applnk that has the corresponding argv[0] as this program // then it uses the name stored in the .desktop file as the caption // for the main widget. This saves duplicating translations for // the app name in the program and in the .desktop files. AppLnkSet apps( appsPath ); QList<AppLnk> appsList = apps.children(); for ( QListIterator<AppLnk> it(appsList); it.current(); ++it ) { if ( (*it)->exec() == appName ) { mw->setCaption( (*it)->name() ); return TRUE; } } */ return FALSE; } void show(QWidget* mw, bool nomax) { setWidgetCaptionFromAppName( mw, appName, QPEApplication::qpeDir() + "apps" ); nomaximize = nomax; qpe_main_widget = mw; #ifndef QT_NO_COP sendQCopQ(); #endif if ( preloaded ) { if (forceshow) show_mx(mw, nomax); } else if ( keep_running ) { show_mx(mw, nomax); } } void loadTextCodecs() { QString path = QPEApplication::qpeDir() + "/plugins/textcodecs"; QDir dir( path, "lib*.so" ); QStringList list; if ( dir. exists ( )) list = dir.entryList(); QStringList::Iterator it; for ( it = list.begin(); it != list.end(); ++it ) { TextCodecInterface *iface = 0; QLibrary *lib = new QLibrary( path + "/" + *it ); if ( lib->queryInterface( IID_QtopiaTextCodec, (QUnknownInterface**)&iface ) == QS_OK && iface ) { QValueList<int> mibs = iface->mibEnums(); for (QValueList<int>::ConstIterator i = mibs.begin(); i != mibs.end(); ++i) { (void)iface->createForMib(*i); // ### it exists now; need to remember if we can delete it } } else { lib->unload(); delete lib; } } } void loadImageCodecs() { QString path = QPEApplication::qpeDir() + "/plugins/imagecodecs"; QDir dir( path, "lib*.so" ); QStringList list; if ( dir. exists ( )) list = dir.entryList(); QStringList::Iterator it; for ( it = list.begin(); it != list.end(); ++it ) { ImageCodecInterface *iface = 0; QLibrary *lib = new QLibrary( path + "/" + *it ); if ( lib->queryInterface( IID_QtopiaImageCodec, (QUnknownInterface**)&iface ) == QS_OK && iface ) { QStringList formats = iface->keys(); for (QStringList::ConstIterator i = formats.begin(); i != formats.end(); ++i) { (void)iface->installIOHandler(*i); // ### it exists now; need to remember if we can delete it } } else { lib->unload(); delete lib; } } } QString styleName; QString decorationName; }; class ResourceMimeFactory : public QMimeSourceFactory { public: ResourceMimeFactory() { setFilePath( Global::helpPath() ); setExtensionType( "html", "text/html;charset=UTF-8" ); } const QMimeSource* data( const QString& abs_name ) const { const QMimeSource * r = QMimeSourceFactory::data( abs_name ); if ( !r ) { int sl = abs_name.length(); do { sl = abs_name.findRev( '/', sl - 1 ); QString name = sl >= 0 ? abs_name.mid( sl + 1 ) : abs_name; int dot = name.findRev( '.' ); if ( dot >= 0 ) name = name.left( dot ); QImage img = Resource::loadImage( name ); if ( !img.isNull() ) r = new QImageDrag( img ); } while ( !r && sl > 0 ); } return r; } }; static int muted = 0; static int micMuted = 0; static void setVolume( int t = 0, int percent = -1 ) { switch ( t ) { case 0: { Config cfg( "qpe" ); cfg.setGroup( "Volume" ); if ( percent < 0 ) percent = cfg.readNumEntry( "VolumePercent", 50 ); int fd = 0; if ( ( fd = open( "/dev/mixer", O_RDWR ) ) >= 0 ) { int vol = muted ? 0 : percent; // set both channels to same volume vol |= vol << 8; ioctl( fd, MIXER_WRITE( 0 ), &vol ); ::close( fd ); } } break; } } static void setMic( int t = 0, int percent = -1 ) { switch ( t ) { case 0: { Config cfg( "qpe" ); cfg.setGroup( "Volume" ); if ( percent < 0 ) percent = cfg.readNumEntry( "Mic", 50 ); int fd = 0; int mic = micMuted ? 0 : percent; if ( ( fd = open( "/dev/mixer", O_RDWR ) ) >= 0 ) { ioctl( fd, MIXER_WRITE( SOUND_MIXER_MIC ), &mic ); ::close( fd ); } } break; } } static void setBass( int t = 0, int percent = -1 ) { switch ( t ) { case 0: { Config cfg( "qpe" ); cfg.setGroup( "Volume" ); if ( percent < 0 ) percent = cfg.readNumEntry( "BassPercent", 50 ); int fd = 0; int bass = percent; if ( ( fd = open( "/dev/mixer", O_RDWR ) ) >= 0 ) { ioctl( fd, MIXER_WRITE( SOUND_MIXER_BASS ), &bass ); ::close( fd ); } } break; } } static void setTreble( int t = 0, int percent = -1 ) { switch ( t ) { case 0: { Config cfg( "qpe" ); cfg.setGroup( "Volume" ); if ( percent < 0 ) percent = cfg.readNumEntry( "TreblePercent", 50 ); int fd = 0; int treble = percent; if ( ( fd = open( "/dev/mixer", O_RDWR ) ) >= 0 ) { ioctl( fd, MIXER_WRITE( SOUND_MIXER_TREBLE ), &treble ); ::close( fd ); } } break; } } /*! \class QPEApplication qpeapplication.h \brief The QPEApplication class implements various system services that are available to all Qtopia applications. Simply by using QPEApplication instead of QApplication, a standard Qt application becomes a Qtopia application. It automatically follows style changes, quits and raises, and in the case of \link docwidget.html document-oriented\endlink applications, changes the currently displayed document in response to the environment. To create a \link docwidget.html document-oriented\endlink application use showMainDocumentWidget(); to create a non-document-oriented application use showMainWidget(). The keepRunning() function indicates whether the application will continue running after it's processed the last \link qcop.html QCop\endlink message. This can be changed using setKeepRunning(). A variety of signals are emitted when certain events occur, for example, timeChanged(), clockChanged(), weekChanged(), dateFormatChanged() and volumeChanged(). If the application receives a \link qcop.html QCop\endlink message on the application's QPE/Application/\e{appname} channel, the appMessage() signal is emitted. There are also flush() and reload() signals, which are emitted when synching begins and ends respectively - upon these signals, the application should save and reload any data files that are involved in synching. Most of these signals will initially be received and unfiltered through the appMessage() signal. This class also provides a set of useful static functions. The qpeDir() and documentDir() functions return the respective paths. The grabKeyboard() and ungrabKeyboard() functions are used to control whether the application takes control of the device's physical buttons (e.g. application launch keys). The stylus' mode of operation is set with setStylusOperation() and retrieved with stylusOperation(). There are also setInputMethodHint() and inputMethodHint() functions. \ingroup qtopiaemb */ /*! \fn void QPEApplication::clientMoused() \internal */ /*! \fn void QPEApplication::timeChanged(); This signal is emitted when the time changes outside the normal passage of time, i.e. if the time is set backwards or forwards. */ /*! \fn void QPEApplication::clockChanged( bool ampm ); This signal is emitted when the user changes the clock's style. If \a ampm is TRUE, the user wants a 12-hour AM/PM clock, otherwise, they want a 24-hour clock. */ /*! \fn void QPEApplication::volumeChanged( bool muted ) This signal is emitted whenever the mute state is changed. If \a muted is TRUE, then sound output has been muted. */ /*! \fn void QPEApplication::weekChanged( bool startOnMonday ) This signal is emitted if the week start day is changed. If \a startOnMonday is TRUE then the first day of the week is Monday; if \a startOnMonday is FALSE then the first day of the week is Sunday. */ /*! - \fn void QPEApplication::dateFormatChanged() + \fn void QPEApplication::dateFormatChanged(DateFormat) This signal is emitted whenever the date format is changed. */ /*! \fn void QPEApplication::flush() ### */ /*! \fn void QPEApplication::reload() */ /*! \fn void QPEApplication::appMessage( const QCString& msg, const QByteArray& data ) This signal is emitted when a message is received on this application's QPE/Application/<i>appname</i> \link qcop.html QCop\endlink channel. The slot to which you connect this signal uses \a msg and \a data in the following way: \code void MyWidget::receive( const QCString& msg, const QByteArray& data ) { QDataStream stream( data, IO_ReadOnly ); if ( msg == "someMessage(int,int,int)" ) { int a,b,c; stream >> a >> b >> c; ... } else if ( msg == "otherMessage(QString)" ) { ... } } \endcode \sa qcop.html Note that messages received here may be processed by qpe application and emitted as signals, such as flush() and reload(). */ /*! Constructs a QPEApplication just as you would construct a QApplication, passing \a argc, \a argv, and \a t. For applications, \a t should be the default, GuiClient. Only the Qtopia server passes GuiServer. */ QPEApplication::QPEApplication( int & argc, char **argv, Type t ) : QApplication( argc, argv, t ) { d = new QPEApplicationData; d->loadTextCodecs(); d->loadImageCodecs(); int dw = desktop() ->width(); if ( dw < 200 ) { setFont( QFont( "helvetica", 8 ) ); AppLnk::setSmallIconSize( 10 ); AppLnk::setBigIconSize( 28 ); } else if ( dw > 600 ) { setFont( QFont( "helvetica", 18 ) ); AppLnk::setSmallIconSize( 24 ); AppLnk::setBigIconSize( 48 ); } else if ( dw > 200 ) { setFont( QFont( "helvetica", 10 ) ); AppLnk::setSmallIconSize( 14 ); AppLnk::setBigIconSize( 32 ); } QMimeSourceFactory::setDefaultFactory( new ResourceMimeFactory ); connect( this, SIGNAL( lastWindowClosed() ), this, SLOT( hideOrQuit() ) ); #if defined(Q_WS_QWS) && !defined(QT_NO_COP) QString qcopfn( "/tmp/qcop-msg-" ); qcopfn += QString( argv[ 0 ] ); // append command name QFile f( qcopfn ); if ( f.open( IO_ReadOnly ) ) { flock( f.handle(), LOCK_EX ); } sysChannel = new QCopChannel( "QPE/System", this ); connect( sysChannel, SIGNAL( received( const QCString &, const QByteArray & ) ), this, SLOT( systemMessage( const QCString &, const QByteArray & ) ) ); QCString channel = QCString( argv[ 0 ] ); channel.replace( QRegExp( ".*/" ), "" ); d->appName = channel; channel = "QPE/Application/" + channel; pidChannel = new QCopChannel( channel, this ); connect( pidChannel, SIGNAL( received( const QCString &, const QByteArray & ) ), this, SLOT( pidMessage( const QCString &, const QByteArray & ) ) ); if ( f.isOpen() ) { d->keep_running = FALSE; QDataStream ds( &f ); QCString channel, message; QByteArray data; while ( !ds.atEnd() ) { ds >> channel >> message >> data; d->enqueueQCop( channel, message, data ); } flock( f.handle(), LOCK_UN ); f.close(); f.remove(); } for ( int a = 0; a < argc; a++ ) { if ( qstrcmp( argv[ a ], "-preload" ) == 0 ) { argv[ a ] = argv[ a + 1 ]; a++; d->preloaded = TRUE; argc -= 1; } else if ( qstrcmp( argv[ a ], "-preload-show" ) == 0 ) { argv[ a ] = argv[ a + 1 ]; a++; d->preloaded = TRUE; d->forceshow = TRUE; argc -= 1; } } /* overide stored arguments */ setArgs( argc, argv ); #endif // qwsSetDecoration( new QPEDecoration() ); #ifndef QT_NO_TRANSLATION QStringList langs = Global::languageList(); for ( QStringList::ConstIterator it = langs.begin(); it != langs.end(); ++it ) { QString lang = *it; QTranslator * trans; QString tfn; trans = new QTranslator( this ); tfn = qpeDir() + "/i18n/" + lang + "/libqpe.qm"; if ( trans->load( tfn ) ) installTranslator( trans ); else delete trans; trans = new QTranslator( this ); tfn = qpeDir() + "/i18n/" + lang + "/" + d->appName + ".qm"; if ( trans->load( tfn ) ) installTranslator( trans ); else delete trans; //###language/font hack; should look it up somewhere #ifdef QWS if ( lang == "ja" || lang == "zh_CN" || lang == "zh_TW" || lang == "ko" ) { QFont fn = FontManager::unicodeFont( FontManager::Proportional ); setFont( fn ); } #endif } #endif applyStyle(); if ( type() == GuiServer ) { setVolume(); } installEventFilter( this ); QPEMenuToolFocusManager::initialize(); #ifdef QT_NO_QWS_CURSOR // if we have no cursor, probably don't want tooltips QToolTip::setEnabled( FALSE ); #endif } static QPtrDict<void>* inputMethodDict = 0; static void createInputMethodDict() { if ( !inputMethodDict ) inputMethodDict = new QPtrDict<void>; } /*! Returns the currently set hint to the system as to whether widget \a w has any use for text input methods. \sa setInputMethodHint() InputMethodHint */ QPEApplication::InputMethodHint QPEApplication::inputMethodHint( QWidget * w ) { if ( inputMethodDict && w ) return ( InputMethodHint ) ( int ) inputMethodDict->find( w ); return Normal; } /*! \enum QPEApplication::InputMethodHint \value Normal the application sometimes needs text input (the default). \value AlwaysOff the application never needs text input. \value AlwaysOn the application always needs text input. */ /*! Hints to the system that widget \a w has use for text input methods as specified by \a mode. \sa inputMethodHint() InputMethodHint */ void QPEApplication::setInputMethodHint( QWidget * w, InputMethodHint mode ) { createInputMethodDict(); if ( mode == Normal ) { inputMethodDict->remove ( w ); } else { inputMethodDict->insert( w, ( void* ) mode ); } } class HackDialog : public QDialog { public: void acceptIt() { accept(); } void rejectIt() { reject(); } }; void QPEApplication::mapToDefaultAction( QWSKeyEvent * ke, int key ) { // specialised actions for certain widgets. May want to // add more stuff here. if ( activePopupWidget() && activePopupWidget() ->inherits( "QListBox" ) && activePopupWidget() ->parentWidget() && activePopupWidget() ->parentWidget() ->inherits( "QComboBox" ) ) key = Qt::Key_Return; if ( activePopupWidget() && activePopupWidget() ->inherits( "QPopupMenu" ) ) key = Qt::Key_Return; #ifdef QWS ke->simpleData.keycode = key; #endif } class HackWidget : public QWidget { public: bool needsOk() { return ( getWState() & WState_Reserved1 ); } }; /*! \internal */ #ifdef QWS bool QPEApplication::qwsEventFilter( QWSEvent * e ) { if ( !d->notbusysent && e->type == QWSEvent::Focus ) { if ( qApp->type() != QApplication::GuiServer ) { QCopEnvelope e( "QPE/System", "notBusy(QString)" ); e << d->appName; } d->notbusysent = TRUE; } if ( type() == GuiServer ) { switch ( e->type ) { case QWSEvent::Mouse: if ( e->asMouse() ->simpleData.state && !QWidget::find( e->window() ) ) emit clientMoused(); break; default: break; } } if ( e->type == QWSEvent::Key ) { QWSKeyEvent *ke = ( QWSKeyEvent * ) e; if ( ke->simpleData.keycode == Qt::Key_F33 ) { // Use special "OK" key to press "OK" on top level widgets QWidget * active = activeWindow(); QWidget *popup = 0; if ( active && active->isPopup() ) { popup = active; active = active->parentWidget(); } if ( active && ( int ) active->winId() == ke->simpleData.window && !active->testWFlags( WStyle_Customize | WType_Popup | WType_Desktop ) ) { if ( ke->simpleData.is_press ) { if ( popup ) popup->close(); if ( active->inherits( "QDialog" ) ) { HackDialog * d = ( HackDialog * ) active; d->acceptIt(); return TRUE; } else if ( ( ( HackWidget * ) active ) ->needsOk() ) { QSignal s; s.connect( active, SLOT( accept() ) ); s.activate(); } else { // do the same as with the select key: Map to the default action of the widget: mapToDefaultAction( ke, Qt::Key_Return ); } } } } else if ( ke->simpleData.keycode == Qt::Key_F30 ) { // Use special "select" key to do whatever default action a widget has mapToDefaultAction( ke, Qt::Key_Space ); } else if ( ke->simpleData.keycode == Qt::Key_Escape && ke->simpleData.is_press ) { // Escape key closes app if focus on toplevel QWidget * active = activeWindow(); if ( active && active->testWFlags( WType_TopLevel ) && ( int ) active->winId() == ke->simpleData.window && !active->testWFlags( WStyle_Dialog | WStyle_Customize | WType_Popup | WType_Desktop ) ) { if ( active->inherits( "QDialog" ) ) { HackDialog * d = ( HackDialog * ) active; d->rejectIt(); return TRUE; } else if ( strcmp( argv() [ 0 ], "embeddedkonsole" ) != 0 ) { active->close(); } } } else if ( ke->simpleData.keycode >= Qt::Key_F1 && ke->simpleData.keycode <= Qt::Key_F29 ) { // this should be if ( ODevice::inst ( )-> buttonForKeycode ( ... )) // but we cannot access libopie function within libqpe :( QWidget * active = activeWindow ( ); if ( active && ((int) active-> winId ( ) == ke-> simpleData.window )) { if ( d-> kbgrabbed ) { // we grabbed the keyboard QChar ch ( ke-> simpleData.unicode ); QKeyEvent qke ( ke-> simpleData. is_press ? QEvent::KeyPress : QEvent::KeyRelease, ke-> simpleData.keycode, ch. latin1 ( ), ke-> simpleData.modifiers, QString ( ch ), ke-> simpleData.is_auto_repeat, 1 ); QObject *which = QWidget::keyboardGrabber ( ); if ( !which ) which = QApplication::focusWidget ( ); if ( !which ) which = QApplication::activeWindow ( ); if ( !which ) which = qApp; QApplication::sendEvent ( which, &qke ); } else { // we didn't grab the keyboard, so send the event to the launcher QCopEnvelope e ( "QPE/Launcher", "deviceButton(int,int,int)" ); e << int( ke-> simpleData.keycode ) << int( ke-> simpleData. is_press ) << int( ke-> simpleData.is_auto_repeat ); } } return true; } } if ( e->type == QWSEvent::Focus ) { QWSFocusEvent * fe = ( QWSFocusEvent* ) e; if ( !fe->simpleData.get_focus ) { QWidget * active = activeWindow(); while ( active && active->isPopup() ) { active->close(); active = activeWindow(); } } else { // make sure our modal widget is ALWAYS on top QWidget *topm = activeModalWidget(); if ( topm ) { topm->raise(); } } if ( fe->simpleData.get_focus && inputMethodDict ) { InputMethodHint m = inputMethodHint( QWidget::find( e->window() ) ); if ( m == AlwaysOff ) Global::hideInputMethod(); if ( m == AlwaysOn ) Global::showInputMethod(); } } return QApplication::qwsEventFilter( e ); } #endif /*! Destroys the QPEApplication. */ QPEApplication::~QPEApplication() { ungrabKeyboard(); #if defined(Q_WS_QWS) && !defined(QT_NO_COP) // Need to delete QCopChannels early, since the display will // be gone by the time we get to ~QObject(). delete sysChannel; delete pidChannel; #endif delete d; } /*! Returns <tt>$OPIEDIR/</tt>. */ QString QPEApplication::qpeDir() { const char * base = getenv( "OPIEDIR" ); if ( base ) return QString( base ) + "/"; return QString( "../" ); } /*! Returns the user's current Document directory. There is a trailing "/". .. well, it does now,, and there's no trailing '/' */ QString QPEApplication::documentDir() { const char* base = getenv( "HOME"); if ( base ) return QString( base ) + "/Documents"; return QString( "../Documents" ); } static int deforient = -1; /*! \internal */ int QPEApplication::defaultRotation() { if ( deforient < 0 ) { QString d = getenv( "QWS_DISPLAY" ); if ( d.contains( "Rot90" ) ) { deforient = 90; } else if ( d.contains( "Rot180" ) ) { deforient = 180; } else if ( d.contains( "Rot270" ) ) { deforient = 270; } else { deforient = 0; } } return deforient; } /*! \internal */ void QPEApplication::setDefaultRotation( int r ) { if ( qApp->type() == GuiServer ) { deforient = r; setenv( "QWS_DISPLAY", QString( "Transformed:Rot%1:0" ).arg( r ).latin1(), 1 ); Config config("qpe"); config.setGroup( "Rotation" ); config.writeEntry( "Rot", r ); } else { #ifndef QT_NO_COP { QCopEnvelope e( "QPE/System", "setDefaultRotation(int)" ); e << r; } #endif } } /*! \internal */ void QPEApplication::applyStyle() { Config config( "qpe" ); config.setGroup( "Appearance" ); // don't block ourselves ... Opie::force_appearance = 0; static QString appname = Opie::binaryName ( ); QStringList ex = config. readListEntry ( "NoStyle", ';' ); int nostyle = 0; for ( QStringList::Iterator it = ex. begin ( ); it != ex. end ( ); ++it ) { if ( QRegExp (( *it ). mid ( 1 ), false, true ). find ( appname, 0 ) >= 0 ) { nostyle = ( *it ). left ( 1 ). toInt ( 0, 32 ); break; } } // Widget style QString style = config.readEntry( "Style", "Light" ); // don't set a custom style if ( nostyle & Opie::Force_Style ) style = "Light"; internalSetStyle ( style ); // Colors QColor bgcolor( config.readEntry( "Background", "#E5E1D5" ) ); QColor btncolor( config.readEntry( "Button", "#D6CDBB" ) ); QPalette pal( btncolor, bgcolor ); QString color = config.readEntry( "Highlight", "#800000" ); pal.setColor( QColorGroup::Highlight, QColor( color ) ); color = config.readEntry( "HighlightedText", "#FFFFFF" ); pal.setColor( QColorGroup::HighlightedText, QColor( color ) ); color = config.readEntry( "Text", "#000000" ); pal.setColor( QColorGroup::Text, QColor( color ) ); color = config.readEntry( "ButtonText", "#000000" ); pal.setColor( QPalette::Active, QColorGroup::ButtonText, QColor( color ) ); color = config.readEntry( "Base", "#FFFFFF" ); pal.setColor( QColorGroup::Base, QColor( color ) ); pal.setColor( QPalette::Disabled, QColorGroup::Text, pal.color( QPalette::Active, QColorGroup::Background ).dark() ); setPalette( pal, TRUE ); // Window Decoration QString dec = config.readEntry( "Decoration", "Qtopia" ); // don't set a custom deco if ( nostyle & Opie::Force_Decoration ) dec = ""; //qDebug ( "Setting Deco: %s -- old %s (%d)", dec.latin1(), d-> decorationName.latin1(), nostyle); if ( dec != d->decorationName ) { qwsSetDecoration( new QPEDecoration( dec ) ); d->decorationName = dec; } // Font QString ff = config.readEntry( "FontFamily", font().family() ); int fs = config.readNumEntry( "FontSize", font().pointSize() ); // don't set a custom font if ( nostyle & Opie::Force_Font ) { ff = "Helvetica"; fs = 10; } setFont ( QFont ( ff, fs ), true ); // revert to global blocking policy ... Opie::force_appearance = config. readBoolEntry ( "ForceStyle", false ) ? Opie::Force_All : Opie::Force_None; Opie::force_appearance &= ~nostyle; } void QPEApplication::systemMessage( const QCString& msg, const QByteArray& data ) { #ifdef Q_WS_QWS QDataStream stream( data, IO_ReadOnly ); if ( msg == "applyStyle()" ) { applyStyle(); } else if ( msg == "toggleApplicationMenu()" ) { QWidget *active = activeWindow ( ); if ( active ) { QPEMenuToolFocusManager *man = QPEMenuToolFocusManager::manager ( ); bool oldactive = man-> isActive ( ); man-> setActive( !man-> isActive() ); if ( !oldactive && !man-> isActive ( )) { // no menubar to toggle -> try O-Menu QCopEnvelope e ( "QPE/TaskBar", "toggleStartMenu()" ); } } } else if ( msg == "setDefaultRotation(int)" ) { if ( type() == GuiServer ) { int r; stream >> r; setDefaultRotation( r ); } } else if ( msg == "setCurrentRotation(int)" ) { int r; stream >> r; setCurrentRotation( r ); } else if ( msg == "shutdown()" ) { if ( type() == GuiServer ) shutdown(); } else if ( msg == "quit()" ) { if ( type() != GuiServer ) tryQuit(); } else if ( msg == "forceQuit()" ) { if ( type() != GuiServer ) quit(); } else if ( msg == "restart()" ) { if ( type() == GuiServer ) restart(); } else if ( msg == "language(QString)" ) { if ( type() == GuiServer ) { QString l; stream >> l; QString cl = getenv( "LANG" ); if ( cl != l ) { if ( l.isNull() ) unsetenv( "LANG" ); else setenv( "LANG", l.latin1(), 1 ); restart(); } } } else if ( msg == "timeChange(QString)" ) { QString t; stream >> t; if ( t.isNull() ) unsetenv( "TZ" ); else setenv( "TZ", t.latin1(), 1 ); // emit the signal so everyone else knows... emit timeChanged(); } else if ( msg == "execute(QString)" ) { if ( type() == GuiServer ) { QString t; stream >> t; Global::execute( t ); } } else if ( msg == "execute(QString,QString)" ) { if ( type() == GuiServer ) { QString t, d; stream >> t >> d; Global::execute( t, d ); } } else if ( msg == "addAlarm(QDateTime,QCString,QCString,int)" ) { if ( type() == GuiServer ) { QDateTime when; QCString channel, message; int data; stream >> when >> channel >> message >> data; AlarmServer::addAlarm( when, channel, message, data ); } } else if ( msg == "deleteAlarm(QDateTime,QCString,QCString,int)" ) { if ( type() == GuiServer ) { QDateTime when; QCString channel, message; int data; stream >> when >> channel >> message >> data; AlarmServer::deleteAlarm( when, channel, message, data ); } } else if ( msg == "clockChange(bool)" ) { int tmp; stream >> tmp; emit clockChanged( tmp ); } else if ( msg == "weekChange(bool)" ) { int tmp; stream >> tmp; emit weekChanged( tmp ); } else if ( msg == "setDateFormat(DateFormat)" ) { DateFormat tmp; stream >> tmp; emit dateFormatChanged( tmp ); } else if ( msg == "setVolume(int,int)" ) { int t, v; stream >> t >> v; setVolume( t, v ); emit volumeChanged( muted ); } else if ( msg == "volumeChange(bool)" ) { stream >> muted; setVolume(); emit volumeChanged( muted ); } else if ( msg == "setMic(int,int)" ) { // Added: 2002-02-08 by Jeremy Cowgar <jc@cowgar.com> int t, v; stream >> t >> v; setMic( t, v ); emit micChanged( micMuted ); } else if ( msg == "micChange(bool)" ) { // Added: 2002-02-08 by Jeremy Cowgar <jc@cowgar.com> stream >> micMuted; setMic(); emit micChanged( micMuted ); } else if ( msg == "setBass(int,int)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> int t, v; stream >> t >> v; setBass( t, v ); } else if ( msg == "bassChange(bool)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> setBass(); } else if ( msg == "setTreble(int,int)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> int t, v; stream >> t >> v; setTreble( t, v ); } else if ( msg == "trebleChange(bool)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> setTreble(); } #endif } /*! \internal */ bool QPEApplication::raiseAppropriateWindow() { bool r = FALSE; // ########## raise()ing main window should raise and set active // ########## it and then all childen. This belongs in Qt/Embedded QWidget *top = d->qpe_main_widget; if ( !top ) top = mainWidget(); if ( top && d->keep_running ) { if ( top->isVisible() ) r = TRUE; else if (d->preloaded) { // We are preloaded and not visible.. pretend we just started.. QCopEnvelope e("QPE/System", "fastAppShowing(QString)"); e << d->appName; } d->show_mx(top, d->nomaximize); top->raise(); top->setActiveWindow(); } QWidget *topm = activeModalWidget(); if ( topm && topm != top ) { topm->show(); topm->raise(); topm->setActiveWindow(); // If we haven't already handled the fastAppShowing message if (!top && d->preloaded) { QCopEnvelope e("QPE/System", "fastAppShowing(QString)"); e << d->appName; } r = FALSE; } return r; } void QPEApplication::pidMessage( const QCString& msg, const QByteArray& data) { #ifdef Q_WS_QWS if ( msg == "quit()" ) { tryQuit(); } else if ( msg == "quitIfInvisible()" ) { if ( d->qpe_main_widget && !d->qpe_main_widget->isVisible() ) quit(); } else if ( msg == "close()" ) { hideOrQuit(); } else if ( msg == "disablePreload()" ) { d->preloaded = FALSE; d->keep_running = TRUE; /* so that quit will quit */ } else if ( msg == "enablePreload()" ) { if (d->qpe_main_widget) d->preloaded = TRUE; d->keep_running = TRUE; /* so next quit won't quit */ } else if ( msg == "raise()" ) { d->keep_running = TRUE; d->notbusysent = FALSE; raiseAppropriateWindow(); // Tell the system we're still chugging along... QCopEnvelope e("QPE/System", "appRaised(QString)"); e << d->appName; } else if ( msg == "flush()" ) { emit flush(); // we need to tell the desktop QCopEnvelope e( "QPE/Desktop", "flushDone(QString)" ); e << d->appName; } else if ( msg == "reload()" ) { emit reload(); } else if ( msg == "setDocument(QString)" ) { d->keep_running = TRUE; QDataStream stream( data, IO_ReadOnly ); QString doc; stream >> doc; QWidget *mw = mainWidget(); if ( !mw ) mw = d->qpe_main_widget; if ( mw ) Global::setDocument( mw, doc ); } else if ( msg == "nextView()" ) { qDebug("got nextView()"); /* if ( raiseAppropriateWindow() ) */ emit appMessage( msg, data); } else { emit appMessage( msg, data); } #endif } /*! Sets widget \a mw as the mainWidget() and shows it. For small windows, consider passing TRUE for \a nomaximize rather than the default FALSE. \sa showMainDocumentWidget() */ void QPEApplication::showMainWidget( QWidget* mw, bool nomaximize ) { d->show(mw, nomaximize ); } /*! Sets widget \a mw as the mainWidget() and shows it. For small windows, consider passing TRUE for \a nomaximize rather than the default FALSE. This calls designates the application as a \link docwidget.html document-oriented\endlink application. The \a mw widget \e must have this slot: setDocument(const QString&). \sa showMainWidget() */ void QPEApplication::showMainDocumentWidget( QWidget* mw, bool nomaximize ) { if ( mw && argc() == 2 ) Global::setDocument( mw, QString::fromUtf8(argv()[1]) ); d->show(mw, nomaximize ); } /*! If an application is started via a \link qcop.html QCop\endlink message, the application will process the \link qcop.html QCop\endlink message and then quit. If the application calls this function while processing a \link qcop.html QCop\endlink message, after processing its outstanding \link qcop.html QCop\endlink messages the application will start 'properly' and show itself. \sa keepRunning() */ void QPEApplication::setKeepRunning() { if ( qApp && qApp->inherits( "QPEApplication" ) ) { QPEApplication * qpeApp = ( QPEApplication* ) qApp; qpeApp->d->keep_running = TRUE; } } /*! Returns TRUE if the application will quit after processing the current list of qcop messages; otherwise returns FALSE. \sa setKeepRunning() */ bool QPEApplication::keepRunning() const { return d->keep_running; } /*! \internal */ void QPEApplication::internalSetStyle( const QString &style ) { #if QT_VERSION >= 300 if ( style == "QPE" ) { setStyle( new QPEStyle ); } else { QStyle *s = QStyleFactory::create( style ); if ( s ) setStyle( s ); } #else if ( style == "Windows" ) { setStyle( new QWindowsStyle ); } else if ( style == "QPE" ) { setStyle( new QPEStyle ); } else if ( style == "Light" ) { setStyle( new LightStyle ); } #ifndef QT_NO_STYLE_PLATINUM else if ( style == "Platinum" ) { setStyle( new QPlatinumStyle ); } #endif #ifndef QT_NO_STYLE_MOTIF else if ( style == "Motif" ) { setStyle( new QMotifStyle ); } #endif #ifndef QT_NO_STYLE_MOTIFPLUS else if ( style == "MotifPlus" ) { setStyle( new QMotifPlusStyle ); } #endif else { QStyle *sty = 0; QString path = QPEApplication::qpeDir ( ) + "/plugins/styles/"; if ( style. find ( ".so" ) > 0 ) path += style; else path = path + "lib" + style. lower ( ) + ".so"; // compatibility static QLibrary *lastlib = 0; static StyleInterface *lastiface = 0; QLibrary *lib = new QLibrary ( path ); StyleInterface *iface = 0; if (( lib-> queryInterface ( IID_Style, ( QUnknownInterface ** ) &iface ) == QS_OK ) && iface ) sty = iface-> style ( ); if ( sty ) { setStyle ( sty ); if ( lastiface ) lastiface-> release ( ); lastiface = iface; if ( lastlib ) { lastlib-> unload ( ); delete lastlib; } lastlib = lib; } else { if ( iface ) iface-> release ( ); delete lib; setStyle ( new LightStyle ( )); } } #endif } /*! \internal */ void QPEApplication::prepareForTermination( bool willrestart ) { if ( willrestart ) { // Draw a big wait icon, the image can be altered in later revisions // QWidget *d = QApplication::desktop(); QImage img = Resource::loadImage( "launcher/new_wait" ); QPixmap pix; pix.convertFromImage( img.smoothScale( 1 * img.width(), 1 * img.height() ) ); QLabel *lblWait = new QLabel( 0, "wait hack!", QWidget::WStyle_Customize | QWidget::WStyle_NoBorder | QWidget::WStyle_Tool ); lblWait->setPixmap( pix ); lblWait->setAlignment( QWidget::AlignCenter ); lblWait->show(); lblWait->showMaximized(); } #ifndef SINGLE_APP { QCopEnvelope envelope( "QPE/System", "forceQuit()" ); } processEvents(); // ensure the message goes out. sleep( 1 ); // You have 1 second to comply. #endif } /*! \internal */ void QPEApplication::shutdown() { // Implement in server's QPEApplication subclass } /*! \internal */ void QPEApplication::restart() { // Implement in server's QPEApplication subclass } static QPtrDict<void>* stylusDict = 0; static void createDict() { if ( !stylusDict ) stylusDict = new QPtrDict<void>; } /*! Returns the current StylusMode for widget \a w. \sa setStylusOperation() StylusMode */ QPEApplication::StylusMode QPEApplication::stylusOperation( QWidget* w ) { if ( stylusDict ) return ( StylusMode ) ( int ) stylusDict->find( w ); return LeftOnly; } /*! \enum QPEApplication::StylusMode \value LeftOnly the stylus only generates LeftButton events (the default). \value RightOnHold the stylus generates RightButton events if the user uses the press-and-hold gesture. \sa setStylusOperation() stylusOperation() */ /*! Causes widget \a w to receive mouse events according to the stylus \a mode. \sa stylusOperation() StylusMode */ void QPEApplication::setStylusOperation( QWidget * w, StylusMode mode ) { createDict(); if ( mode == LeftOnly ) { stylusDict->remove ( w ); w->removeEventFilter( qApp ); } else { stylusDict->insert( w, ( void* ) mode ); connect( w, SIGNAL( destroyed() ), qApp, SLOT( removeSenderFromStylusDict() ) ); w->installEventFilter( qApp ); } } /*! \reimp */ bool QPEApplication::eventFilter( QObject *o, QEvent *e ) { if ( stylusDict && e->type() >= QEvent::MouseButtonPress && e->type() <= QEvent::MouseMove ) { QMouseEvent * me = ( QMouseEvent* ) e; StylusMode mode = (StylusMode)(int)stylusDict->find(o); switch (mode) { case RightOnHold: switch ( me->type() ) { case QEvent::MouseButtonPress: if ( me->button() == LeftButton ) { d->presstimer = startTimer(500); // #### pref. d->presswidget = (QWidget*)o; d->presspos = me->pos(); d->rightpressed = FALSE; } break; case QEvent::MouseMove: if (d->presstimer && (me->pos() - d->presspos).manhattanLength() > 8) { killTimer(d->presstimer); d->presstimer = 0; } break; case QEvent::MouseButtonRelease: if ( me->button() == LeftButton ) { if ( d->presstimer ) { killTimer(d->presstimer); d->presstimer = 0; } if ( d->rightpressed && d->presswidget ) { // Right released postEvent( d->presswidget, new QMouseEvent( QEvent::MouseButtonRelease, me->pos(), RightButton, LeftButton + RightButton ) ); // Left released, off-widget postEvent( d->presswidget, new QMouseEvent( QEvent::MouseMove, QPoint( -1, -1), LeftButton, LeftButton ) ); postEvent( d->presswidget, new QMouseEvent( QEvent::MouseButtonRelease, QPoint( -1, -1), LeftButton, LeftButton ) ); d->rightpressed = FALSE; return TRUE; // don't send the real Left release } } break; default: break; } break; default: ; } } else if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease ) { QKeyEvent *ke = (QKeyEvent *)e; if ( ke->key() == Key_Enter ) { if ( o->isA( "QRadioButton" ) || o->isA( "QCheckBox" ) ) { postEvent( o, new QKeyEvent( e->type(), Key_Space, ' ', ke->state(), " ", ke->isAutoRepeat(), ke->count() ) ); return TRUE; } } } return FALSE; } /*! \reimp */ void QPEApplication::timerEvent( QTimerEvent *e ) { if ( e->timerId() == d->presstimer && d->presswidget ) { // Right pressed postEvent( d->presswidget, new QMouseEvent( QEvent::MouseButtonPress, d->presspos, RightButton, LeftButton ) ); killTimer( d->presstimer ); d->presstimer = 0; d->rightpressed = TRUE; } } void QPEApplication::removeSenderFromStylusDict() { stylusDict->remove ( ( void* ) sender() ); if ( d->presswidget == sender() ) d->presswidget = 0; } /*! \internal */ bool QPEApplication::keyboardGrabbed() const { return d->kbgrabbed; } /*! Reverses the effect of grabKeyboard(). This is called automatically on program exit. */ void QPEApplication::ungrabKeyboard() { ((QPEApplication *) qApp )-> d-> kbgrabbed = false; } /*! Grabs the physical keyboard keys, e.g. the application's launching keys. Instead of launching applications when these keys are pressed the signals emitted are sent to this application instead. Some games programs take over the launch keys in this way to make interaction easier. \sa ungrabKeyboard() */ void QPEApplication::grabKeyboard() { ((QPEApplication *) qApp )-> d-> kbgrabbed = true; } /*! \reimp */ int QPEApplication::exec() { #ifndef QT_NO_COP d->sendQCopQ(); #endif if ( d->keep_running ) //|| d->qpe_main_widget && d->qpe_main_widget->isVisible() ) return QApplication::exec(); #ifndef QT_NO_COP { QCopEnvelope e( "QPE/System", "closing(QString)" ); e << d->appName; } #endif processEvents(); return 0; } /*! \internal External request for application to quit. Quits if possible without loosing state. */ void QPEApplication::tryQuit() { if ( activeModalWidget() || strcmp( argv() [ 0 ], "embeddedkonsole" ) == 0 ) return ; // Inside modal loop or konsole. Too hard to save state. #ifndef QT_NO_COP { QCopEnvelope e( "QPE/System", "closing(QString)" ); e << d->appName; } #endif processEvents(); quit(); } /*! \internal User initiated quit. Makes the window 'Go Away'. If preloaded this means hiding the window. If not it means quitting the application. As this is user initiated we don't need to check state. */ void QPEApplication::hideOrQuit() { processEvents(); // If we are a preloaded application we don't actually quit, so emit // a System message indicating we're quasi-closing. if ( d->preloaded && d->qpe_main_widget ) #ifndef QT_NO_COP { QCopEnvelope e("QPE/System", "fastAppHiding(QString)" ); e << d->appName; d->qpe_main_widget->hide(); } #endif else quit(); } #if defined(QT_QWS_IPAQ) || defined(QT_QWS_SL5XXX) // The libraries with the skiff package (and possibly others) have // completely useless implementations of builtin new and delete that // use about 50% of your CPU. Here we revert to the simple libc // functions. void* operator new[]( size_t size ) { return malloc( size ); } void* operator new( size_t size ) { return malloc( size ); } void operator delete[]( void* p ) { free( p ); } void operator delete[]( void* p, size_t /*size*/ ) { free( p ); } + void operator delete( void* p ) { free( p ); } void operator delete( void* p, size_t /*size*/ ) { free( p ); } #endif #if ( QT_VERSION <= 230 ) && !defined(SINGLE_APP) #include <qwidgetlist.h> #ifdef QWS #include <qgfx_qws.h> extern QRect qt_maxWindowRect; void qt_setMaxWindowRect(const QRect& r ) { qt_maxWindowRect = qt_screen->mapFromDevice( r, qt_screen->mapToDevice( QSize( qt_screen->width(), qt_screen->height() ) ) ); // Re-resize any maximized windows QWidgetList* l = QApplication::topLevelWidgets(); if ( l ) { QWidget * w = l->first(); while ( w ) { if ( w->isVisible() && w->isMaximized() ) { w->showMaximized(); } w = l->next(); } delete l; } } #endif #endif diff --git a/library/storage.cpp b/library/storage.cpp index f4c1c02..12f9df9 100644 --- a/library/storage.cpp +++ b/library/storage.cpp @@ -1,261 +1,261 @@ /********************************************************************** ** Copyright (C) Holger 'zecke' Freyther <freyther@kde.org> ** Copyright (C) Lorn Potter <llornkcor@handhelds.org> ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Opie 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 <qpe/storage.h> #ifdef QT_QWS_SL5XXX #include <qpe/custom.h> #endif #include <qfile.h> #include <qtimer.h> #include <qcopchannel_qws.h> #include <stdio.h> #if defined(_OS_LINUX_) || defined(Q_OS_LINUX) #include <sys/vfs.h> #include <mntent.h> #endif #include <qstringlist.h> #include <sys/vfs.h> #include <mntent.h> static bool isCF(const QString& m) { FILE* f = fopen("/var/run/stab", "r"); if (!f) f = fopen("/var/state/pcmcia/stab", "r"); if (!f) f = fopen("/var/lib/pcmcia/stab", "r"); if ( f ) { char line[1024]; char devtype[80]; char devname[80]; while ( fgets( line, 1024, f ) ) { // 0 ide ide-cs 0 hda 3 0 if ( sscanf(line,"%*d %s %*s %*s %s", devtype, devname )==2 ) { if ( QString(devtype) == "ide" && m.find(devname)>0 ) { fclose(f); return TRUE; } } } fclose(f); } return FALSE; } StorageInfo::StorageInfo( QObject *parent ) : QObject( parent ) { mFileSystems.setAutoDelete( TRUE ); channel = new QCopChannel( "QPE/Card", this ); connect( channel, SIGNAL(received(const QCString &, const QByteArray &)), this, SLOT(cardMessage( const QCString &, const QByteArray &)) ); update(); } const FileSystem *StorageInfo::fileSystemOf( const QString &filename ) { for (QListIterator<FileSystem> i(mFileSystems); i.current(); ++i) { if ( filename.startsWith( (*i)->path() ) ) return (*i); } return 0; } void StorageInfo::cardMessage( const QCString& msg, const QByteArray& ) { if ( msg == "mtabChanged()" ) update(); } // cause of the lack of a d pointer we need // to store informations in a config file :( void StorageInfo::update() { //qDebug("StorageInfo::updating"); #if defined(_OS_LINUX_) || defined(Q_OS_LINUX) struct mntent *me; FILE *mntfp = setmntent( "/etc/mtab", "r" ); QStringList curdisks; QStringList curopts; QStringList curfs; bool rebuild = FALSE; int n=0; if ( mntfp ) { while ( (me = getmntent( mntfp )) != 0 ) { QString fs = me->mnt_fsname; if ( fs.left(7)=="/dev/hd" || fs.left(7)=="/dev/sd" || fs.left(8)=="/dev/mtd" || fs.left(9) == "/dev/mmcd" || fs.left( 14 ) == "/dev/mmc/part1" || fs.left(5)=="tmpfs" || fs.left(9)=="/dev/root" ) { n++; curdisks.append(fs); curopts.append( me->mnt_opts ); //qDebug("-->fs %s opts %s", fs.latin1(), me->mnt_opts ); curfs.append( me->mnt_dir ); bool found = FALSE; for (QListIterator<FileSystem> i(mFileSystems); i.current(); ++i) { if ( (*i)->disk() == fs ) { found = TRUE; break; } } if ( !found ) rebuild = TRUE; } } endmntent( mntfp ); } if ( rebuild || n != (int)mFileSystems.count() ) { mFileSystems.clear(); QStringList::ConstIterator it=curdisks.begin(); QStringList::ConstIterator fsit=curfs.begin(); QStringList::ConstIterator optsIt=curopts.begin(); for (; it!=curdisks.end(); ++it, ++fsit, ++optsIt) { QString opts = *optsIt; QString disk = *it; QString humanname; bool removable = FALSE; if ( isCF(disk) ) { humanname = tr("CF Card"); removable = TRUE; } else if ( disk == "/dev/hda1" ) { humanname = tr("Hard Disk"); } else if ( disk.left(9) == "/dev/mmcd" ) { humanname = tr("SD Card"); removable = TRUE; } else if ( disk.left( 14 ) == "/dev/mmc/part1" ) { humanname = tr("MMC Card"); removable = TRUE; } else if ( disk.left(7) == "/dev/hd" ) humanname = tr("Hard Disk") + " " + disk; else if ( disk.left(7) == "/dev/sd" ) humanname = tr("SCSI Hard Disk") + " " + disk; else if ( disk.left(14) == "/dev/mtdblock6" ) //openzaurus ramfs humanname = tr("Internal Memory"); else if ( disk == "/dev/mtdblock1" || humanname == "/dev/mtdblock/1" ) humanname = tr("Internal Storage"); else if ( disk.left(14) == "/dev/mtdblock/" ) humanname = tr("Internal Storage") + " " + disk; else if ( disk.left(13) == "/dev/mtdblock" ) humanname = tr("Internal Storage") + " " + disk; else if ( disk.left(9) == "/dev/root" ) humanname = tr("Internal Storage") + " " + disk; else if ( disk.left(5) == "tmpfs" ) //ipaqs /mnt/ramfs humanname = tr("Internal Memory"); FileSystem *fs = new FileSystem( disk, *fsit, humanname, removable, opts ); mFileSystems.append( fs ); } emit disksChanged(); } else { // just update them for (QListIterator<FileSystem> i(mFileSystems); i.current(); ++i) i.current()->update(); } #endif } bool deviceTab( const char *device) { QString name = device; bool hasDevice=false; struct mntent *me; FILE *mntfp = setmntent( "/etc/mtab", "r" ); if ( mntfp ) { while ( (me = getmntent( mntfp )) != 0 ) { QString deviceName = me->mnt_fsname; // qDebug(deviceName); if( deviceName.left(name.length()) == name) { hasDevice = true; } } } endmntent( mntfp ); return hasDevice; } /*! - * @fn hasCf() + * @fn static bool StorageInfo::hasCf() * @brief returns whether device has Cf mounted * */ bool StorageInfo::hasCf() { return deviceTab("/dev/hd"); } /*! - * @fn hasSd() + * @fn static bool StorageInfo::hasSd() * @brief returns whether device has SD mounted * */ bool StorageInfo::hasSd() { return deviceTab("/dev/mmcd"); } /*! - * @fn hasMmc() + * @fn static bool StorageInfo::hasMmc() * @brief reutrns whether device has mmc mounted * */ bool StorageInfo::hasMmc() { bool hasMmc=false; if( deviceTab("/dev/mmc/part")) hasMmc=true; if( deviceTab("/dev/mmcd")) hasMmc=true; return hasMmc; } //--------------------------------------------------------------------------- FileSystem::FileSystem( const QString &disk, const QString &path, const QString &name, bool rem, const QString &o ) : fsdisk( disk ), fspath( path ), humanname( name ), blkSize(512), totalBlks(0), availBlks(0), removable( rem ), opts( o ) { update(); } void FileSystem::update() { #if defined(_OS_LINUX_) || defined(Q_OS_LINUX) struct statfs fs; if ( !statfs( fspath.latin1(), &fs ) ) { blkSize = fs.f_bsize; totalBlks = fs.f_blocks; availBlks = fs.f_bavail; } else { blkSize = 0; totalBlks = 0; availBlks = 0; } #endif } |