File indexing completed on 2024-05-19 09:32:17

0001 /*
0002    This file is part of the KDE project
0003    SPDX-FileCopyrightText: 2007 Will Stephenson <wstephenson@kde.org>
0004    SPDX-FileCopyrightText: 2009 Ben Cooksley <bcooksley@kde.org>
0005    SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
0006    SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0007 
0008    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0009 */
0010 
0011 #include "MenuItem.h"
0012 
0013 #include "kcmmetadatahelpers.h"
0014 
0015 #include <QList>
0016 #include <QString>
0017 
0018 #include <KCModuleLoader>
0019 #include <KConfigGroup>
0020 #include <KDesktopFile>
0021 #include <KJsonUtils>
0022 #include <QFileInfo>
0023 
0024 static bool childIsLessThan(MenuItem *left, MenuItem *right)
0025 {
0026     return left->weight() < right->weight();
0027 }
0028 
0029 class MenuItem::Private
0030 {
0031 public:
0032     Private()
0033     {
0034     }
0035 
0036     MenuItem *parent;
0037     QList<MenuItem *> children;
0038     bool menu;
0039     QString name;
0040     QString category;
0041     int weight;
0042     KService::Ptr service;
0043     bool showDefaultIndicator = false;
0044     bool isCategoryOwner = false;
0045     QString comment;
0046     QString iconName;
0047     QString systemsettingsCategoryModule;
0048     bool isSystemsettingsCategory = false;
0049     bool isSystemsettingsRootCategory = false;
0050     bool isExternalAppModule = false;
0051     KPluginMetaData metaData;
0052 };
0053 
0054 MenuItem::MenuItem(bool isMenu, MenuItem *itsParent)
0055     : d(new Private())
0056 {
0057     d->parent = itsParent;
0058     d->menu = isMenu;
0059 
0060     if (d->parent) {
0061         d->parent->children().append(this);
0062     }
0063 }
0064 
0065 MenuItem::~MenuItem()
0066 {
0067     qDeleteAll(d->children);
0068     delete d;
0069 }
0070 
0071 void MenuItem::sortChildrenByWeight()
0072 {
0073     std::sort(d->children.begin(), d->children.end(), childIsLessThan);
0074 }
0075 
0076 MenuItem *MenuItem::child(int index)
0077 {
0078     return d->children.at(index);
0079 }
0080 
0081 QStringList MenuItem::keywords(bool doesRemoveDuplicates) const
0082 {
0083     QStringList listOfKeywords;
0084     const QJsonObject rawData = d->metaData.rawData();
0085     // Add English keywords so users in other languages won't have to switch IME when searching.
0086     if (!QLocale().name().startsWith(QLatin1String("en_"))) {
0087         listOfKeywords << rawData[QStringLiteral("KPlugin")][QStringLiteral("Name")].toString();
0088         listOfKeywords << d->metaData.value(QStringLiteral("X-KDE-Keywords"), QString()).split(QLatin1String(","));
0089     }
0090     listOfKeywords << KJsonUtils::readTranslatedString(rawData, QStringLiteral("X-KDE-Keywords")).split(QStringLiteral(","));
0091     listOfKeywords << d->name;
0092     for (MenuItem *child : std::as_const(d->children)) {
0093         listOfKeywords << child->keywords(false);
0094     }
0095     // Remove any soft hyphens (used in long words in some languages)
0096     listOfKeywords.replaceInStrings(QStringLiteral("\u00AD"), QString());
0097     // Only remove duplicate keywords in the end
0098     if (doesRemoveDuplicates) {
0099         listOfKeywords.removeDuplicates();
0100     }
0101     return listOfKeywords;
0102 }
0103 
0104 MenuItem *MenuItem::parent() const
0105 {
0106     return d->parent;
0107 }
0108 
0109 QList<MenuItem *> &MenuItem::children() const
0110 {
0111     return d->children;
0112 }
0113 
0114 QString MenuItem::comment() const
0115 {
0116     return d->comment;
0117 }
0118 
0119 QString MenuItem::iconName() const
0120 {
0121     return d->iconName;
0122 }
0123 
0124 bool MenuItem::isExternalAppModule() const
0125 {
0126     return d->isExternalAppModule;
0127 }
0128 
0129 bool MenuItem::isSystemsettingsCategory() const
0130 {
0131     return d->isSystemsettingsCategory;
0132 }
0133 
0134 QString MenuItem::systemsettingsCategoryModule() const
0135 {
0136     return d->systemsettingsCategoryModule;
0137 }
0138 
0139 bool MenuItem::isSystemsettingsRootCategory() const
0140 {
0141     return d->isSystemsettingsRootCategory;
0142 }
0143 
0144 QString &MenuItem::name() const
0145 {
0146     return d->name;
0147 }
0148 
0149 QString &MenuItem::category() const
0150 {
0151     return d->category;
0152 }
0153 
0154 int MenuItem::weight()
0155 {
0156     return d->weight;
0157 }
0158 
0159 bool MenuItem::menu() const
0160 {
0161     return d->menu;
0162 }
0163 
0164 void MenuItem::setCategoryConfig(const KDesktopFile &file)
0165 {
0166     const KConfigGroup grp = file.desktopGroup();
0167     d->category = grp.readEntry("X-KDE-System-Settings-Category");
0168     if (d->category.isEmpty()) {
0169         d->category = grp.readEntry("X-KDE-KInfoCenter-Category");
0170     }
0171     d->name = grp.readEntry("Name");
0172     d->weight = grp.readEntry(QStringLiteral("X-KDE-Weight"), 100);
0173     d->comment = grp.readEntry("Comment");
0174     d->iconName = grp.readEntry("Icon");
0175     d->isSystemsettingsCategory = true;
0176     d->systemsettingsCategoryModule = grp.readEntry("X-KDE-System-Settings-Category-Module");
0177     d->isSystemsettingsRootCategory = QFileInfo(file.fileName()).fileName() == QLatin1String("settings-root-category.desktop");
0178 }
0179 
0180 void MenuItem::setMetaData(const KPluginMetaData &data)
0181 {
0182     d->metaData = data;
0183     if (d->isSystemsettingsCategory) {
0184         return;
0185     }
0186     d->category = data.value(QStringLiteral("X-KDE-System-Settings-Category"));
0187     if (d->category.isEmpty()) {
0188         d->category = data.value(QStringLiteral("X-KDE-KInfoCenter-Category"));
0189     }
0190     d->name = data.name();
0191     d->weight = data.value(QStringLiteral("X-KDE-Weight"), 100);
0192     d->comment = data.description();
0193     d->iconName = data.iconName();
0194     d->systemsettingsCategoryModule = data.value(QStringLiteral("X-KDE-System-Settings-Category-Module"));
0195     d->isExternalAppModule = data.value(QStringLiteral("IsExternalApp"), false);
0196 }
0197 
0198 KPluginMetaData MenuItem::metaData()
0199 {
0200     return d->metaData;
0201 }
0202 
0203 bool MenuItem::showDefaultIndicator() const
0204 {
0205     return d->showDefaultIndicator;
0206 }
0207 
0208 bool MenuItem::isCategoryOwner() const
0209 {
0210     return d->isCategoryOwner;
0211 }
0212 
0213 void MenuItem::setCategoryOwner(bool owner)
0214 {
0215     d->isCategoryOwner = owner;
0216 }
0217 
0218 void MenuItem::updateDefaultIndicator()
0219 {
0220     d->showDefaultIndicator = false;
0221     if (isLibrary()) {
0222         std::unique_ptr<KCModuleData> moduleData(loadModuleData(d->metaData));
0223         d->showDefaultIndicator = moduleData && !moduleData->isDefaults();
0224     }
0225 
0226     if (menu()) {
0227         for (auto child : std::as_const(children())) {
0228             d->showDefaultIndicator |= child->showDefaultIndicator();
0229         }
0230     }
0231     if (d->parent) {
0232         d->parent->updateDefaultIndicator();
0233     }
0234 }
0235 
0236 void MenuItem::setDefaultIndicator(bool defaultIndicator)
0237 {
0238     d->showDefaultIndicator = defaultIndicator;
0239     if (menu()) {
0240         for (auto child : std::as_const(children())) {
0241             d->showDefaultIndicator |= child->showDefaultIndicator();
0242         }
0243     }
0244     if (d->parent) {
0245         d->parent->updateDefaultIndicator();
0246     }
0247 }
0248 
0249 MenuItem *MenuItem::descendantForModule(const QString &moduleName)
0250 {
0251     if (d->service) {
0252         if (d->service->desktopEntryName() == moduleName) {
0253             return this;
0254         }
0255     }
0256 
0257     if (d->metaData.isValid() && d->metaData.pluginId() == moduleName) {
0258         return this;
0259     }
0260 
0261     for (auto child : std::as_const(d->children)) {
0262         MenuItem *candidate = child->descendantForModule(moduleName);
0263         if (candidate) {
0264             return candidate;
0265         }
0266     }
0267 
0268     return nullptr;
0269 }
0270 
0271 bool MenuItem::isLibrary()
0272 {
0273     return d->metaData.isValid() && !isExternalAppModule();
0274 }