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 }