-rw-r--r-- | library/applnk.cpp | 83 |
1 files changed, 56 insertions, 27 deletions
diff --git a/library/applnk.cpp b/library/applnk.cpp index 2af6cf4..5185b5f 100644 --- a/library/applnk.cpp +++ b/library/applnk.cpp @@ -1,153 +1,165 @@ /********************************************************************** ** 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. ** **********************************************************************/ +#define QTOPIA_INTERNAL_MIMEEXT + #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> #ifdef Q_WS_QWS #include <qgfx_qws.h> #endif #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("[^0-9A-Za-z.]"),"_"); // Njaard says this is broken 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: QArray<int> mCat; }; /*! \class AppLnk applnk.h \brief The AppLnk class represents an application available on the system. Information about applications are stored in Qtopia as ".desktop" files. When read, these files are stored as AppLnk objects. */ /*! Sets the size used for small icons to \a small pixels. Only affects AppLnk objects created after the call. */ void AppLnk::setSmallIconSize(int small) { smallSize = small; } /*! Returns the size used for small icons. */ int AppLnk::smallIconSize() { return smallSize; } /*! Sets the size used for large icons to \a big pixels. Only affects AppLnk objects created after the call. */ void AppLnk::setBigIconSize(int big) { bigSize = big; } /*! Returns the size used for large icons. */ int AppLnk::bigIconSize() { return bigSize; } /*! \fn QString AppLnk::name() const Returns the Name property. */ /*! \fn QString AppLnk::exec() const Returns the Exec property. This is the executable program associated with the AppLnk. */ /*! \fn QString AppLnk::rotation() const Returns the Rotation property. */ /*! \fn QString AppLnk::comment() const Returns the Comment property. */ /*! \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. */ const QArray<int>& AppLnk::categories() const { 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. */ @@ -190,512 +202,529 @@ AppLnk::AppLnk( const QString &file ) mComment = config.readEntry( "Comment", QString::null ); mMimeTypes = config.readListEntry( "MimeType", ';' ); mMimeTypeIcons = config.readListEntry( "MimeTypeIcons", ';' ); mLinkFile = file; mFile = config.readEntry("File", QString::null); sl = config.readListEntry("Categories", ';'); } } // let's try our darndest to create categories... Categories cat( 0 ); cat.load( categoryFileName() ); d->mCat.resize( sl.count() ); int i; QStringList::Iterator it; for ( i = 0, it = sl.begin(); it != sl.end(); ++it, i++ ) { bool number; int id = (*it).toInt( &number ); if ( !number ) { // convert from old school... id = cat.id( "Document Viewer", *it ); if ( id == 0 ) id = cat.addCategory( "Document Viewer", *it ); } d->mCat[i] = id; } mId = 0; } /*! Returns a small pixmap associated with the application. */ const QPixmap& AppLnk::pixmap() const { if ( mPixmap.isNull() ) { AppLnk* that = (AppLnk*)this; if ( mIconFile.isEmpty() ) { MimeType mt(type()); that->mPixmap = mt.pixmap(); if ( that->mPixmap.isNull() ) that->mPixmap = Resource::loadPixmap("UnknownDocument-14"); return that->mPixmap; } QImage unscaledIcon = Resource::loadImage( that->mIconFile ); that->mPixmap.convertFromImage( unscaledIcon.smoothScale( smallSize, smallSize ) ); that->mBigPixmap.convertFromImage( unscaledIcon.smoothScale( bigSize, bigSize ) ); return that->mPixmap; } return mPixmap; } /*! Returns a large pixmap associated with the application. */ const QPixmap& AppLnk::bigPixmap() const { if ( mBigPixmap.isNull() ) { AppLnk* that = (AppLnk*)this; if ( mIconFile.isEmpty() ) { MimeType mt(type()); that->mBigPixmap = mt.bigPixmap(); if ( that->mBigPixmap.isNull() ) that->mBigPixmap = Resource::loadPixmap("UnknownDocument"); return that->mBigPixmap; } QImage unscaledIcon = Resource::loadImage( that->mIconFile ); that->mPixmap.convertFromImage( unscaledIcon.smoothScale( smallSize, smallSize ) ); that->mBigPixmap.convertFromImage( unscaledIcon.smoothScale( bigSize, bigSize ) ); return that->mBigPixmap; } return mBigPixmap; } /*! Returns the type of the application. */ QString AppLnk::type() const { if ( mType.isNull() ) { AppLnk* that = (AppLnk*)this; MimeType mt(file()); that->mType = mt.id(); return that->mType; } return mType; } /*! Returns the file associated with the AppLnk. \sa exec() */ QString AppLnk::file() const { if ( mFile.isNull() ) { - AppLnk* that = (AppLnk*)this; + AppLnk* that = (AppLnk*)this; // copy? + 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; } else if ( mType.contains('/') ) { that->mFile = QString(getenv("HOME"))+"/Documents/"+mType+"/"+safeFileName(that->mName); - if ( QFile::exists(that->mFile) || QFile::exists(that->mFile+".desktop") ) { + if ( QFile::exists(that->mFile+ext) || QFile::exists(that->mFile+".desktop") ) { // a .desktop with the same name exists int n=1; + qWarning("AppLnk::file() n=1 %s", that->mFile.latin1() ); QString nn; - while (QFile::exists((nn=(that->mFile+"_"+QString::number(n)))) + while (QFile::exists((nn=(that->mFile+"_"+QString::number(n)))+ext) || QFile::exists(nn+".desktop")) n++; that->mFile = nn; + qWarning("AppLnl::file() now mFile is %s", that->mFile.latin1() ); } that->mLinkFile = that->mFile+".desktop"; + that->mFile += ext; } + prepareDirectories(that->mFile); + QFile f(that->mFile); + if ( !f.open(IO_WriteOnly) ) + that->mFile = QString::null; return that->mFile; } return mFile; } /*! Returns the desktop file coresponding to this AppLnk. \sa file(), exec() */ QString AppLnk::linkFile() const { if ( mLinkFile.isNull() ) { AppLnk* that = (AppLnk*)this; if ( type().contains('/') ) { StorageInfo storage; const FileSystem *fs = storage.fileSystemOf( that->mFile ); -// qDebug("creating lnkFile for %s", mFile.latin1() ); -// if ( fs ) -// qDebug("fs is %s", fs->path().latin1() ); if ( fs && fs->isRemovable() ) { -// qDebug("isRemovable"); that->mLinkFile = fs->path(); } else that->mLinkFile = getenv( "HOME" ); that->mLinkFile += "/Documents/"+type()+"/"+safeFileName(that->mName); - if ( QFile::exists(that->mLinkFile+".desktop") ) { + if ( QFile::exists(that->mLinkFile+".desktop") ) { // ok the file exists lets check if we point to the same file int n=1; QString nn; - while (QFile::exists((nn=that->mLinkFile+"_"+QString::number(n))+".desktop")) + AppLnk lnk( that->mLinkFile+".desktop" ); + if(that->file() != lnk.file() ){ + qWarning("AppLnk::linkFile exists %s", that->mLinkFile.latin1() ); + while (QFile::exists((nn=that->mLinkFile+"_"+QString::number(n))+".desktop")){ n++; - that->mLinkFile = nn; + AppLnk lnk(nn ); // just to be sure + if(lnk.file() ==that->file() ){ + break; + } + } + that->mLinkFile = nn; + } } that->mLinkFile += ".desktop"; -// qDebug("file is %s", mLinkFile.latin1() ); + qWarning("AppLnk::linkFile is %s", that->mLinkFile.latin1() ); + 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; } /*! Destroys the AppLnk. Note that if the AppLnk is current 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; } /*! Executes the application associated with this AppLnk. */ void AppLnk::execute() const { execute(QStringList()); } /*! Executes the application associated with this AppLnk, with \a args as arguments. */ 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, 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() */ void AppLnk::setExec( const QString& exec ) { mExec = exec; } /*! 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() */ 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. \sa comment() */ void AppLnk::setComment( const QString& comment ) { mComment = comment; } /*! Sets the Type property to \a type. \sa type() */ void AppLnk::setType( const QString& type ) { mType = type; } /*! Sets the Icon property to \a iconname. \sa pixmap(), bigPixmap() */ void AppLnk::setIcon( const QString& iconname ) { mIconFile = iconname; QImage unscaledIcon = Resource::loadImage( mIconFile ); mPixmap.convertFromImage( unscaledIcon.smoothScale( smallSize, smallSize ) ); mBigPixmap.convertFromImage( unscaledIcon.smoothScale( bigSize, bigSize ) ); } /*! Sets the Categories property to \a c. \sa categories() */ void AppLnk::setCategories( const QArray<int>& c ) { d->mCat = c; } /*! \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. */ bool AppLnk::ensureLinkExists() const { QString lf = linkFile(); - if ( !QFile::exists(lf) ) { - // May need to create directories - QFileInfo fi(lf); - if ( system(("mkdir -p "+fi.dirPath(TRUE))) ) - return FALSE; - } - return TRUE; + return prepareDirectories(lf); } /*! Commits the AppLnk to disk. Returns whether the operation succeeded. The "linkChanged(QString)" message is sent to the "QPE/System" QCop channel as a result. */ bool AppLnk::writeLink() const { // Only re-writes settable parts QString lf = linkFile(); if ( !ensureLinkExists() ) return FALSE; - Config config( lf, Config::File ); + storeLink(); + return TRUE; +} + +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 ( !mComment.isNull() ) config.writeEntry("Comment",mComment); config.writeEntry("File",file()); // write out the id... int i; QStringList sl; for ( i = 0; i < int(d->mCat.count()); i++ ) { sl.append( QString::number( d->mCat[i] ) ); } config.writeEntry( "Categories", sl, ';' ); QCopEnvelope e("QPE/System", "linkChanged(QString)"); - e << lf; - - return TRUE; + e << mLinkFile; } /*! Sets the property named \a key to \a value. */ 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. */ 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); } /*! Deletes both the linkFile() and file() associated with this AppLnk. */ void AppLnk::removeFiles() { bool valid = isValid(); - if ( !valid || QFile::remove(linkFile()) ) { + if ( !valid || !linkFileKnown() || QFile::remove(linkFile()) ) { if ( QFile::remove(file()) ) { QCopEnvelope e("QPE/System", "linkChanged(QString)"); - e << linkFile(); + if ( linkFileKnown() ) + e << linkFile(); + else + e << file(); } else if ( valid ) { // restore link writeLink(); } } } /*! Delete the linkFile(), leaving any file() untouched. */ void AppLnk::removeLinkFile() { - if ( isValid() && QFile::remove(linkFile()) ) { + if ( isValid() && linkFileKnown() && QFile::remove(linkFile()) ) { QCopEnvelope e("QPE/System", "linkChanged(QString)"); e << linkFile(); } } 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 types in the set. \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 a \a directory (recursively). The directories may contain ".directory" files which overrides any AppLnk::type() value of AppLnk objects found in the directory. This allows simple localization of application types. */ 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 call 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 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) { @@ -990,104 +1019,104 @@ void DocLnkSet::findChildren(const QString &dr, const QValueList<QRegExp> &mimeF /*! \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 \a file is first attempted to be read as a .desktop 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. As with 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 ( QFile::exists( linkFile() ) ) + if ( linkFileKnown() && QFile::exists( linkFile() ) ) a.append(linkFile()); else a.append(file()); app->execute(a); } } |