File indexing completed on 2024-07-14 15:50:43

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