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

KDED

  • sources
  • kde-4.12
  • kdelibs
  • 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  bool shouldAdd = true;
271  foreach (const QString &otherType, service->serviceTypes()) {
272  // Skip derived types if the base class is listed (#321706)
273  if (stName != otherType && mime->is(otherType)) {
274  // But don't skip aliases (they got resolved into mime->name() already, but don't let two aliases cancel out)
275  if (KMimeTypeRepository::self()->canonicalName(otherType) != mime->name()) {
276  //kDebug() << "Skipping" << mime->name() << "because of" << otherType << "(canonical" << KMimeTypeRepository::self()->canonicalName(otherType) << ") while parsing" << service->entryPath();
277  shouldAdd = false;
278  }
279  }
280  }
281  if (shouldAdd) {
282  //kDebug(7021) << "Adding service" << service->entryPath() << "to" << mime->name();
283  m_offerHash.addServiceOffer(mime->name(), offer); // mime->name so that we resolve aliases
284  }
285  }
286  }
287  }
288  }
289 
290  // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash
291  KMimeAssociations mimeAssociations(m_offerHash);
292  mimeAssociations.parseAllMimeAppsList();
293 
294  // Now for each mimetype, collect services from parent mimetypes
295  collectInheritedServices();
296 
297  // Now collect the offsets into the (future) offer list
298  // The loops look very much like the ones in saveOfferList obviously.
299  int offersOffset = 0;
300  const int offerEntrySize = sizeof( qint32 ) * 4; // four qint32s, see saveOfferList.
301 
302  // TODO: idea: we could iterate over m_offerHash, and look up the servicetype or mimetype.
303  // Would that be faster than iterating over all servicetypes and mimetypes?
304 
305  KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
306  const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
307  for( ; itstf != endstf; ++itstf ) {
308  KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
309  const int numOffers = m_offerHash.offersFor(entry->name()).count();
310  if ( numOffers ) {
311  entry->setServiceOffersOffset( offersOffset );
312  offersOffset += offerEntrySize * numOffers;
313  }
314  }
315  KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
316  const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
317  for( ; itmtf != endmtf; ++itmtf )
318  {
319  KMimeTypeFactory::MimeTypeEntry::Ptr entry = KMimeTypeFactory::MimeTypeEntry::Ptr::staticCast( *itmtf );
320  const int numOffers = m_offerHash.offersFor(entry->name()).count();
321  if ( numOffers ) {
322  //kDebug() << entry->name() << "offset=" << offersOffset;
323  entry->setServiceOffersOffset( offersOffset );
324  offersOffset += offerEntrySize * numOffers;
325  }
326  }
327 }
328 
329 void KBuildServiceFactory::saveOfferList(QDataStream &str)
330 {
331  m_offerListOffset = str.device()->pos();
332 
333  // For each entry in servicetypeFactory
334  KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin();
335  const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd();
336  for( ; itstf != endstf; ++itstf ) {
337  // export associated services
338  const KServiceType::Ptr entry = KServiceType::Ptr::staticCast( *itstf );
339  Q_ASSERT( entry );
340 
341  QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
342  qStableSort( offers ); // by initial preference
343 
344  for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
345  it2 != offers.constEnd(); ++it2) {
346  //kDebug(7021) << "servicetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath();
347 
348  str << (qint32) entry->offset();
349  str << (qint32) (*it2).service()->offset();
350  str << (qint32) (*it2).preference();
351  str << (qint32) 0; // mimeTypeInheritanceLevel
352  // update offerEntrySize in populateServiceTypes if you add/remove something here
353  }
354  }
355 
356  // For each entry in mimeTypeFactory
357  KSycocaEntryDict::const_iterator itmtf = m_mimeTypeFactory->entryDict()->constBegin();
358  const KSycocaEntryDict::const_iterator endmtf = m_mimeTypeFactory->entryDict()->constEnd();
359  for( ; itmtf != endmtf; ++itmtf ) {
360  // export associated services
361  const KMimeTypeFactory::MimeTypeEntry::Ptr entry = KMimeTypeFactory::MimeTypeEntry::Ptr::staticCast( *itmtf );
362  Q_ASSERT( entry );
363  QList<KServiceOffer> offers = m_offerHash.offersFor(entry->name());
364  qStableSort( offers ); // by initial preference
365 
366  for(QList<KServiceOffer>::const_iterator it2 = offers.constBegin();
367  it2 != offers.constEnd(); ++it2) {
368  //kDebug(7021) << "mimetype offers list:" << entry->name() << "->" << (*it2).service()->entryPath() << "pref" << (*it2).preference();
369  Q_ASSERT((*it2).service()->offset() != 0);
370  str << (qint32) entry->offset();
371  str << (qint32) (*it2).service()->offset();
372  str << (qint32) (*it2).preference();
373  str << (qint32) (*it2).mimeTypeInheritanceLevel();
374  // update offerEntrySize in populateServiceTypes if you add/remove something here
375  }
376  }
377 
378  str << (qint32) 0; // End of list marker (0)
379 }
380 
381 void KBuildServiceFactory::addEntry(const KSycocaEntry::Ptr& newEntry)
382 {
383  Q_ASSERT(newEntry);
384  if (m_dupeDict.contains(newEntry))
385  return;
386 
387  const KService::Ptr service = KService::Ptr::staticCast( newEntry );
388  m_dupeDict.insert(newEntry);
389  KSycocaFactory::addEntry(newEntry);
390 }
KSycocaDict
KOfferHash::offersFor
QList< KServiceOffer > offersFor(const QString &serviceType) const
Definition: kmimeassociations.h:41
KMimeTypeFactory::allMimeTypes
QStringList allMimeTypes()
KSharedPtr< KService >
KServiceFactory::m_nameDictOffset
int m_nameDictOffset
KBuildServiceFactory::findServiceByMenuId
virtual KService::Ptr findServiceByMenuId(const QString &menuId)
Reimplemented from KServiceFactory.
Definition: kbuildservicefactory.cpp:80
KSycocaDict::add
void add(const QString &key, const KSycocaEntry::Ptr &payload)
KServiceFactory::m_nameDict
KSycocaDict * m_nameDict
kbuildservicegroupfactory.h
KMimeAssociations
Parse mimeapps.list files and:
Definition: kmimeassociations.h:62
kdebug.h
KService::ServiceTypeAndPreference
KBuildServiceFactory::~KBuildServiceFactory
virtual ~KBuildServiceFactory()
Definition: kbuildservicefactory.cpp:65
KOfferHash::addServiceOffer
void addServiceOffer(const QString &serviceType, const KServiceOffer &offer)
Definition: kmimeassociations.cpp:118
KService
name
const char * name(StandardAction id)
KBuildServiceGroupFactory::addNewChild
void addNewChild(const QString &parent, const KSycocaEntry::Ptr &newEntry)
Adds the entry newEntry to the "parent group" parent, creating the group if necassery.
Definition: kbuildservicegroupfactory.cpp:125
KServiceFactory::m_menuIdDictOffset
int m_menuIdDictOffset
KSycocaResourceList
Definition: ksycocaresourcelist.h:32
kmimetyperepository_p.h
KBuildServiceFactory::createEntry
virtual KSycocaEntry * createEntry(const QString &file, const char *resource) const
Construct a KService from a config file.
Definition: kbuildservicefactory.cpp:85
KMimeTypeRepository::self
static KMimeTypeRepository * self()
QString
KSycocaFactory
kdesktopfile.h
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
klocale.h
KBuildServiceFactory::resourceTypes
static QStringList resourceTypes()
Returns all resource types for this service factory.
Definition: kbuildservicefactory.cpp:60
KSycocaDict::save
void save(QDataStream &str)
kglobal.h
ksycocadict_p.h
KServiceFactory::m_relNameDict
KSycocaDict * m_relNameDict
KSycocaEntry
KBuildMimeTypeFactory::createFakeMimeType
void createFakeMimeType(const QString &name)
Definition: kbuildmimetypefactory.cpp:121
QStringList
KService::menuId
QString menuId() const
KServiceFactory::m_menuIdDict
KSycocaDict * m_menuIdDict
KBuildServiceFactory::addEntry
virtual void addEntry(const KSycocaEntry::Ptr &newEntry)
Add a new entry.
Definition: kbuildservicefactory.cpp:381
kbuildservicefactory.h
KServiceFactory::m_offerListOffset
int m_offerListOffset
KBuildServiceGroupFactory
Service group factory for building ksycoca.
Definition: kbuildservicegroupfactory.h:30
QSet< QString >
KServiceOffer
KDesktopFile
KBuildServiceFactory::findServiceByDesktopPath
virtual KService::Ptr findServiceByDesktopPath(const QString &name)
Reimplemented from KServiceFactory.
Definition: kbuildservicefactory.cpp:75
KMimeTypeFactory::entryDict
KSycocaEntryDict * entryDict()
ksycoca.h
KBuildMimeTypeFactory
Mime-type factory for building ksycoca.
Definition: kbuildmimetypefactory.h:32
KServiceFactory::offers
KServiceOfferList offers(int serviceTypeOffset, int serviceOffersOffset)
KBuildServiceFactory::findServiceByDesktopName
virtual KService::Ptr findServiceByDesktopName(const QString &name)
Reimplemented from KServiceFactory.
Definition: kbuildservicefactory.cpp:70
KService::desktopEntryName
QString desktopEntryName() const
kstandarddirs.h
KSharedPtr::staticCast
static KSharedPtr< T > staticCast(const KSharedPtr< U > &o)
KBuildServiceFactory::postProcessServices
void postProcessServices()
Definition: kbuildservicefactory.cpp:192
KServiceFactory::m_relNameDictOffset
int m_relNameDictOffset
KBuildServiceFactory::save
virtual void save(QDataStream &str)
Write out service specific index files.
Definition: kbuildservicefactory.cpp:124
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
kbuildmimetypefactory.h
qint32
ksycocaresourcelist.h
KOfferHash::hasRemovedOffer
bool hasRemovedOffer(const QString &serviceType, KService::Ptr service) const
Definition: kmimeassociations.cpp:152
KBuildServiceFactory::saveHeader
virtual void saveHeader(QDataStream &str)
Write out header information.
Definition: kbuildservicefactory.cpp:114
KServiceFactory
KBuildServiceFactory::KBuildServiceFactory
KBuildServiceFactory(KSycocaFactory *serviceTypeFactory, KBuildMimeTypeFactory *mimeTypeFactory, KBuildServiceGroupFactory *serviceGroupFactory)
Create factory.
Definition: kbuildservicefactory.cpp:36
KMimeTypeRepository::canonicalName
QString canonicalName(const QString &mime)
kmimetypefactory.h
QList< KServiceOffer >
KService::parentApp
QString parentApp() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:51:13 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDED

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

kdelibs API Reference

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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