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