1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 #include "kservicegroup.h"
9 #include "kservice.h"
10 #include "kservicefactory_p.h"
11 #include "kservicegroup_p.h"
12 #include "kservicegroupfactory_p.h"
13 #include "ksycoca_p.h"
14 #include "servicesdebug.h"
15 #include <KConfigGroup>
16 #include <KDesktopFile>
17 #include <ksycoca.h>
18 
KServiceGroup(const QString & name)19 KServiceGroup::KServiceGroup(const QString &name)
20     : KSycocaEntry(*new KServiceGroupPrivate(name))
21 {
22 }
23 
KServiceGroup(const QString & configFile,const QString & _relpath)24 KServiceGroup::KServiceGroup(const QString &configFile, const QString &_relpath)
25     : KSycocaEntry(*new KServiceGroupPrivate(_relpath))
26 {
27     Q_D(KServiceGroup);
28 
29     QString cfg = configFile;
30     if (cfg.isEmpty()) {
31         cfg = _relpath + QLatin1String(".directory");
32     }
33 
34     d->load(cfg);
35 }
36 
load(const QString & cfg)37 void KServiceGroupPrivate::load(const QString &cfg)
38 {
39     directoryEntryPath = cfg;
40 
41     const KDesktopFile desktopFile(cfg);
42 
43     const KConfigGroup config = desktopFile.desktopGroup();
44 
45     m_strCaption = config.readEntry("Name");
46     m_strIcon = config.readEntry("Icon");
47     m_strComment = config.readEntry("Comment");
48     deleted = config.readEntry("Hidden", false);
49     m_bNoDisplay = desktopFile.noDisplay();
50     m_strBaseGroupName = config.readEntry("X-KDE-BaseGroup");
51     suppressGenericNames = config.readEntry("X-KDE-SuppressGenericNames", QStringList());
52 
53     // Fill in defaults.
54     if (m_strCaption.isEmpty()) {
55         m_strCaption = path;
56         if (m_strCaption.endsWith(QLatin1Char('/'))) {
57             m_strCaption.chop(1);
58         }
59         int i = m_strCaption.lastIndexOf(QLatin1Char('/'));
60         if (i > 0) {
61             m_strCaption.remove(0, i + 1);
62         }
63     }
64     if (m_strIcon.isEmpty()) {
65         m_strIcon = QStringLiteral("folder");
66     }
67 }
68 
KServiceGroup(QDataStream & _str,int offset,bool deep)69 KServiceGroup::KServiceGroup(QDataStream &_str, int offset, bool deep)
70     : KSycocaEntry(*new KServiceGroupPrivate(_str, offset))
71 {
72     Q_D(KServiceGroup);
73     d->m_bDeep = deep;
74     d->load(_str);
75 }
76 
~KServiceGroup()77 KServiceGroup::~KServiceGroup()
78 {
79 }
80 
relPath() const81 QString KServiceGroup::relPath() const
82 {
83     return entryPath();
84 }
85 
caption() const86 QString KServiceGroup::caption() const
87 {
88     Q_D(const KServiceGroup);
89     return d->m_strCaption;
90 }
91 
icon() const92 QString KServiceGroup::icon() const
93 {
94     Q_D(const KServiceGroup);
95     return d->m_strIcon;
96 }
97 
comment() const98 QString KServiceGroup::comment() const
99 {
100     Q_D(const KServiceGroup);
101     return d->m_strComment;
102 }
103 
childCount() const104 int KServiceGroup::childCount() const
105 {
106     Q_D(const KServiceGroup);
107     return d->childCount();
108 }
109 
childCount() const110 int KServiceGroupPrivate::childCount() const
111 {
112     if (m_childCount == -1) {
113         m_childCount = 0;
114 
115         for (KServiceGroup::List::ConstIterator it = m_serviceList.begin(); it != m_serviceList.end(); ++it) {
116             KSycocaEntry::Ptr p = *it;
117             if (p->isType(KST_KService)) {
118                 KService::Ptr service(static_cast<KService *>(p.data()));
119                 if (!service->noDisplay()) {
120                     m_childCount++;
121                 }
122             } else if (p->isType(KST_KServiceGroup)) {
123                 KServiceGroup::Ptr serviceGroup(static_cast<KServiceGroup *>(p.data()));
124                 m_childCount += serviceGroup->childCount();
125             }
126         }
127     }
128     return m_childCount;
129 }
130 
showInlineHeader() const131 bool KServiceGroup::showInlineHeader() const
132 {
133     Q_D(const KServiceGroup);
134     return d->m_bShowInlineHeader;
135 }
136 
showEmptyMenu() const137 bool KServiceGroup::showEmptyMenu() const
138 {
139     Q_D(const KServiceGroup);
140     return d->m_bShowEmptyMenu;
141 }
142 
inlineAlias() const143 bool KServiceGroup::inlineAlias() const
144 {
145     Q_D(const KServiceGroup);
146     return d->m_bInlineAlias;
147 }
148 
setInlineAlias(bool _b)149 void KServiceGroup::setInlineAlias(bool _b)
150 {
151     Q_D(KServiceGroup);
152     d->m_bInlineAlias = _b;
153 }
154 
setShowEmptyMenu(bool _b)155 void KServiceGroup::setShowEmptyMenu(bool _b)
156 {
157     Q_D(KServiceGroup);
158     d->m_bShowEmptyMenu = _b;
159 }
160 
setShowInlineHeader(bool _b)161 void KServiceGroup::setShowInlineHeader(bool _b)
162 {
163     Q_D(KServiceGroup);
164     d->m_bShowInlineHeader = _b;
165 }
166 
inlineValue() const167 int KServiceGroup::inlineValue() const
168 {
169     Q_D(const KServiceGroup);
170     return d->m_inlineValue;
171 }
172 
setInlineValue(int _val)173 void KServiceGroup::setInlineValue(int _val)
174 {
175     Q_D(KServiceGroup);
176     d->m_inlineValue = _val;
177 }
178 
allowInline() const179 bool KServiceGroup::allowInline() const
180 {
181     Q_D(const KServiceGroup);
182     return d->m_bAllowInline;
183 }
184 
setAllowInline(bool _b)185 void KServiceGroup::setAllowInline(bool _b)
186 {
187     Q_D(KServiceGroup);
188     d->m_bAllowInline = _b;
189 }
190 
noDisplay() const191 bool KServiceGroup::noDisplay() const
192 {
193     Q_D(const KServiceGroup);
194     return d->m_bNoDisplay || d->m_strCaption.startsWith(QLatin1Char('.'));
195 }
196 
suppressGenericNames() const197 QStringList KServiceGroup::suppressGenericNames() const
198 {
199     Q_D(const KServiceGroup);
200     return d->suppressGenericNames;
201 }
202 
load(QDataStream & s)203 void KServiceGroupPrivate::load(QDataStream &s)
204 {
205     QStringList groupList;
206     qint8 noDisplay;
207     qint8 _showEmptyMenu;
208     qint8 inlineHeader;
209     qint8 _inlineAlias;
210     qint8 _allowInline;
211     s >> m_strCaption >> m_strIcon >> m_strComment >> groupList >> m_strBaseGroupName >> m_childCount >> noDisplay >> suppressGenericNames >> directoryEntryPath
212         >> sortOrder >> _showEmptyMenu >> inlineHeader >> _inlineAlias >> _allowInline;
213 
214     m_bNoDisplay = (noDisplay != 0);
215     m_bShowEmptyMenu = (_showEmptyMenu != 0);
216     m_bShowInlineHeader = (inlineHeader != 0);
217     m_bInlineAlias = (_inlineAlias != 0);
218     m_bAllowInline = (_allowInline != 0);
219 
220     if (m_bDeep) {
221         for (const QString &path : std::as_const(groupList)) {
222             if (path.endsWith(QLatin1Char('/'))) {
223                 KServiceGroup::Ptr serviceGroup;
224                 serviceGroup = KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(path, false);
225                 if (serviceGroup) {
226                     m_serviceList.append(KServiceGroup::SPtr(serviceGroup));
227                 }
228             } else {
229                 KService::Ptr service;
230                 service = KSycocaPrivate::self()->serviceFactory()->findServiceByDesktopPath(path);
231                 if (service) {
232                     m_serviceList.append(KServiceGroup::SPtr(service));
233                 }
234             }
235         }
236     }
237 }
238 
addEntry(const KSycocaEntry::Ptr & entry)239 void KServiceGroup::addEntry(const KSycocaEntry::Ptr &entry)
240 {
241     Q_D(KServiceGroup);
242     d->m_serviceList.append(entry);
243 }
244 
save(QDataStream & s)245 void KServiceGroupPrivate::save(QDataStream &s)
246 {
247     KSycocaEntryPrivate::save(s);
248 
249     QStringList groupList;
250     for (const KSycocaEntry::Ptr &p : std::as_const(m_serviceList)) {
251         if (p->isType(KST_KService)) {
252             KService::Ptr service(static_cast<KService *>(p.data()));
253             groupList.append(service->entryPath());
254         } else if (p->isType(KST_KServiceGroup)) {
255             KServiceGroup::Ptr serviceGroup(static_cast<KServiceGroup *>(p.data()));
256             groupList.append(serviceGroup->relPath());
257         } else {
258             // fprintf(stderr, "KServiceGroup: Unexpected object in list!\n");
259         }
260     }
261 
262     (void)childCount();
263 
264     qint8 noDisplay = m_bNoDisplay ? 1 : 0;
265     qint8 _showEmptyMenu = m_bShowEmptyMenu ? 1 : 0;
266     qint8 inlineHeader = m_bShowInlineHeader ? 1 : 0;
267     qint8 _inlineAlias = m_bInlineAlias ? 1 : 0;
268     qint8 _allowInline = m_bAllowInline ? 1 : 0;
269     s << m_strCaption << m_strIcon << m_strComment << groupList << m_strBaseGroupName << m_childCount << noDisplay << suppressGenericNames << directoryEntryPath
270       << sortOrder << _showEmptyMenu << inlineHeader << _inlineAlias << _allowInline;
271 }
272 
groupEntries(EntriesOptions options)273 QList<KServiceGroup::Ptr> KServiceGroup::groupEntries(EntriesOptions options)
274 {
275     Q_D(KServiceGroup);
276     bool sort = options & SortEntries || options & AllowSeparators;
277     QList<KServiceGroup::Ptr> list;
278     const List tmp = d->entries(this, sort, options & ExcludeNoDisplay, options & AllowSeparators, options & SortByGenericName);
279     for (const SPtr &ptr : tmp) {
280         if (ptr->isType(KST_KServiceGroup)) {
281             KServiceGroup::Ptr serviceGroup(static_cast<KServiceGroup *>(ptr.data()));
282             list.append(serviceGroup);
283         } else if (ptr->isType(KST_KServiceSeparator)) {
284             list.append(KServiceGroup::Ptr(static_cast<KServiceGroup *>(new KSycocaEntry())));
285         } else if (sort && ptr->isType(KST_KService)) {
286             break;
287         }
288     }
289     return list;
290 }
291 
serviceEntries(EntriesOptions options)292 KService::List KServiceGroup::serviceEntries(EntriesOptions options)
293 {
294     Q_D(KServiceGroup);
295     bool sort = options & SortEntries || options & AllowSeparators;
296     QList<KService::Ptr> list;
297     const List tmp = d->entries(this, sort, options & ExcludeNoDisplay, options & AllowSeparators, options & SortByGenericName);
298     bool foundService = false;
299     for (const SPtr &ptr : tmp) {
300         if (ptr->isType(KST_KService)) {
301             list.append(KService::Ptr(static_cast<KService *>(ptr.data())));
302             foundService = true;
303         } else if (ptr->isType(KST_KServiceSeparator) && foundService) {
304             list.append(KService::Ptr(static_cast<KService *>(new KSycocaEntry())));
305         }
306     }
307     return list;
308 }
309 
entries(bool sort)310 KServiceGroup::List KServiceGroup::entries(bool sort)
311 {
312     Q_D(KServiceGroup);
313     return d->entries(this, sort, true, false, false);
314 }
315 
entries(bool sort,bool excludeNoDisplay)316 KServiceGroup::List KServiceGroup::entries(bool sort, bool excludeNoDisplay)
317 {
318     Q_D(KServiceGroup);
319     return d->entries(this, sort, excludeNoDisplay, false, false);
320 }
321 
entries(bool sort,bool excludeNoDisplay,bool allowSeparators,bool sortByGenericName)322 KServiceGroup::List KServiceGroup::entries(bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName)
323 {
324     Q_D(KServiceGroup);
325     return d->entries(this, sort, excludeNoDisplay, allowSeparators, sortByGenericName);
326 }
327 
addItem(KServiceGroup::List & sorted,const KSycocaEntry::Ptr & p,bool & addSeparator)328 static void addItem(KServiceGroup::List &sorted, const KSycocaEntry::Ptr &p, bool &addSeparator)
329 {
330     if (addSeparator && !sorted.isEmpty()) {
331         sorted.append(KSycocaEntry::Ptr(new KServiceSeparator()));
332     }
333     sorted.append(p);
334     addSeparator = false;
335 }
336 
entries(KServiceGroup * group,bool sort,bool excludeNoDisplay,bool allowSeparators,bool sortByGenericName)337 KServiceGroup::List KServiceGroupPrivate::entries(KServiceGroup *group, bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName)
338 {
339     KSycoca::self()->ensureCacheValid();
340 
341     // If the entries haven't been loaded yet, we have to reload ourselves
342     // together with the entries. We can't only load the entries afterwards
343     // since the offsets could have been changed if the database has changed.
344 
345     KServiceGroup::Ptr grp;
346     if (!m_bDeep) {
347         grp = KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(path, true);
348 
349         group = grp.data();
350         if (nullptr == group) { // No guarantee that we still exist!
351             return KServiceGroup::List();
352         }
353     }
354 
355     if (!sort) {
356         return group->d_func()->m_serviceList;
357     }
358 
359     // Sort the list alphabetically, according to locale.
360     // Groups come first, then services.
361 
362     // We use a QMap, for sorting using a stored temporary key.
363     typedef QMap<QByteArray, KServiceGroup::SPtr> SortedContainer;
364     SortedContainer slist;
365     SortedContainer glist;
366     const auto listService = group->d_func()->m_serviceList;
367     for (const KSycocaEntry::Ptr &p : listService) {
368         bool noDisplay = p->isType(KST_KServiceGroup) ? static_cast<KServiceGroup *>(p.data())->noDisplay() : static_cast<KService *>(p.data())->noDisplay();
369         if (excludeNoDisplay && noDisplay) {
370             continue;
371         }
372         // Choose the right list
373         SortedContainer &list = p->isType(KST_KServiceGroup) ? glist : slist;
374         QString name;
375         if (p->isType(KST_KServiceGroup)) {
376             name = static_cast<KServiceGroup *>(p.data())->caption();
377         } else if (sortByGenericName) {
378             name = static_cast<KService *>(p.data())->genericName() + QLatin1Char(' ') + p->name();
379         } else {
380             name = p->name() + QLatin1Char(' ') + static_cast<KService *>(p.data())->genericName();
381         }
382 
383         const QByteArray nameStr = name.toLocal8Bit();
384 
385         QByteArray key;
386         // strxfrm() crashes on Solaris and strxfrm is not defined under wince
387 #if !defined(USE_SOLARIS) && !defined(_WIN32_WCE)
388         // maybe it'd be better to use wcsxfrm() where available
389         key.resize(name.length() * 4 + 1);
390         size_t ln = strxfrm(key.data(), nameStr.constData(), key.size());
391         if (ln != size_t(-1)) {
392             key.resize(ln);
393             if (int(ln) >= key.size()) {
394                 // didn't fit?
395                 ln = strxfrm(key.data(), nameStr.constData(), key.size());
396                 if (ln == size_t(-1)) {
397                     key = nameStr;
398                 }
399             }
400         } else
401 #endif
402         {
403             key = nameStr;
404         }
405         list.insert(key, KServiceGroup::SPtr(p));
406     }
407 
408     if (sortOrder.isEmpty()) {
409         sortOrder << QStringLiteral(":M");
410         sortOrder << QStringLiteral(":F");
411         sortOrder << QStringLiteral(":OIH IL[4]"); // just inline header
412     }
413 
414     QString rp = path;
415     if (rp == QLatin1String("/")) {
416         rp.clear();
417     }
418 
419     // Iterate through the sort spec list.
420     // If an entry gets mentioned explicitly, we remove it from the sorted list
421     for (const QString &item : std::as_const(sortOrder)) {
422         if (item.isEmpty()) {
423             continue;
424         }
425         if (item[0] == QLatin1Char('/')) {
426             QString groupPath = rp + QStringView(item).mid(1) + QLatin1Char('/');
427             // Remove entry from sorted list of services.
428             for (SortedContainer::iterator it2 = glist.begin(); it2 != glist.end(); ++it2) {
429                 const KServiceGroup::Ptr group(static_cast<KServiceGroup *>(it2.value().data()));
430                 if (group->relPath() == groupPath) {
431                     glist.erase(it2);
432                     break;
433                 }
434             }
435         } else if (item[0] != QLatin1Char(':')) {
436             // Remove entry from sorted list of services.
437             // TODO: Remove item from sortOrder-list if not found
438             // TODO: This prevents duplicates
439             for (SortedContainer::iterator it2 = slist.begin(); it2 != slist.end(); ++it2) {
440                 const KService::Ptr service(static_cast<KService *>(it2.value().data()));
441                 if (service->menuId() == item) {
442                     slist.erase(it2);
443                     break;
444                 }
445             }
446         }
447     }
448 
449     KServiceGroup::List sorted;
450 
451     bool needSeparator = false;
452     // Iterate through the sort spec list.
453     // Add the entries to the list according to the sort spec.
454     for (QStringList::ConstIterator it(sortOrder.constBegin()); it != sortOrder.constEnd(); ++it) {
455         const QString &item = *it;
456         if (item.isEmpty()) {
457             continue;
458         }
459         if (item[0] == QLatin1Char(':')) {
460             // Special condition...
461             if (item == QLatin1String(":S")) {
462                 if (allowSeparators) {
463                     needSeparator = true;
464                 }
465             } else if (item.contains(QLatin1String(":O"))) {
466                 // todo parse attribute:
467                 QString tmp(item);
468                 tmp.remove(QStringLiteral(":O"));
469                 QStringList optionAttribute = tmp.split(QLatin1Char(' '), Qt::SkipEmptyParts);
470                 if (optionAttribute.isEmpty()) {
471                     optionAttribute.append(tmp);
472                 }
473                 bool showEmptyMenu = false;
474                 bool showInline = false;
475                 bool showInlineHeader = false;
476                 bool showInlineAlias = false;
477                 int inlineValue = -1;
478 
479                 for (QStringList::Iterator it3 = optionAttribute.begin(); it3 != optionAttribute.end(); ++it3) {
480                     parseAttribute(*it3, showEmptyMenu, showInline, showInlineHeader, showInlineAlias, inlineValue);
481                 }
482                 for (SortedContainer::Iterator it2 = glist.begin(); it2 != glist.end(); ++it2) {
483                     KServiceGroup::Ptr group(static_cast<KServiceGroup *>(it2.value().data()));
484                     group->setShowEmptyMenu(showEmptyMenu);
485                     group->setAllowInline(showInline);
486                     group->setShowInlineHeader(showInlineHeader);
487                     group->setInlineAlias(showInlineAlias);
488                     group->setInlineValue(inlineValue);
489                 }
490 
491             } else if (item == QLatin1String(":M")) {
492                 // Add sorted list of sub-menus
493                 for (SortedContainer::const_iterator it2 = glist.constBegin(); it2 != glist.constEnd(); ++it2) {
494                     addItem(sorted, it2.value(), needSeparator);
495                 }
496             } else if (item == QLatin1String(":F")) {
497                 // Add sorted list of services
498                 for (SortedContainer::const_iterator it2 = slist.constBegin(); it2 != slist.constEnd(); ++it2) {
499                     addItem(sorted, it2.value(), needSeparator);
500                 }
501             } else if (item == QLatin1String(":A")) {
502                 // Add sorted lists of services and submenus
503                 SortedContainer::Iterator it_s = slist.begin();
504                 SortedContainer::Iterator it_g = glist.begin();
505 
506                 while (true) {
507                     if (it_s == slist.end()) {
508                         if (it_g == glist.end()) {
509                             break; // Done
510                         }
511 
512                         // Insert remaining sub-menu
513                         addItem(sorted, it_g.value(), needSeparator);
514                         it_g++;
515                     } else if (it_g == glist.end()) {
516                         // Insert remaining service
517                         addItem(sorted, it_s.value(), needSeparator);
518                         it_s++;
519                     } else if (it_g.key() < it_s.key()) {
520                         // Insert sub-menu first
521                         addItem(sorted, it_g.value(), needSeparator);
522                         it_g++;
523                     } else {
524                         // Insert service first
525                         addItem(sorted, it_s.value(), needSeparator);
526                         it_s++;
527                     }
528                 }
529             }
530         } else if (item[0] == QLatin1Char('/')) {
531             QString groupPath = rp + QStringView(item).mid(1) + QLatin1Char('/');
532 
533             for (KServiceGroup::List::ConstIterator it2(group->d_func()->m_serviceList.constBegin()); it2 != group->d_func()->m_serviceList.constEnd(); ++it2) {
534                 if (!(*it2)->isType(KST_KServiceGroup)) {
535                     continue;
536                 }
537                 KServiceGroup::Ptr group(static_cast<KServiceGroup *>((*it2).data()));
538                 if (group->relPath() == groupPath) {
539                     if (!excludeNoDisplay || !group->noDisplay()) {
540                         ++it;
541                         const QString &nextItem = (it == sortOrder.constEnd()) ? QString() : *it;
542 
543                         if (nextItem.startsWith(QLatin1String(":O"))) {
544                             QString tmp(nextItem);
545                             tmp.remove(QStringLiteral(":O"));
546                             QStringList optionAttribute = tmp.split(QLatin1Char(' '), Qt::SkipEmptyParts);
547                             if (optionAttribute.isEmpty()) {
548                                 optionAttribute.append(tmp);
549                             }
550                             bool bShowEmptyMenu = false;
551                             bool bShowInline = false;
552                             bool bShowInlineHeader = false;
553                             bool bShowInlineAlias = false;
554                             int inlineValue = -1;
555                             for (const QString &opt_attr : std::as_const(optionAttribute)) {
556                                 parseAttribute(opt_attr, bShowEmptyMenu, bShowInline, bShowInlineHeader, bShowInlineAlias, inlineValue);
557                                 group->setShowEmptyMenu(bShowEmptyMenu);
558                                 group->setAllowInline(bShowInline);
559                                 group->setShowInlineHeader(bShowInlineHeader);
560                                 group->setInlineAlias(bShowInlineAlias);
561                                 group->setInlineValue(inlineValue);
562                             }
563                         } else {
564                             it--;
565                         }
566 
567                         addItem(sorted, KServiceGroup::SPtr(group), needSeparator);
568                     }
569                     break;
570                 }
571             }
572         } else {
573             for (KServiceGroup::List::ConstIterator it2(group->d_func()->m_serviceList.constBegin()); it2 != group->d_func()->m_serviceList.constEnd(); ++it2) {
574                 if (!(*it2)->isType(KST_KService)) {
575                     continue;
576                 }
577                 const KService::Ptr service(static_cast<KService *>((*it2).data()));
578                 if (service->menuId() == item) {
579                     if (!excludeNoDisplay || !service->noDisplay()) {
580                         addItem(sorted, (*it2), needSeparator);
581                     }
582                     break;
583                 }
584             }
585         }
586     }
587 
588     return sorted;
589 }
590 
parseAttribute(const QString & item,bool & showEmptyMenu,bool & showInline,bool & showInlineHeader,bool & showInlineAlias,int & inlineValue)591 void KServiceGroupPrivate::parseAttribute(const QString &item,
592                                           bool &showEmptyMenu,
593                                           bool &showInline,
594                                           bool &showInlineHeader,
595                                           bool &showInlineAlias,
596                                           int &inlineValue)
597 {
598     if (item == QLatin1String("ME")) { // menu empty
599         showEmptyMenu = true;
600     } else if (item == QLatin1String("NME")) { // not menu empty
601         showEmptyMenu = false;
602     } else if (item == QLatin1String("I")) { // inline menu !
603         showInline = true;
604     } else if (item == QLatin1String("NI")) { // not inline menu!
605         showInline = false;
606     } else if (item == QLatin1String("IH")) { // inline  header!
607         showInlineHeader = true;
608     } else if (item == QLatin1String("NIH")) { // not inline  header!
609         showInlineHeader = false;
610     } else if (item == QLatin1String("IA")) { // inline alias!
611         showInlineAlias = true;
612     } else if (item == QLatin1String("NIA")) { // not inline alias!
613         showInlineAlias = false;
614     } else if ((item).contains(QLatin1String("IL"))) { // inline limit!
615         QString tmp(item);
616         tmp.remove(QStringLiteral("IL["));
617         tmp.remove(QLatin1Char(']'));
618         bool ok;
619         int _inlineValue = tmp.toInt(&ok);
620         if (!ok) { // error
621             _inlineValue = -1;
622         }
623         inlineValue = _inlineValue;
624     } else {
625         qCDebug(SERVICES) << "This attribute is not supported:" << item;
626     }
627 }
628 
setLayoutInfo(const QStringList & layout)629 void KServiceGroup::setLayoutInfo(const QStringList &layout)
630 {
631     Q_D(KServiceGroup);
632     d->sortOrder = layout;
633 }
634 
layoutInfo() const635 QStringList KServiceGroup::layoutInfo() const
636 {
637     Q_D(const KServiceGroup);
638     return d->sortOrder;
639 }
640 
root()641 KServiceGroup::Ptr KServiceGroup::root()
642 {
643     KSycoca::self()->ensureCacheValid();
644     return KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(QStringLiteral("/"), true);
645 }
646 
group(const QString & relPath)647 KServiceGroup::Ptr KServiceGroup::group(const QString &relPath)
648 {
649     if (relPath.isEmpty()) {
650         return root();
651     }
652     KSycoca::self()->ensureCacheValid();
653     return KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(relPath, true);
654 }
655 
childGroup(const QString & parent)656 KServiceGroup::Ptr KServiceGroup::childGroup(const QString &parent)
657 {
658     KSycoca::self()->ensureCacheValid();
659     return KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(QLatin1String("#parent#") + parent, true);
660 }
661 
baseGroupName() const662 QString KServiceGroup::baseGroupName() const
663 {
664     return d_func()->m_strBaseGroupName;
665 }
666 
directoryEntryPath() const667 QString KServiceGroup::directoryEntryPath() const
668 {
669     Q_D(const KServiceGroup);
670     return d->directoryEntryPath;
671 }
672 
673 class KServiceSeparatorPrivate : public KSycocaEntryPrivate
674 {
675 public:
K_SYCOCATYPE(KST_KServiceSeparator,KSycocaEntryPrivate)676     K_SYCOCATYPE(KST_KServiceSeparator, KSycocaEntryPrivate)
677 
678     KServiceSeparatorPrivate(const QString &name)
679         : KSycocaEntryPrivate(name)
680     {
681     }
682 
683     QString name() const override;
684 };
685 
name() const686 QString KServiceSeparatorPrivate::name() const
687 {
688     return QStringLiteral("separator");
689 }
690 
KServiceSeparator()691 KServiceSeparator::KServiceSeparator()
692     : KSycocaEntry(*new KServiceSeparatorPrivate(QStringLiteral("separator")))
693 {
694 }
695 
~KServiceSeparator()696 KServiceSeparator::~KServiceSeparator()
697 {
698 }
699