• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.1 API Reference
  • KDE Home
  • Contact Us
 

KDED

  • kded
kbuildservicefactory.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  * Copyright (C) 1999, 2007 David Faure <faure@kde.org>
3  * 1999 Waldo Bastian <bastian@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License version 2 as published by the Free Software Foundation;
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB. If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  **/
19 
20 #include "kbuildservicefactory.h"
21 #include "kbuildservicegroupfactory.h"
22 #include "kbuildmimetypefactory.h"
23 #include "kmimetyperepository_p.h"
24 #include "ksycoca.h"
25 #include "ksycocadict_p.h"
26 #include "ksycocaresourcelist.h"
27 #include "kdesktopfile.h"
28 
29 #include <kglobal.h>
30 #include <kstandarddirs.h>
31 #include <klocale.h>
32 #include <kdebug.h>
33 #include <assert.h>
34 #include <kmimetypefactory.h>
35 
36 KBuildServiceFactory::KBuildServiceFactory( KSycocaFactory *serviceTypeFactory,
37  KBuildMimeTypeFactory *mimeTypeFactory,
38  KBuildServiceGroupFactory *serviceGroupFactory ) :
39  KServiceFactory(),
40  m_nameMemoryHash(),
41  m_relNameMemoryHash(),
42  m_menuIdMemoryHash(),
43  m_dupeDict(),
44  m_serviceTypeFactory( serviceTypeFactory ),
45  m_mimeTypeFactory( mimeTypeFactory ),
46  m_serviceGroupFactory( serviceGroupFactory )
47 {
48  m_resourceList = new KSycocaResourceList();
49  // We directly care about services desktop files.
50  // All the application desktop files are parsed on demand from the vfolder menu code.
51  m_resourceList->add( "services", "*.desktop" );
52 
53  m_nameDict = new KSycocaDict();
54  m_relNameDict = new KSycocaDict();
55  m_menuIdDict = new KSycocaDict();
56 }
57 
58 // return all service types for this factory
59 // i.e. first arguments to m_resourceList->add() above
60 QStringList KBuildServiceFactory::resourceTypes()
61 {
62  return QStringList() << "services";
63 }
64 
65 KBuildServiceFactory::~KBuildServiceFactory()
66 {
67  delete m_resourceList;
68 }
69 
70 KService::Ptr KBuildServiceFactory::findServiceByDesktopName(const QString &name)
71 {
72  return m_nameMemoryHash.value(name);
73 }
74 
75 KService::Ptr KBuildServiceFactory::findServiceByDesktopPath(const QString &name)
76 {
77  return m_relNameMemoryHash.value(name);
78 }
79 
80 KService::Ptr KBuildServiceFactory::findServiceByMenuId(const QString &menuId)
81 {
82  return m_menuIdMemoryHash.value(menuId);
83 }
84 
85 KSycocaEntry* KBuildServiceFactory::createEntry( const QString& file, const char *resource ) const
86 {
87  QString name = file;
88  int pos = name.lastIndexOf('/');
89  if (pos != -1) {
90  name = name.mid(pos+1);
91  }
92  // Is it a .desktop file?
93  if (name.endsWith(QLatin1String(".desktop"))) {
94  KDesktopFile desktopFile(resource, file);
95 
96  KService * serv = new KService(&desktopFile);
97  //kDebug(7021) << "Creating KService from" << file << "entryPath=" << serv->entryPath();
98  // Note that the menuId will be set by the vfolder_menu.cpp code just after
99  // createEntry returns.
100 
101  if ( serv->isValid() && !serv->isDeleted() ) {
102  return serv;
103  } else {
104  if (!serv->isDeleted()) {
105  kWarning(7012) << "Invalid Service : " << file;
106  }
107  delete serv;
108  return 0;
109  }
110  } // TODO else if a Windows application, new KService(name, exec, icon)
111  return 0;
112 }
113 
114 void KBuildServiceFactory::saveHeader(QDataStream &str)
115 {
116  KSycocaFactory::saveHeader(str);
117 
118  str << (qint32) m_nameDictOffset;
119  str << (qint32) m_relNameDictOffset;
120  str << (qint32) m_offerListOffset;
121  str << (qint32) m_menuIdDictOffset;
122 }
123 
124 void KBuildServiceFactory::save(QDataStream &str)
125 {
126  KSycocaFactory::save(str);
127 
128  m_nameDictOffset = str.device()->pos();
129  m_nameDict->save(str);
130 
131  m_relNameDictOffset = str.device()->pos();
132  m_relNameDict->save(str);
133 
134  saveOfferList(str);
135 
136  m_menuIdDictOffset = str.device()->pos();
137  m_menuIdDict->save(str);
138 
139  int endOfFactoryData = str.device()->pos();
140 
141  // Update header (pass #3)
142  saveHeader(str);
143 
144  // Seek to end.
145  str.device()->seek(endOfFactoryData);
146 }
147 
148 void KBuildServiceFactory::collectInheritedServices()
149 {
150  // For each mimetype, go up the parent-mimetype chains and collect offers.
151  // For "removed associations" to work, we can't just grab everything from all parents.
152  // We need to process parents before children, hence the recursive call in
153  // collectInheritedServices(mime) and the QSet to process a given parent only once.
154  QSet<QString> visitedMimes;
155  const QStringList allMimeTypes = m_mimeTypeFactory->allMimeTypes();
156  Q_FOREACH(const QString& mimeType, allMimeTypes) {
157  collectInheritedServices(mimeType, visitedMimes);
158  }
159  // TODO do the same for all/all and all/allfiles, if (!KServiceTypeProfile::configurationMode())
160 }
161 
162 void KBuildServiceFactory::collectInheritedServices(const QString& mimeTypeName, QSet<QString>& visitedMimes)
163 {
164  if (visitedMimes.contains(mimeTypeName))
165  return;
166  visitedMimes.insert(mimeTypeName);
167 
168  // With multiple inheritance, the "mimeTypeInheritanceLevel" isn't exactly
169  // correct (it should only be increased when going up a level, not when iterating
170  // through the multiple parents at a given level). I don't think we care, though.
171  int mimeTypeInheritanceLevel = 0;
172 
173  Q_FOREACH(const QString& parentMimeType, KMimeTypeRepository::self()->parents(mimeTypeName)) {
174 
175  collectInheritedServices(parentMimeType, visitedMimes);
176 
177  ++mimeTypeInheritanceLevel;
178  const QList<KServiceOffer>& offers = m_offerHash.offersFor(parentMimeType);
179  QList<KServiceOffer>::const_iterator itserv = offers.begin();
180  const QList<KServiceOffer>::const_iterator endserv = offers.end();
181  for ( ; itserv != endserv; ++itserv ) {
182  if (!m_offerHash.hasRemovedOffer(mimeTypeName, (*itserv).service())) {
183  KServiceOffer offer(*itserv);
184  offer.setMimeTypeInheritanceLevel(mimeTypeInheritanceLevel);
185  //kDebug(7021) << "INHERITANCE: Adding service" << (*itserv).service()->entryPath() << "to" << mimeTypeName << "mimeTypeInheritanceLevel=" << mimeTypeInheritanceLevel;
186  m_offerHash.addServiceOffer( mimeTypeName, offer );
187  }
188  }
189  }
190 }
191 
192 void KBuildServiceFactory::postProcessServices()
193 {
194  // By doing all this here rather than in addEntry (and removing when replacing
195  // with local override), we only do it for the final applications.
196 
197  // For every service...
198  KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
199  const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
200  for( ; itserv != endserv ; ++itserv ) {
201 
202  KSycocaEntry::Ptr entry = *itserv;
203  KService::Ptr service = KService::Ptr::staticCast(entry);
204 
205  if (!service->isDeleted()) {
206  const QString parent = service->parentApp();
207  if (!parent.isEmpty())
208  m_serviceGroupFactory->addNewChild(parent, KSycocaEntry::Ptr::staticCast(service));
209  }
210 
211  const QString name = service->desktopEntryName();
212  m_nameDict->add(name, entry);
213  m_nameMemoryHash.insert(name, service);
214 
215  const QString relName = service->entryPath();
216  //kDebug(7021) << "adding service" << service.data() << service->type() << "menuId=" << service->menuId() << "name=" << name << "relName=" << relName;
217  m_relNameDict->add(relName, entry);
218  m_relNameMemoryHash.insert(relName, service); // for KMimeAssociations
219 
220  const QString menuId = service->menuId();
221  if (!menuId.isEmpty()) { // empty for services, non-empty for applications
222  m_menuIdDict->add(menuId, entry);
223  m_menuIdMemoryHash.insert(menuId, service); // for KMimeAssociations
224  }
225  }
226  populateServiceTypes();
227 }
228 
229 void KBuildServiceFactory::populateServiceTypes()
230 {
231  // For every service...
232  KSycocaEntryDict::Iterator itserv = m_entryDict->begin();
233  const KSycocaEntryDict::Iterator endserv = m_entryDict->end();
234  for( ; itserv != endserv ; ++itserv ) {
235 
236  KService::Ptr service = KService::Ptr::staticCast(*itserv);
237  QVector<KService::ServiceTypeAndPreference> serviceTypeList = service->_k_accessServiceTypes();
238  //bool hasAllAll = false;
239  //bool hasAllFiles = false;
240 
241  // Add this service to all its servicetypes (and their parents) and to all its mimetypes
242  for (int i = 0; i < serviceTypeList.count() /*don't cache it, it can change during iteration!*/; ++i) {
243  const QString stName = serviceTypeList[i].serviceType;
244  // It could be a servicetype or a mimetype.
245  KServiceType::Ptr serviceType = KServiceType::serviceType(stName);
246  if (serviceType) {
247  const int preference = serviceTypeList[i].preference;
248  const QString parent = serviceType->parentServiceType();
249  if (!parent.isEmpty())
250  serviceTypeList.append(KService::ServiceTypeAndPreference(preference, parent));
251 
252  //kDebug(7021) << "Adding service" << service->entryPath() << "to" << serviceType->name() << "pref=" << preference;
253  m_offerHash.addServiceOffer(stName, KServiceOffer(service, preference, 0, service->allowAsDefault()) );
254  } else {
255  KServiceOffer offer(service, serviceTypeList[i].preference, 0, service->allowAsDefault());
256  KMimeType::Ptr mime = KMimeType::mimeType(stName, KMimeType::ResolveAliases);
257  if (!mime) {
258  if (stName.startsWith(QLatin1String("x-scheme-handler/"))) {
259  // Create those on demand
260  m_mimeTypeFactory->createFakeMimeType(stName);
261  m_offerHash.addServiceOffer(stName, offer );
262  } else {
263  kDebug(7021) << service->entryPath() << "specifies undefined mimetype/servicetype" << stName;
264  // technically we could call addServiceOffer here, 'mime' isn't used. But it
265  // would be useless, since the loops for writing out the offers iterate
266  // over all known servicetypes and mimetypes. Unknown -> never written out.
267  continue;
268  }
269  } else {
270  m_offerHash.addServiceOffer(mime->name(), offer); // mime->name so that we resolve aliases
271  }
272  }
273  }
274  }
275 
276  // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash
277  KMimeAssociations mimeAssociations(m_offerHash);
278  mimeAssociations.parseAllMimeAppsList();
279 
280  // Now for each mimetype, collect services from parent mimetypes
281  collectInheritedServices();
282 
283  // Now collect the offsets into the (future) offer list
284  // The loops look very much like the ones in saveOfferList obviously.
285  int offersOffset = 0;
286  const int offerEntrySize = sizeof( qint32 ) * 4; // four qint32s, see saveOfferList.
287 
288  // TODO: idea: we could iterate over m_offerHash, and look up the servicetype or mimetype.
289  // Would that be faster than iterating over all servicetypes and mimetypes?
290 
291  KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
292  const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
293  for( ; itstf != endstf; ++itstf ) {
294  KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
295  const int numOffers = m_offerHash.offersFor(entry->name()).count();
296  if ( numOffers ) {
297  entry->setServiceOffersOffset( offersOffset );
298  offersOffset += offerEntrySize * numOffers;
299  }
300  }
301  KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
302  const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
303  for( ; itmtf != endmtf; ++itmtf )
304  {
305  KMimeTypeFactory::MimeTypeEntry::Ptr entry = KMimeTypeFactory::MimeTypeEntry::Ptr::staticCast( *itmtf );
306  const int numOffers = m_offerHash.offersFor(entry->name()).count();
307  if ( numOffers ) {
308  //kDebug() << entry->name() << "offset=" << offersOffset;
309  entry->setServiceOffersOffset( offersOffset );
310  offersOffset += offerEntrySize * numOffers;
311  }
312  }
313 }
314 
315 void KBuildServiceFactory::saveOfferList(QDataStream &str)
316 {
317  m_offerListOffset = str.device()->pos();
318 
319  // For each entry in servicetypeFactory
320  KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
321  const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
322  for( ; itstf != endstf; ++itstf ) {
323  // export associated services
324  const KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
325  Q_ASSERT( entry );
326 
327  QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
328  qStableSort( offers ); // by initial preference
329 
330  for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
331  it2 != offers.constEnd(); ++it2) {
332  //kDebug(7021) << "servicetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath();
333 
334  str << (qint32) entry->offset();
335  str << (qint32) (*it2).service()->offset();
336  str << (qint32) (*it2).preference();
337  str << (qint32) 0; // mimeTypeInheritanceLevel
338  // update offerEntrySize in populateServiceTypes if you add/remove something here
339  }
340  }
341 
342  // For each entry in mimeTypeFactory
343  KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
344  const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
345  for( ; itmtf != endmtf; ++itmtf ) {
346  // export associated services
347  const KMimeTypeFactory::MimeTypeEntry::Ptr entry = KMimeTypeFactory::MimeTypeEntry::Ptr::staticCast( *itmtf );
348  Q_ASSERT( entry );
349  QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
350  qStableSort( offers ); // by initial preference
351 
352  for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
353  it2 != offers.constEnd(); ++it2) {
354  //kDebug(7021) << "mimetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath() << "pref" << (*it2).preference();
355  Q_ASSERT((*it2).service()->offset() != 0);
356  str << (qint32) entry->offset();
357  str << (qint32) (*it2).service()->offset();
358  str << (qint32) (*it2).preference();
359  str << (qint32) (*it2).mimeTypeInheritanceLevel();
360  // update offerEntrySize in populateServiceTypes if you add/remove something here
361  }
362  }
363 
364  str << (qint32) 0; // End of list marker (0)
365 }
366 
367 void KBuildServiceFactory::addEntry(const KSycocaEntry::Ptr& newEntry)
368 {
369  Q_ASSERT(newEntry);
370  if (m_dupeDict.contains(newEntry))
371  return;
372 
373  const KService::Ptr service = KService::Ptr::staticCast( newEntry );
374  m_dupeDict.insert(newEntry);
375  KSycocaFactory::addEntry(newEntry);
376 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Wed Mar 20 2013 07:22:22 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDED

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

kdelibs-4.10.1 API Reference

Skip menu "kdelibs-4.10.1 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal