Warning, file /libraries/kmoretools/src/kmoretoolsmenufactory.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2015 Gregor Mi <codestruct@posteo.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "kmoretoolsmenufactory.h"
0008 
0009 #include "kmoretools_debug.h"
0010 #include "kmoretools_p.h"
0011 #include "kmoretoolspresets_p.h"
0012 #include <QDebug>
0013 #include <QStorageInfo>
0014 
0015 #include <KDialogJobUiDelegate>
0016 #include <KIO/ApplicationLauncherJob>
0017 #include <KLocalizedString>
0018 
0019 #include "kmoretools.h"
0020 #include "kmoretoolspresets.h"
0021 
0022 class KMoreToolsMenuFactoryPrivate
0023 {
0024 public:
0025     // Note that this object must live long enough in case the user opens
0026     // the "Configure..." dialog
0027     KMoreTools *kmt = nullptr;
0028 
0029     QMenu *menu = nullptr;
0030     QWidget *parentWidget = nullptr;
0031 };
0032 
0033 class KMoreToolsLazyMenu : public QMenu
0034 {
0035 private Q_SLOTS:
0036     void onAboutToShow()
0037     {
0038         // qDebug() << "onAboutToShow";
0039         clear();
0040         m_aboutToShowFunc(this);
0041     }
0042 
0043 public:
0044     KMoreToolsLazyMenu(QWidget *parent = nullptr)
0045         : QMenu(parent)
0046     {
0047         connect(this, &QMenu::aboutToShow, this, &KMoreToolsLazyMenu::onAboutToShow);
0048     }
0049 
0050     void setAboutToShowAction(std::function<void(QMenu *)> aboutToShowFunc)
0051     {
0052         m_aboutToShowFunc = aboutToShowFunc;
0053     }
0054 
0055 private:
0056     std::function<void(QMenu *)> m_aboutToShowFunc;
0057 };
0058 
0059 KMoreToolsMenuFactory::KMoreToolsMenuFactory(const QString &uniqueId)
0060     : d(new KMoreToolsMenuFactoryPrivate())
0061 {
0062     d->kmt = new KMoreTools(uniqueId);
0063 }
0064 
0065 KMoreToolsMenuFactory::~KMoreToolsMenuFactory()
0066 {
0067     if (d->menu && !d->menu->parent()) {
0068         delete d->menu;
0069     }
0070 
0071     delete d->kmt;
0072 }
0073 
0074 static void runApplication(const KService::Ptr &service, const QList<QUrl> &urls)
0075 {
0076     auto *job = new KIO::ApplicationLauncherJob(service);
0077     job->setUrls(urls);
0078     job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, nullptr));
0079     job->start();
0080 }
0081 
0082 // "file static" => no symbol will be exported
0083 static void addItemFromKmtService(KMoreToolsMenuBuilder *menuBuilder, QMenu *menu, KMoreToolsService *kmtService, const QUrl &url, bool isMoreSection)
0084 {
0085     auto menuItem = menuBuilder->addMenuItem(kmtService, isMoreSection ? KMoreTools::MenuSection_More : KMoreTools::MenuSection_Main);
0086 
0087     if (kmtService->isInstalled()) {
0088         auto kService = kmtService->installedService();
0089 
0090         if (!kService) {
0091             // if the corresponding desktop file is not installed
0092             // then the isInstalled was true because of the Exec line check
0093             // and we use the desktopfile provided by KMoreTools.
0094             // Otherwise *kService would crash.
0095             qCDebug(KMORETOOLS) << "Desktop file not installed:" << kmtService->desktopEntryName() << "=> Use desktop file provided by KMoreTools";
0096             kService = kmtService->kmtProvidedService();
0097         }
0098 
0099         if (!url.isEmpty() && kmtService->maxUrlArgCount() > 0) {
0100             menu->connect(menuItem->action(), &QAction::triggered, menu, [kService, url](bool) {
0101                 runApplication(kService, {url});
0102             });
0103         } else {
0104             menu->connect(menuItem->action(), &QAction::triggered, menu, [kService](bool) {
0105                 runApplication(kService, {});
0106             });
0107         }
0108     }
0109 }
0110 
0111 // "file static" => no symbol will be exported
0112 static void addItemsFromKmtServiceList(KMoreToolsMenuBuilder *menuBuilder,
0113                                        QMenu *menu,
0114                                        const QList<KMoreToolsService *> &kmtServiceList,
0115                                        const QUrl &url,
0116                                        bool isMoreSection,
0117                                        QString firstMoreSectionDesktopEntryName)
0118 {
0119     for (auto kmtService : kmtServiceList) {
0120         // Check the pointer just in case a null pointer got in somewhere
0121         if (!kmtService) {
0122             continue;
0123         }
0124         if (kmtService->desktopEntryName() == firstMoreSectionDesktopEntryName) {
0125             // once we reach the potential first "more section desktop entry name"
0126             // all remaining services are added to the more section by default
0127             isMoreSection = true;
0128         }
0129         addItemFromKmtService(menuBuilder, menu, kmtService, url, isMoreSection);
0130     }
0131 }
0132 
0133 /**
0134  * "file static" => no symbol will be exported
0135  * @param isMoreSection: true => all items will be added into the more section
0136  * @param firstMoreSectionDesktopEntryName: only valid when @p isMoreSection is false:
0137  *                                           see KMoreToolsPresets::registerServicesByGroupingNames
0138  */
0139 static void addItemsForGroupingNameWithSpecialHandling(KMoreToolsMenuBuilder *menuBuilder,
0140                                                        QMenu *menu,
0141                                                        QList<KMoreToolsService *> kmtServiceList,
0142                                                        const QString &groupingName,
0143                                                        const QUrl &url,
0144                                                        bool isMoreSection,
0145                                                        QString firstMoreSectionDesktopEntryName)
0146 {
0147     //
0148     // special handlings
0149     //
0150     if (groupingName == QLatin1String("disk-usage") && !url.isEmpty()) {
0151         //
0152         // "disk-usage" plus a given URL. If no url is given there is no need
0153         // for special handling
0154         //
0155 
0156         auto filelightAppIter = std::find_if(kmtServiceList.begin(), kmtServiceList.end(), [](KMoreToolsService *s) {
0157             return s->desktopEntryName() == QLatin1String("org.kde.filelight");
0158         });
0159 
0160         if (filelightAppIter != kmtServiceList.end()) {
0161             auto filelightApp = *filelightAppIter;
0162 
0163             // because we later add all remaining items
0164             kmtServiceList.removeOne(filelightApp);
0165 
0166             const auto filelight1Item = menuBuilder->addMenuItem(filelightApp);
0167 
0168             if (filelightApp->isInstalled()) {
0169                 const auto filelightService = filelightApp->installedService();
0170 
0171                 filelight1Item->action()->setText(
0172                     filelightApp->formatString(i18nc("@action:inmenu %1=\"$GenericName\"", "%1 - current folder", QStringLiteral("$GenericName"))));
0173                 menu->connect(filelight1Item->action(), &QAction::triggered, menu, [filelightService, url](bool) {
0174                     runApplication(filelightService, {url});
0175                 });
0176 
0177                 // For remote URLs like FTP analyzing the device makes no sense
0178                 if (url.isLocalFile()) {
0179                     const auto filelight2Item = menuBuilder->addMenuItem(filelightApp);
0180                     filelight2Item->action()->setText(
0181                         filelightApp->formatString(i18nc("@action:inmenu %1=\"$GenericName\"", "%1 - current device", QStringLiteral("$GenericName"))));
0182                     menu->connect(filelight2Item->action(), &QAction::triggered, menu, [filelightService, url](bool) {
0183                         const QStorageInfo info(url.toLocalFile());
0184 
0185                         if (info.isValid() && info.isReady()) {
0186                             runApplication(filelightService, {QUrl::fromLocalFile(info.rootPath())});
0187                         }
0188                     });
0189                 }
0190 
0191                 auto filelight3Item = menuBuilder->addMenuItem(filelightApp, KMoreTools::MenuSection_More);
0192                 if (filelightApp->isInstalled()) {
0193                     filelight3Item->action()->setText(
0194                         filelightApp->formatString(i18nc("@action:inmenu %1=\"$GenericName\"", "%1 - all devices", QStringLiteral("$GenericName"))));
0195                     const auto filelightService = filelightApp->installedService();
0196                     menu->connect(filelight3Item->action(), &QAction::triggered, menu, [filelightService](bool) {
0197                         runApplication(filelightService, {});
0198                     });
0199                 }
0200             }
0201         } else {
0202             qWarning() << "org.kde.filelight should be present in KMoreTools but it is not!";
0203         }
0204 
0205     } else if (groupingName == QLatin1String("disk-partitions")) {
0206         // better because the Partition editors all have the same GenericName
0207         menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName ($Name)"));
0208 
0209         addItemsFromKmtServiceList(menuBuilder, menu, kmtServiceList, url, isMoreSection, firstMoreSectionDesktopEntryName);
0210 
0211         menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default
0212 
0213         return; // skip processing remaining list (would result in duplicates)
0214 
0215     } else if (groupingName == QLatin1String("git-clients-and-actions")) {
0216         // Here we change the default item text and make sure that the url
0217         // argument is properly handled.
0218         //
0219 
0220         menuBuilder->setInitialItemTextTemplate(QStringLiteral("$Name")); // just use the application name
0221 
0222         for (auto kmtService : std::as_const(kmtServiceList)) {
0223             // Check the pointer just in case a null pointer got in somewhere
0224             if (!kmtService) {
0225                 continue;
0226             }
0227             QUrl argUrl = url;
0228 
0229             if (url.isLocalFile()) { // this can only be done for local files, remote urls probably won't work for git clients anyway
0230                 // by default we need an URL pointing to a directory
0231                 // (this impl currently leads to wrong behaviour if the root dir of a git repo is chosen because it always goes one level up)
0232                 argUrl = KmtUrlUtil::localFileAbsoluteDir(url); // needs local file
0233 
0234                 if (kmtService->desktopEntryName() == _("git-cola-view-history.kmt-edition")) {
0235                     // in this case we need the file because we would like to see its history
0236                     argUrl = url;
0237                 }
0238             }
0239 
0240             addItemFromKmtService(menuBuilder, menu, kmtService, argUrl, isMoreSection);
0241         }
0242 
0243         menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default
0244 
0245         return; // skip processing remaining list (would result in duplicates)
0246     }
0247 
0248     //
0249     // default handling (or process remaining list)
0250     //
0251     menuBuilder->setInitialItemTextTemplate(QStringLiteral("$Name")); // just use the application name
0252     addItemsFromKmtServiceList(menuBuilder, menu, kmtServiceList, url, isMoreSection, firstMoreSectionDesktopEntryName);
0253     menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default
0254 }
0255 
0256 QMenu *KMoreToolsMenuFactory::createMenuFromGroupingNames(const QStringList &groupingNames, const QUrl &url)
0257 {
0258     delete d->menu;
0259 
0260     auto menu = new KMoreToolsLazyMenu(d->parentWidget);
0261     menu->setAboutToShowAction([this, groupingNames, url](QMenu *m) {
0262         fillMenuFromGroupingNames(m, groupingNames, url);
0263     });
0264     d->menu = menu;
0265 
0266     return d->menu;
0267 }
0268 
0269 void KMoreToolsMenuFactory::fillMenuFromGroupingNames(QMenu *menu, const QStringList &groupingNames, const QUrl &url)
0270 {
0271     const auto menuBuilder = d->kmt->menuBuilder();
0272     menuBuilder->clear();
0273 
0274     bool isMoreSection = false;
0275 
0276     for (const auto &groupingName : groupingNames) {
0277         if (groupingName == QLatin1String("more:")) {
0278             isMoreSection = true;
0279             continue;
0280         }
0281 
0282         QString firstMoreSectionDesktopEntryName;
0283         auto kmtServiceList = KMoreToolsPresetsPrivate::registerServicesByGroupingNames(&firstMoreSectionDesktopEntryName, d->kmt, {groupingName});
0284 
0285         addItemsForGroupingNameWithSpecialHandling(menuBuilder, menu, kmtServiceList, groupingName, url, isMoreSection, firstMoreSectionDesktopEntryName);
0286     }
0287 
0288     menuBuilder->buildByAppendingToMenu(menu);
0289 }
0290 
0291 void KMoreToolsMenuFactory::setParentWidget(QWidget *widget)
0292 {
0293     d->parentWidget = widget;
0294 }