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

source code of kservice/src/services/kservicegroup.cpp