• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 "kmimetyperepository_p.h"
00024 #include "ksycoca.h"
00025 #include "ksycocadict_p.h"
00026 #include "kresourcelist.h"
00027 #include "kdesktopfile.h"
00028 
00029 #include <kglobal.h>
00030 #include <kstandarddirs.h>
00031 #include <klocale.h>
00032 #include <kdebug.h>
00033 #include <assert.h>
00034 #include <kmimetypefactory.h>
00035 
00036 KBuildServiceFactory::KBuildServiceFactory( KSycocaFactory *serviceTypeFactory,
00037                                             KBuildMimeTypeFactory *mimeTypeFactory,
00038                                             KBuildServiceGroupFactory *serviceGroupFactory ) :
00039     KServiceFactory(),
00040     m_nameMemoryHash(),
00041     m_relNameMemoryHash(),
00042     m_menuIdMemoryHash(),
00043     m_dupeDict(),
00044     m_serviceTypeFactory( serviceTypeFactory ),
00045     m_mimeTypeFactory( mimeTypeFactory ),
00046     m_serviceGroupFactory( serviceGroupFactory )
00047 {
00048     m_resourceList = new KSycocaResourceList();
00049     // We directly care about services desktop files.
00050     // All the application desktop files are parsed on demand from the vfolder menu code.
00051     m_resourceList->add( "services", "*.desktop" );
00052 
00053     m_nameDict = new KSycocaDict();
00054     m_relNameDict = new KSycocaDict();
00055     m_menuIdDict = new KSycocaDict();
00056 }
00057 
00058 // return all service types for this factory
00059 // i.e. first arguments to m_resourceList->add() above
00060 QStringList KBuildServiceFactory::resourceTypes()
00061 {
00062     return QStringList() << "services";
00063 }
00064 
00065 KBuildServiceFactory::~KBuildServiceFactory()
00066 {
00067     delete m_resourceList;
00068 }
00069 
00070 KService::Ptr KBuildServiceFactory::findServiceByDesktopName(const QString &name)
00071 {
00072     return m_nameMemoryHash.value(name);
00073 }
00074 
00075 KService::Ptr KBuildServiceFactory::findServiceByDesktopPath(const QString &name)
00076 {
00077     return m_relNameMemoryHash.value(name);
00078 }
00079 
00080 KService::Ptr KBuildServiceFactory::findServiceByMenuId(const QString &menuId)
00081 {
00082     return m_menuIdMemoryHash.value(menuId);
00083 }
00084 
00085 KSycocaEntry* KBuildServiceFactory::createEntry( const QString& file, const char *resource ) const
00086 {
00087     QString name = file;
00088     int pos = name.lastIndexOf('/');
00089     if (pos != -1) {
00090         name = name.mid(pos+1);
00091     }
00092     // Is it a .desktop file?
00093     if (name.endsWith(QLatin1String(".desktop"))) {
00094         KDesktopFile desktopFile(resource, file);
00095 
00096         KService * serv = new KService(&desktopFile);
00097         //kDebug(7021) << "Creating KService from" << file << "entryPath=" << serv->entryPath();
00098         // Note that the menuId will be set by the vfolder_menu.cpp code just after
00099         // createEntry returns.
00100 
00101         if ( serv->isValid() && !serv->isDeleted() ) {
00102             return serv;
00103         } else {
00104             if (!serv->isDeleted()) {
00105                 kWarning(7012) << "Invalid Service : " << file;
00106             }
00107             delete serv;
00108             return 0;
00109         }
00110     } // TODO else if a Windows application,  new KService(name, exec, icon)
00111     return 0;
00112 }
00113 
00114 void KBuildServiceFactory::saveHeader(QDataStream &str)
00115 {
00116     KSycocaFactory::saveHeader(str);
00117 
00118     str << (qint32) m_nameDictOffset;
00119     str << (qint32) m_relNameDictOffset;
00120     str << (qint32) m_offerListOffset;
00121     str << (qint32) m_menuIdDictOffset;
00122 }
00123 
00124 void KBuildServiceFactory::save(QDataStream &str)
00125 {
00126     KSycocaFactory::save(str);
00127 
00128     m_nameDictOffset = str.device()->pos();
00129     m_nameDict->save(str);
00130 
00131     m_relNameDictOffset = str.device()->pos();
00132     m_relNameDict->save(str);
00133 
00134     saveOfferList(str);
00135 
00136     m_menuIdDictOffset = str.device()->pos();
00137     m_menuIdDict->save(str);
00138 
00139     int endOfFactoryData = str.device()->pos();
00140 
00141     // Update header (pass #3)
00142     saveHeader(str);
00143 
00144     // Seek to end.
00145     str.device()->seek(endOfFactoryData);
00146 }
00147 
00148 void KBuildServiceFactory::collectInheritedServices()
00149 {
00150     // For each mimetype, go up the parent-mimetype chains and collect offers.
00151     // For "removed associations" to work, we can't just grab everything from all parents.
00152     // We need to process parents before children, hence the recursive call in
00153     // collectInheritedServices(mime) and the QSet to process a given parent only once.
00154     QSet<QString> visitedMimes;
00155     const QStringList allMimeTypes = m_mimeTypeFactory->allMimeTypes();
00156     Q_FOREACH(const QString& mimeType, allMimeTypes) {
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(const QString& mimeTypeName, QSet<QString>& visitedMimes)
00163 {
00164     if (visitedMimes.contains(mimeTypeName))
00165         return;
00166     visitedMimes.insert(mimeTypeName);
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     Q_FOREACH(const QString& parentMimeType, KMimeTypeRepository::self()->parents(mimeTypeName)) {
00174 
00175         collectInheritedServices(parentMimeType, visitedMimes);
00176 
00177         ++mimeTypeInheritanceLevel;
00178         const QList<KServiceOffer>& offers = m_offerHash.offersFor(parentMimeType);
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     }
00190 }
00191 
00192 void KBuildServiceFactory::postProcessServices()
00193 {
00194     // By doing all this here rather than in addEntry (and removing when replacing
00195     // with local override), we only do it for the final applications.
00196 
00197     // For every service...
00198     KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
00199     const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
00200     for( ; itserv != endserv ; ++itserv ) {
00201 
00202         KSycocaEntry::Ptr entry = *itserv;
00203         KService::Ptr service = KService::Ptr::staticCast(entry);
00204 
00205         if (!service->isDeleted()) {
00206             const QString parent = service->parentApp();
00207             if (!parent.isEmpty())
00208                 m_serviceGroupFactory->addNewChild(parent, KSycocaEntry::Ptr::staticCast(service));
00209         }
00210 
00211         const QString name = service->desktopEntryName();
00212         m_nameDict->add(name, entry);
00213         m_nameMemoryHash.insert(name, service);
00214 
00215         const QString relName = service->entryPath();
00216         //kDebug(7021) << "adding service" << service.data() << service->type() << "menuId=" << service->menuId() << "name=" << name << "relName=" << relName;
00217         m_relNameDict->add(relName, entry);
00218         m_relNameMemoryHash.insert(relName, service); // for KMimeAssociations
00219 
00220         const QString menuId = service->menuId();
00221         if (!menuId.isEmpty()) { // empty for services, non-empty for applications
00222             m_menuIdDict->add(menuId, entry);
00223             m_menuIdMemoryHash.insert(menuId, service); // for KMimeAssociations
00224         }
00225     }
00226     populateServiceTypes();
00227 }
00228 
00229 void KBuildServiceFactory::populateServiceTypes()
00230 {
00231     // For every service...
00232     KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
00233     const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
00234     for( ; itserv != endserv ; ++itserv ) {
00235 
00236         KService::Ptr service = KService::Ptr::staticCast(*itserv);
00237         QVector<KService::ServiceTypeAndPreference> serviceTypeList = service->_k_accessServiceTypes();
00238         //bool hasAllAll = false;
00239         //bool hasAllFiles = false;
00240 
00241         // Add this service to all its servicetypes (and their parents) and to all its mimetypes
00242         for (int i = 0; i < serviceTypeList.count() /*don't cache it, it can change during iteration!*/; ++i) {
00243             const QString stName = serviceTypeList[i].serviceType;
00244             // It could be a servicetype or a mimetype.
00245             KServiceType::Ptr serviceType = KServiceType::serviceType(stName);
00246             if (serviceType) {
00247                 const int preference = serviceTypeList[i].preference;
00248                 const QString parent = serviceType->parentServiceType();
00249                 if (!parent.isEmpty())
00250                     serviceTypeList.append(KService::ServiceTypeAndPreference(preference, parent));
00251 
00252                 //kDebug(7021) << "Adding service" << service->entryPath() << "to" << serviceType->name() << "pref=" << preference;
00253                 m_offerHash.addServiceOffer(stName, KServiceOffer(service, preference, 0, service->allowAsDefault()) );
00254             } else {
00255                 KMimeType::Ptr mime = KMimeType::mimeType(stName, KMimeType::ResolveAliases);
00256                 if (!mime) {
00257                     if (stName.startsWith("x-scheme-handler/")) {
00258                         // Create those on demand
00259                         m_mimeTypeFactory->createFakeMimeType(stName);
00260                     } else {
00261                         kDebug(7021) << service->entryPath() << "specifies undefined mimetype/servicetype" << stName;
00262                         // technically we could call addServiceOffer here, 'mime' isn't used. But it
00263                         // would be useless, since the loops for writing out the offers iterate
00264                         // over all known servicetypes and mimetypes. Unknown -> never written out.
00265                         continue;
00266                     }
00267                 }
00268                 m_offerHash.addServiceOffer(stName, KServiceOffer(service, serviceTypeList[i].preference, 0, service->allowAsDefault()) );
00269             }
00270         }
00271     }
00272 
00273     // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash
00274     KMimeAssociations mimeAssociations(m_offerHash);
00275     mimeAssociations.parseAllMimeAppsList();
00276 
00277     // Now for each mimetype, collect services from parent mimetypes
00278     collectInheritedServices();
00279 
00280     // Now collect the offsets into the (future) offer list
00281     // The loops look very much like the ones in saveOfferList obviously.
00282     int offersOffset = 0;
00283     const int offerEntrySize = sizeof( qint32 ) * 4; // four qint32s, see saveOfferList.
00284 
00285     // TODO: idea: we could iterate over m_offerHash, and look up the servicetype or mimetype.
00286     // Would that be faster than iterating over all servicetypes and mimetypes?
00287 
00288     KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
00289     const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
00290     for( ; itstf != endstf; ++itstf ) {
00291         KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
00292         const int numOffers = m_offerHash.offersFor(entry->name()).count();
00293         if ( numOffers ) {
00294             entry->setServiceOffersOffset( offersOffset );
00295             offersOffset += offerEntrySize * numOffers;
00296         }
00297     }
00298     KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
00299     const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
00300     for( ; itmtf != endmtf; ++itmtf )
00301     {
00302         KMimeTypeFactory::MimeTypeEntry::Ptr entry = KMimeTypeFactory::MimeTypeEntry::Ptr::staticCast( *itmtf );
00303         const int numOffers = m_offerHash.offersFor(entry->name()).count();
00304         if ( numOffers ) {
00305             //kDebug() << entry->name() << "offset=" << offersOffset;
00306             entry->setServiceOffersOffset( offersOffset );
00307             offersOffset += offerEntrySize * numOffers;
00308         }
00309     }
00310 }
00311 
00312 void KBuildServiceFactory::saveOfferList(QDataStream &str)
00313 {
00314     m_offerListOffset = str.device()->pos();
00315 
00316     // For each entry in servicetypeFactory
00317     KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
00318     const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
00319     for( ; itstf != endstf; ++itstf ) {
00320         // export associated services
00321         const KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
00322         Q_ASSERT( entry );
00323 
00324         QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
00325         qStableSort( offers ); // by initial preference
00326 
00327         for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
00328             it2 != offers.constEnd(); ++it2) {
00329             //kDebug(7021) << "servicetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath();
00330 
00331             str << (qint32) entry->offset();
00332             str << (qint32) (*it2).service()->offset();
00333             str << (qint32) (*it2).preference();
00334             str << (qint32) 0; // mimeTypeInheritanceLevel
00335             // update offerEntrySize in populateServiceTypes if you add/remove something here
00336         }
00337     }
00338 
00339     // For each entry in mimeTypeFactory
00340     KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
00341     const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
00342     for( ; itmtf != endmtf; ++itmtf ) {
00343         // export associated services
00344         const KMimeTypeFactory::MimeTypeEntry::Ptr entry = KMimeTypeFactory::MimeTypeEntry::Ptr::staticCast( *itmtf );
00345         Q_ASSERT( entry );
00346         QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
00347         qStableSort( offers ); // by initial preference
00348 
00349         for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
00350             it2 != offers.constEnd(); ++it2) {
00351             //kDebug(7021) << "mimetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath() << "pref" << (*it2).preference();
00352             Q_ASSERT((*it2).service()->offset() != 0);
00353             str << (qint32) entry->offset();
00354             str << (qint32) (*it2).service()->offset();
00355             str << (qint32) (*it2).preference();
00356             str << (qint32) (*it2).mimeTypeInheritanceLevel();
00357             // update offerEntrySize in populateServiceTypes if you add/remove something here
00358         }
00359     }
00360 
00361     str << (qint32) 0;               // End of list marker (0)
00362 }
00363 
00364 void KBuildServiceFactory::addEntry(const KSycocaEntry::Ptr& newEntry)
00365 {
00366     Q_ASSERT(newEntry);
00367     if (m_dupeDict.contains(newEntry))
00368         return;
00369 
00370     const KService::Ptr service = KService::Ptr::staticCast( newEntry );
00371     m_dupeDict.insert(newEntry);
00372     KSycocaFactory::addEntry(newEntry);
00373 }

KDED

Skip menu "KDED"
  • Main Page
  • Namespace List
  • 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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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