• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDED

kbuildservicefactory.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999, 2007 David Faure <faure@kde.org>
00003  *                1999 Waldo Bastian <bastian@kde.org>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation;
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 #include "kbuildservicefactory.h"
00021 #include "kbuildservicegroupfactory.h"
00022 #include "kbuildmimetypefactory.h"
00023 #include "ksycoca.h"
00024 #include "ksycocadict.h"
00025 #include "kresourcelist.h"
00026 #include "kdesktopfile.h"
00027 
00028 #include <kglobal.h>
00029 #include <kstandarddirs.h>
00030 #include <klocale.h>
00031 #include <kdebug.h>
00032 #include <assert.h>
00033 #include <kmimetypefactory.h>
00034 
00035 KBuildServiceFactory::KBuildServiceFactory( KSycocaFactory *serviceTypeFactory,
00036                                             KBuildMimeTypeFactory *mimeTypeFactory,
00037                                             KBuildServiceGroupFactory *serviceGroupFactory ) :
00038     KServiceFactory(),
00039     m_nameMemoryHash(),
00040     m_relNameMemoryHash(),
00041     m_menuIdMemoryHash(),
00042     m_dupeDict(),
00043     m_serviceTypeFactory( serviceTypeFactory ),
00044     m_mimeTypeFactory( mimeTypeFactory ),
00045     m_serviceGroupFactory( serviceGroupFactory )
00046 {
00047     m_resourceList = new KSycocaResourceList();
00048     // We directly care about services desktop files.
00049     // All the application desktop files are parsed on demand from the vfolder menu code.
00050     m_resourceList->add( "services", "*.desktop" );
00051 }
00052 
00053 // return all service types for this factory
00054 // i.e. first arguments to m_resourceList->add() above
00055 QStringList KBuildServiceFactory::resourceTypes()
00056 {
00057     return QStringList() << "services";
00058 }
00059 
00060 KBuildServiceFactory::~KBuildServiceFactory()
00061 {
00062     delete m_resourceList;
00063 }
00064 
00065 KService::Ptr KBuildServiceFactory::findServiceByDesktopName(const QString &name)
00066 {
00067     return m_nameMemoryHash.value(name);
00068 }
00069 
00070 KService::Ptr KBuildServiceFactory::findServiceByDesktopPath(const QString &name)
00071 {
00072     return m_relNameMemoryHash.value(name);
00073 }
00074 
00075 KService::Ptr KBuildServiceFactory::findServiceByMenuId(const QString &menuId)
00076 {
00077     return m_menuIdMemoryHash.value(menuId);
00078 }
00079 
00080 KSycocaEntry* KBuildServiceFactory::createEntry( const QString& file, const char *resource ) const
00081 {
00082     QString name = file;
00083     int pos = name.lastIndexOf('/');
00084     if (pos != -1) {
00085         name = name.mid(pos+1);
00086     }
00087     // Is it a .desktop file?
00088     if (!name.endsWith(".desktop"))
00089         return 0;
00090 
00091     KDesktopFile desktopFile(resource, file);
00092 
00093     KService * serv = new KService(&desktopFile);
00094     //kDebug(7021) << "Creating KService from" << file << "entryPath=" << serv->entryPath();
00095     // Note that the menuId will be set by the vfolder_menu.cpp code just after
00096     // createEntry returns.
00097 
00098     if ( serv->isValid() && !serv->isDeleted() ) {
00099         return serv;
00100     } else {
00101         if (!serv->isDeleted())
00102             kWarning(7012) << "Invalid Service : " << file;
00103         delete serv;
00104         return 0;
00105     }
00106 }
00107 
00108 void KBuildServiceFactory::saveHeader(QDataStream &str)
00109 {
00110     KSycocaFactory::saveHeader(str);
00111 
00112     str << (qint32) m_nameDictOffset;
00113     str << (qint32) m_relNameDictOffset;
00114     str << (qint32) m_offerListOffset;
00115     str << (qint32) m_menuIdDictOffset;
00116 }
00117 
00118 void KBuildServiceFactory::save(QDataStream &str)
00119 {
00120     KSycocaFactory::save(str);
00121 
00122     m_nameDictOffset = str.device()->pos();
00123     m_nameDict->save(str);
00124 
00125     m_relNameDictOffset = str.device()->pos();
00126     m_relNameDict->save(str);
00127 
00128     saveOfferList(str);
00129 
00130     m_menuIdDictOffset = str.device()->pos();
00131     m_menuIdDict->save(str);
00132 
00133     int endOfFactoryData = str.device()->pos();
00134 
00135     // Update header (pass #3)
00136     saveHeader(str);
00137 
00138     // Seek to end.
00139     str.device()->seek(endOfFactoryData);
00140 }
00141 
00142 void KBuildServiceFactory::collectInheritedServices()
00143 {
00144     // For each mimetype, go up the parent-mimetype chains and collect offers.
00145     // For "removed associations" to work, we can't just grab everything from all parents.
00146     // We need to process parents before children, hence the recursive call in
00147     // collectInheritedServices(mime) and the QSet to process a given parent only once.
00148     QSet<KMimeType::Ptr> visitedMimes;
00149     const KMimeType::List allMimeTypes = m_mimeTypeFactory->allMimeTypes();
00150     KMimeType::List::const_iterator itm = allMimeTypes.begin();
00151     for( ; itm != allMimeTypes.end(); ++itm ) {
00152         const KMimeType::Ptr mimeType = *itm;
00153         collectInheritedServices(mimeType, visitedMimes);
00154     }
00155     // TODO do the same for all/all and all/allfiles, if (!KServiceTypeProfile::configurationMode())
00156 }
00157 
00158 void KBuildServiceFactory::collectInheritedServices(KMimeType::Ptr mimeType, QSet<KMimeType::Ptr>& visitedMimes)
00159 {
00160     if (visitedMimes.contains(mimeType))
00161         return;
00162     visitedMimes.insert(mimeType);
00163 
00164     // With multiple inheritance, the "mimeTypeInheritanceLevel" isn't exactly
00165     // correct (it should only be increased when going up a level, not when iterating
00166     // through the multiple parents at a given level). I don't think we care, though.
00167     int mimeTypeInheritanceLevel = 0;
00168 
00169     const QString mimeTypeName = mimeType->name();
00170     Q_FOREACH(const QString& parent, mimeType->parentMimeTypes()) {
00171         const KMimeType::Ptr parentMimeType =
00172             m_mimeTypeFactory->findMimeTypeByName(parent, KMimeType::ResolveAliases);
00173 
00174         if ( parentMimeType ) {
00175             collectInheritedServices(parentMimeType, visitedMimes);
00176 
00177             ++mimeTypeInheritanceLevel;
00178             const QList<KServiceOffer>& offers = m_offerHash.offersFor(parent);
00179             QList<KServiceOffer>::const_iterator itserv = offers.begin();
00180             const QList<KServiceOffer>::const_iterator endserv = offers.end();
00181             for ( ; itserv != endserv; ++itserv ) {
00182                 if (!m_offerHash.hasRemovedOffer(mimeTypeName, (*itserv).service())) {
00183                     KServiceOffer offer(*itserv);
00184                     offer.setMimeTypeInheritanceLevel(mimeTypeInheritanceLevel);
00185                     //kDebug(7021) << "INHERITANCE: Adding service" << (*itserv).service()->entryPath() << "to" << mimeTypeName << "mimeTypeInheritanceLevel=" << mimeTypeInheritanceLevel;
00186                     m_offerHash.addServiceOffer( mimeTypeName, offer );
00187                 }
00188             }
00189         } else {
00190             kWarning(7012) << "parent mimetype not found:" << parent;
00191             break;
00192         }
00193     }
00194 }
00195 
00196 
00197 void KBuildServiceFactory::populateServiceTypes()
00198 {
00199     // For every service...
00200     KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
00201     const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
00202     for( ; itserv != endserv ; ++itserv ) {
00203         KService::Ptr service = KService::Ptr::staticCast(*itserv);
00204         QVector<KService::ServiceTypeAndPreference> serviceTypeList = service->_k_accessServiceTypes();
00205         //bool hasAllAll = false;
00206         //bool hasAllFiles = false;
00207 
00208         // Add this service to all its servicetypes (and their parents) and to all its mimetypes
00209         for (int i = 0; i < serviceTypeList.count() /*don't cache it, it can change during iteration!*/; ++i) {
00210             const QString stName = serviceTypeList[i].serviceType;
00211             // It could be a servicetype or a mimetype.
00212             KServiceType::Ptr serviceType = KServiceType::serviceType(stName);
00213             if (!serviceType) {
00214                 serviceType = KServiceType::Ptr::staticCast(m_mimeTypeFactory->findMimeTypeByName(stName, KMimeType::ResolveAliases));
00215             }
00216             // TODO. But maybe we should rename all/all to */*, to also support image/*?
00217             // Not sure how to model all/allfiles then, though
00218             // Also this kind of thing isn't in the XDG standards...
00219 #if 0
00220             if (!serviceType) {
00221                 if ( stName == QLatin1String( "all/all" ) ) {
00222                     hasAllAll = true;
00223                     continue;
00224                 } else if ( stName == QLatin1String( "all/allfiles" ) ) {
00225                     hasAllFiles = true;
00226                     continue;
00227                 }
00228             }
00229 #endif
00230 
00231             if (!serviceType) {
00232                 kDebug(7021) << service->entryPath() << "specifies undefined mimetype/servicetype" << stName;
00233                 continue;
00234             }
00235 
00236             const int preference = serviceTypeList[i].preference;
00237             const QString parent = serviceType->parentServiceType();
00238             if (!parent.isEmpty())
00239                 serviceTypeList.append(KService::ServiceTypeAndPreference(preference, parent));
00240 
00241             //kDebug(7021) << "Adding service" << service->entryPath() << "to" << serviceType->name() << "pref=" << preference;
00242             m_offerHash.addServiceOffer(stName, KServiceOffer(service, preference, 0, service->allowAsDefault()) );
00243         }
00244     }
00245 
00246     // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash
00247     KMimeAssociations mimeAssociations(m_offerHash);
00248     mimeAssociations.parseAllMimeAppsList();
00249 
00250     // Now for each mimetype, collect services from parent mimetypes
00251     collectInheritedServices();
00252 
00253     // Now collect the offsets into the (future) offer list
00254     // The loops look very much like the ones in saveOfferList obviously.
00255     int offersOffset = 0;
00256     const int offerEntrySize = sizeof( qint32 ) * 4; // four qint32s, see saveOfferList.
00257 
00258     KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
00259     const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
00260     for( ; itstf != endstf; ++itstf ) {
00261         KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
00262         const int numOffers = m_offerHash.offersFor(entry->name()).count();
00263         if ( numOffers ) {
00264             entry->setServiceOffersOffset( offersOffset );
00265             offersOffset += offerEntrySize * numOffers;
00266         }
00267     }
00268     KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
00269     const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
00270     for( ; itmtf != endmtf; ++itmtf )
00271     {
00272         KMimeType::Ptr entry = KMimeType::Ptr::staticCast( *itmtf );
00273         const int numOffers = m_offerHash.offersFor(entry->name()).count();
00274         if ( numOffers ) {
00275             entry->setServiceOffersOffset( offersOffset );
00276             offersOffset += offerEntrySize * numOffers;
00277         }
00278     }
00279 }
00280 
00281 void KBuildServiceFactory::saveOfferList(QDataStream &str)
00282 {
00283     m_offerListOffset = str.device()->pos();
00284 
00285     // For each entry in servicetypeFactory
00286     KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
00287     const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
00288     for( ; itstf != endstf; ++itstf ) {
00289         // export associated services
00290         const KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
00291         Q_ASSERT( entry );
00292 
00293         QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
00294         qStableSort( offers ); // by initial preference
00295 
00296         for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
00297             it2 != offers.constEnd(); ++it2) {
00298             //kDebug(7021) << "servicetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath();
00299 
00300             str << (qint32) entry->offset();
00301             str << (qint32) (*it2).service()->offset();
00302             str << (qint32) (*it2).preference();
00303             str << (qint32) 0; // mimeTypeInheritanceLevel
00304             // update offerEntrySize in populateServiceTypes if you add/remove something here
00305         }
00306     }
00307 
00308     // For each entry in mimeTypeFactory
00309     KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
00310     const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
00311     for( ; itmtf != endmtf; ++itmtf ) {
00312         // export associated services
00313         const KMimeType::Ptr entry = KMimeType::Ptr::staticCast( *itmtf );
00314         Q_ASSERT( entry );
00315         QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
00316         qStableSort( offers ); // by initial preference
00317 
00318         for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
00319             it2 != offers.constEnd(); ++it2) {
00320             //kDebug(7021) << "mimetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath() << "pref" << (*it2).preference();
00321             Q_ASSERT((*it2).service()->offset() != 0);
00322             str << (qint32) entry->offset();
00323             str << (qint32) (*it2).service()->offset();
00324             str << (qint32) (*it2).preference();
00325             str << (qint32) (*it2).mimeTypeInheritanceLevel();
00326             // update offerEntrySize in populateServiceTypes if you add/remove something here
00327         }
00328     }
00329 
00330     str << (qint32) 0;               // End of list marker (0)
00331 }
00332 
00333 void KBuildServiceFactory::addEntry(const KSycocaEntry::Ptr& newEntry)
00334 {
00335     Q_ASSERT(newEntry);
00336     if (m_dupeDict.contains(newEntry))
00337         return;
00338 
00339     KSycocaFactory::addEntry(newEntry);
00340 
00341     const KService::Ptr service = KService::Ptr::staticCast( newEntry );
00342     m_dupeDict.insert(newEntry);
00343 
00344     if (!service->isDeleted()) {
00345         const QString parent = service->parentApp();
00346         if (!parent.isEmpty())
00347             m_serviceGroupFactory->addNewChild(parent, KSycocaEntry::Ptr::staticCast(service));
00348     }
00349 
00350     const QString name = service->desktopEntryName();
00351     m_nameDict->add( name, newEntry );
00352     m_nameMemoryHash.insert(name, service);
00353 
00354     const QString relName = service->entryPath();
00355     //kDebug(7021) << "adding service" << service->menuId() << "name=" << name << "relName=" << relName;
00356     m_relNameDict->add( relName, newEntry );
00357     m_relNameMemoryHash.insert(relName, service); // for KMimeAssociations
00358 
00359     const QString menuId = service->menuId();
00360     if (!menuId.isEmpty()) {
00361         m_menuIdDict->add( menuId, newEntry );
00362         m_menuIdMemoryHash.insert(menuId, service); // for KMimeAssociations
00363     }
00364 }
00365 

KDED

Skip menu "KDED"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal