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 }
KDE 4.6 API Reference