• Skip to content
  • Skip to link menu
KDE 4.3 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     m_nameDict = new KSycocaDict();
00053     m_relNameDict = new KSycocaDict();
00054     m_menuIdDict = new KSycocaDict();
00055 }
00056 
00057 // return all service types for this factory
00058 // i.e. first arguments to m_resourceList->add() above
00059 QStringList KBuildServiceFactory::resourceTypes()
00060 {
00061     return QStringList() << "services";
00062 }
00063 
00064 KBuildServiceFactory::~KBuildServiceFactory()
00065 {
00066     delete m_resourceList;
00067 }
00068 
00069 KService::Ptr KBuildServiceFactory::findServiceByDesktopName(const QString &name)
00070 {
00071     return m_nameMemoryHash.value(name);
00072 }
00073 
00074 KService::Ptr KBuildServiceFactory::findServiceByDesktopPath(const QString &name)
00075 {
00076     return m_relNameMemoryHash.value(name);
00077 }
00078 
00079 KService::Ptr KBuildServiceFactory::findServiceByMenuId(const QString &menuId)
00080 {
00081     return m_menuIdMemoryHash.value(menuId);
00082 }
00083 
00084 KSycocaEntry* KBuildServiceFactory::createEntry( const QString& file, const char *resource ) const
00085 {
00086     QString name = file;
00087     int pos = name.lastIndexOf('/');
00088     if (pos != -1) {
00089         name = name.mid(pos+1);
00090     }
00091     // Is it a .desktop file?
00092     if (!name.endsWith(".desktop"))
00093         return 0;
00094 
00095     KDesktopFile desktopFile(resource, file);
00096 
00097     KService * serv = new KService(&desktopFile);
00098     //kDebug(7021) << "Creating KService from" << file << "entryPath=" << serv->entryPath();
00099     // Note that the menuId will be set by the vfolder_menu.cpp code just after
00100     // createEntry returns.
00101 
00102     if ( serv->isValid() && !serv->isDeleted() ) {
00103         return serv;
00104     } else {
00105         if (!serv->isDeleted())
00106             kWarning(7012) << "Invalid Service : " << file;
00107         delete serv;
00108         return 0;
00109     }
00110 }
00111 
00112 void KBuildServiceFactory::saveHeader(QDataStream &str)
00113 {
00114     KSycocaFactory::saveHeader(str);
00115 
00116     str << (qint32) m_nameDictOffset;
00117     str << (qint32) m_relNameDictOffset;
00118     str << (qint32) m_offerListOffset;
00119     str << (qint32) m_menuIdDictOffset;
00120 }
00121 
00122 void KBuildServiceFactory::save(QDataStream &str)
00123 {
00124     KSycocaFactory::save(str);
00125 
00126     m_nameDictOffset = str.device()->pos();
00127     m_nameDict->save(str);
00128 
00129     m_relNameDictOffset = str.device()->pos();
00130     m_relNameDict->save(str);
00131 
00132     saveOfferList(str);
00133 
00134     m_menuIdDictOffset = str.device()->pos();
00135     m_menuIdDict->save(str);
00136 
00137     int endOfFactoryData = str.device()->pos();
00138 
00139     // Update header (pass #3)
00140     saveHeader(str);
00141 
00142     // Seek to end.
00143     str.device()->seek(endOfFactoryData);
00144 }
00145 
00146 void KBuildServiceFactory::collectInheritedServices()
00147 {
00148     // For each mimetype, go up the parent-mimetype chains and collect offers.
00149     // For "removed associations" to work, we can't just grab everything from all parents.
00150     // We need to process parents before children, hence the recursive call in
00151     // collectInheritedServices(mime) and the QSet to process a given parent only once.
00152     QSet<KMimeType::Ptr> visitedMimes;
00153     const KMimeType::List allMimeTypes = m_mimeTypeFactory->allMimeTypes();
00154     KMimeType::List::const_iterator itm = allMimeTypes.begin();
00155     for( ; itm != allMimeTypes.end(); ++itm ) {
00156         const KMimeType::Ptr mimeType = *itm;
00157         collectInheritedServices(mimeType, visitedMimes);
00158     }
00159     // TODO do the same for all/all and all/allfiles, if (!KServiceTypeProfile::configurationMode())
00160 }
00161 
00162 void KBuildServiceFactory::collectInheritedServices(KMimeType::Ptr mimeType, QSet<KMimeType::Ptr>& visitedMimes)
00163 {
00164     if (visitedMimes.contains(mimeType))
00165         return;
00166     visitedMimes.insert(mimeType);
00167 
00168     // With multiple inheritance, the "mimeTypeInheritanceLevel" isn't exactly
00169     // correct (it should only be increased when going up a level, not when iterating
00170     // through the multiple parents at a given level). I don't think we care, though.
00171     int mimeTypeInheritanceLevel = 0;
00172 
00173     const QString mimeTypeName = mimeType->name();
00174     Q_FOREACH(const QString& parent, mimeType->parentMimeTypes()) {
00175         const KMimeType::Ptr parentMimeType =
00176             m_mimeTypeFactory->findMimeTypeByName(parent, KMimeType::ResolveAliases);
00177 
00178         if ( parentMimeType ) {
00179             collectInheritedServices(parentMimeType, visitedMimes);
00180 
00181             ++mimeTypeInheritanceLevel;
00182             const QList<KServiceOffer>& offers = m_offerHash.offersFor(parent);
00183             QList<KServiceOffer>::const_iterator itserv = offers.begin();
00184             const QList<KServiceOffer>::const_iterator endserv = offers.end();
00185             for ( ; itserv != endserv; ++itserv ) {
00186                 if (!m_offerHash.hasRemovedOffer(mimeTypeName, (*itserv).service())) {
00187                     KServiceOffer offer(*itserv);
00188                     offer.setMimeTypeInheritanceLevel(mimeTypeInheritanceLevel);
00189                     //kDebug(7021) << "INHERITANCE: Adding service" << (*itserv).service()->entryPath() << "to" << mimeTypeName << "mimeTypeInheritanceLevel=" << mimeTypeInheritanceLevel;
00190                     m_offerHash.addServiceOffer( mimeTypeName, offer );
00191                 }
00192             }
00193         } else {
00194             kWarning(7012) << "parent mimetype not found:" << parent;
00195             break;
00196         }
00197     }
00198 }
00199 
00200 void KBuildServiceFactory::postProcessServices()
00201 {
00202     // By doing all this here rather than in addEntry (and removing when replacing
00203     // with local override), we only do it for the final applications.
00204 
00205     // For every service...
00206     KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
00207     const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
00208     for( ; itserv != endserv ; ++itserv ) {
00209 
00210         KSycocaEntry::Ptr entry = *itserv;
00211         KService::Ptr service = KService::Ptr::staticCast(entry);
00212 
00213         if (!service->isDeleted()) {
00214             const QString parent = service->parentApp();
00215             if (!parent.isEmpty())
00216                 m_serviceGroupFactory->addNewChild(parent, KSycocaEntry::Ptr::staticCast(service));
00217         }
00218 
00219         const QString name = service->desktopEntryName();
00220         m_nameDict->add(name, entry);
00221         m_nameMemoryHash.insert(name, service);
00222 
00223         const QString relName = service->entryPath();
00224         //kDebug(7021) << "adding service" << service.data() << service->menuId() << "name=" << name << "relName=" << relName;
00225         m_relNameDict->add(relName, entry);
00226         m_relNameMemoryHash.insert(relName, service); // for KMimeAssociations
00227 
00228         const QString menuId = service->menuId();
00229         if (!menuId.isEmpty()) { // empty for services, non-empty for applications
00230             m_menuIdDict->add(menuId, entry);
00231             m_menuIdMemoryHash.insert(menuId, service); // for KMimeAssociations
00232         }
00233     }
00234     populateServiceTypes();
00235 }
00236 
00237 void KBuildServiceFactory::populateServiceTypes()
00238 {
00239     // For every service...
00240     KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
00241     const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
00242     for( ; itserv != endserv ; ++itserv ) {
00243 
00244         KService::Ptr service = KService::Ptr::staticCast(*itserv);
00245         QVector<KService::ServiceTypeAndPreference> serviceTypeList = service->_k_accessServiceTypes();
00246         //bool hasAllAll = false;
00247         //bool hasAllFiles = false;
00248 
00249         // Add this service to all its servicetypes (and their parents) and to all its mimetypes
00250         for (int i = 0; i < serviceTypeList.count() /*don't cache it, it can change during iteration!*/; ++i) {
00251             const QString stName = serviceTypeList[i].serviceType;
00252             // It could be a servicetype or a mimetype.
00253             KServiceType::Ptr serviceType = KServiceType::serviceType(stName);
00254             if (!serviceType) {
00255                 serviceType = KServiceType::Ptr::staticCast(m_mimeTypeFactory->findMimeTypeByName(stName, KMimeType::ResolveAliases));
00256             }
00257             // TODO. But maybe we should rename all/all to */*, to also support image/*?
00258             // Not sure how to model all/allfiles then, though
00259             // Also this kind of thing isn't in the XDG standards...
00260 #if 0
00261             if (!serviceType) {
00262                 if ( stName == QLatin1String( "all/all" ) ) {
00263                     hasAllAll = true;
00264                     continue;
00265                 } else if ( stName == QLatin1String( "all/allfiles" ) ) {
00266                     hasAllFiles = true;
00267                     continue;
00268                 }
00269             }
00270 #endif
00271 
00272             if (!serviceType) {
00273                 kDebug(7021) << service->entryPath() << "specifies undefined mimetype/servicetype" << stName;
00274                 continue;
00275             }
00276 
00277             const int preference = serviceTypeList[i].preference;
00278             const QString parent = serviceType->parentServiceType();
00279             if (!parent.isEmpty())
00280                 serviceTypeList.append(KService::ServiceTypeAndPreference(preference, parent));
00281 
00282             //kDebug(7021) << "Adding service" << service->entryPath() << "to" << serviceType->name() << "pref=" << preference;
00283             m_offerHash.addServiceOffer(stName, KServiceOffer(service, preference, 0, service->allowAsDefault()) );
00284         }
00285     }
00286 
00287     // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash
00288     KMimeAssociations mimeAssociations(m_offerHash);
00289     mimeAssociations.parseAllMimeAppsList();
00290 
00291     // Now for each mimetype, collect services from parent mimetypes
00292     collectInheritedServices();
00293 
00294     // Now collect the offsets into the (future) offer list
00295     // The loops look very much like the ones in saveOfferList obviously.
00296     int offersOffset = 0;
00297     const int offerEntrySize = sizeof( qint32 ) * 4; // four qint32s, see saveOfferList.
00298 
00299     KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
00300     const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
00301     for( ; itstf != endstf; ++itstf ) {
00302         KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
00303         const int numOffers = m_offerHash.offersFor(entry->name()).count();
00304         if ( numOffers ) {
00305             entry->setServiceOffersOffset( offersOffset );
00306             offersOffset += offerEntrySize * numOffers;
00307         }
00308     }
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     {
00313         KMimeType::Ptr entry = KMimeType::Ptr::staticCast( *itmtf );
00314         const int numOffers = m_offerHash.offersFor(entry->name()).count();
00315         if ( numOffers ) {
00316             entry->setServiceOffersOffset( offersOffset );
00317             offersOffset += offerEntrySize * numOffers;
00318         }
00319     }
00320 }
00321 
00322 void KBuildServiceFactory::saveOfferList(QDataStream &str)
00323 {
00324     m_offerListOffset = str.device()->pos();
00325 
00326     // For each entry in servicetypeFactory
00327     KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
00328     const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
00329     for( ; itstf != endstf; ++itstf ) {
00330         // export associated services
00331         const KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
00332         Q_ASSERT( entry );
00333 
00334         QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
00335         qStableSort( offers ); // by initial preference
00336 
00337         for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
00338             it2 != offers.constEnd(); ++it2) {
00339             //kDebug(7021) << "servicetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath();
00340 
00341             str << (qint32) entry->offset();
00342             str << (qint32) (*it2).service()->offset();
00343             str << (qint32) (*it2).preference();
00344             str << (qint32) 0; // mimeTypeInheritanceLevel
00345             // update offerEntrySize in populateServiceTypes if you add/remove something here
00346         }
00347     }
00348 
00349     // For each entry in mimeTypeFactory
00350     KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
00351     const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
00352     for( ; itmtf != endmtf; ++itmtf ) {
00353         // export associated services
00354         const KMimeType::Ptr entry = KMimeType::Ptr::staticCast( *itmtf );
00355         Q_ASSERT( entry );
00356         QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
00357         qStableSort( offers ); // by initial preference
00358 
00359         for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
00360             it2 != offers.constEnd(); ++it2) {
00361             //kDebug(7021) << "mimetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath() << "pref" << (*it2).preference();
00362             Q_ASSERT((*it2).service()->offset() != 0);
00363             str << (qint32) entry->offset();
00364             str << (qint32) (*it2).service()->offset();
00365             str << (qint32) (*it2).preference();
00366             str << (qint32) (*it2).mimeTypeInheritanceLevel();
00367             // update offerEntrySize in populateServiceTypes if you add/remove something here
00368         }
00369     }
00370 
00371     str << (qint32) 0;               // End of list marker (0)
00372 }
00373 
00374 void KBuildServiceFactory::addEntry(const KSycocaEntry::Ptr& newEntry)
00375 {
00376     Q_ASSERT(newEntry);
00377     if (m_dupeDict.contains(newEntry))
00378         return;
00379 
00380     const KService::Ptr service = KService::Ptr::staticCast( newEntry );
00381     m_dupeDict.insert(newEntry);
00382     KSycocaFactory::addEntry(newEntry);
00383 }

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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