KNewStuff

kmoretoolsmenufactory.cpp
1 /*
2  SPDX-FileCopyrightText: 2015 Gregor Mi <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "kmoretoolsmenufactory.h"
8 
9 #include "kmoretools_p.h"
10 #include "kmoretoolspresets_p.h"
11 #include "knewstuff_debug.h"
12 #include <QDebug>
13 
14 #include <KDialogJobUiDelegate>
15 #include <KLocalizedString>
16 #include <KIO/ApplicationLauncherJob>
17 #include <KMountPoint>
18 #include <KNS3/KMoreTools>
19 #include <KNS3/KMoreToolsPresets>
20 
21 class KMoreToolsMenuFactoryPrivate
22 {
23 public:
24  // Note that this object must live long enough in case the user opens
25  // the "Configure..." dialog
26  KMoreTools* kmt = nullptr;
27 
28  QMenu* menu = nullptr;
29  QWidget* parentWidget = nullptr;
30 };
31 
32 class KMoreToolsLazyMenu : public QMenu
33 {
34 private Q_SLOTS:
35  void onAboutToShow() {
36  //qDebug() << "onAboutToShow";
37  clear();
38  m_aboutToShowFunc(this);
39  }
40 
41 public:
42  KMoreToolsLazyMenu(QWidget* parent = nullptr) : QMenu(parent) {
43  connect(this, &QMenu::aboutToShow, this, &KMoreToolsLazyMenu::onAboutToShow);
44  }
45 
46  void setAboutToShowAction(std::function<void(QMenu*)> aboutToShowFunc) {
47  m_aboutToShowFunc = aboutToShowFunc;
48  }
49 
50 private:
51  std::function<void(QMenu*)> m_aboutToShowFunc;
52 };
53 
55  : d(new KMoreToolsMenuFactoryPrivate())
56 {
57  d->kmt = new KMoreTools(uniqueId);
58 }
59 
60 KMoreToolsMenuFactory::~KMoreToolsMenuFactory()
61 {
62  if (d->menu && !d->menu->parent()) {
63  delete d->menu;
64  }
65 
66  delete d->kmt;
67 
68  delete d;
69 }
70 
71 static void runApplication(const KService::Ptr &service, const QList<QUrl> &urls)
72 {
73  auto *job = new KIO::ApplicationLauncherJob(service);
74  job->setUrls(urls);
75  job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, nullptr));
76  job->start();
77 }
78 
79 // "file static" => no symbol will be exported
80 static void addItemFromKmtService(KMoreToolsMenuBuilder* menuBuilder,
81  QMenu* menu,
82  KMoreToolsService* kmtService,
83  const QUrl& url,
84  bool isMoreSection
85  )
86 {
87  auto menuItem = menuBuilder->addMenuItem(kmtService, isMoreSection ?
89 
90  if (kmtService->isInstalled()) {
91  auto kService = kmtService->installedService();
92 
93  if (!kService) {
94  // if the corresponding desktop file is not installed
95  // then the isInstalled was true because of the Exec line check
96  // and we use the desktopfile provided by KMoreTools.
97  // Otherwise *kService would crash.
98  qCDebug(KNEWSTUFF) << "Desktop file not installed:" << kmtService->desktopEntryName() << "=> Use desktop file provided by KMoreTools";
99  kService = kmtService->kmtProvidedService();
100  }
101 
102  if (!url.isEmpty() && kmtService->maxUrlArgCount() > 0) {
103  menu->connect(menuItem->action(), &QAction::triggered, menu,
104  [kService, url](bool) {
105  runApplication(kService, { url });
106  });
107  } else {
108  menu->connect(menuItem->action(), &QAction::triggered, menu,
109  [kService](bool) {
110  runApplication(kService, { });
111  });
112  }
113  }
114 }
115 
116 // "file static" => no symbol will be exported
117 static void addItemsFromKmtServiceList(KMoreToolsMenuBuilder* menuBuilder,
118  QMenu* menu,
119  const QList<KMoreToolsService*> &kmtServiceList,
120  const QUrl& url,
121  bool isMoreSection,
122  QString firstMoreSectionDesktopEntryName
123  )
124 {
125  for (auto kmtService : kmtServiceList) {
126  // Check the pointer just in case a null pointer got in somewhere
127  if (!kmtService) continue;
128  if (kmtService->desktopEntryName() == firstMoreSectionDesktopEntryName) {
129  // once we reach the potential first "more section desktop entry name"
130  // all remaining services are added to the more section by default
131  isMoreSection = true;
132  }
133  addItemFromKmtService(menuBuilder, menu, kmtService, url, isMoreSection);
134  }
135 }
136 
143 static void addItemsForGroupingNameWithSpecialHandling(KMoreToolsMenuBuilder* menuBuilder,
144  QMenu* menu,
145  QList<KMoreToolsService*> kmtServiceList,
146  const QString& groupingName,
147  const QUrl& url,
148  bool isMoreSection,
149  QString firstMoreSectionDesktopEntryName
150  )
151 {
152  //
153  // special handlings
154  //
155  if (groupingName == QLatin1String("disk-usage") && !url.isEmpty()) {
156  //
157  // "disk-usage" plus a given URL. If no url is given there is no need
158  // for special handling
159  //
160 
161  auto filelightAppIter = std::find_if(kmtServiceList.begin(),
162  kmtServiceList.end(),
163  [](KMoreToolsService* s) {
164  return s->desktopEntryName() == QLatin1String("org.kde.filelight");
165  });
166 
167  if (filelightAppIter != kmtServiceList.end()) {
168  auto filelightApp = *filelightAppIter;
169 
170  // because we later add all remaining items
171  kmtServiceList.removeOne(filelightApp);
172 
173  if (url.isLocalFile()) { // 2015-01-12: Filelight can handle FTP connections
174  // but KIO/kioexec cannot (bug or feature?), so we
175  // don't offer it in this case
176 
177  const auto filelight1Item = menuBuilder->addMenuItem(filelightApp);
178 
179  if (filelightApp->isInstalled()) {
180  const auto filelightService = filelightApp->installedService();
181 
182  filelight1Item->action()->setText(filelightApp->formatString(
183  i18nc("@action:inmenu %1=\"$GenericName\"", "%1 - current folder", QStringLiteral("$GenericName"))));
184  menu->connect(filelight1Item->action(), &QAction::triggered, menu,
185  [filelightService, url](bool) {
186  runApplication(filelightService, { url });
187  });
188 
189  const auto filelight2Item = menuBuilder->addMenuItem(filelightApp);
190  filelight2Item->action()->setText(filelightApp->formatString(
191  i18nc("@action:inmenu %1=\"$GenericName\"", "%1 - current device", QStringLiteral("$GenericName"))));
192  menu->connect(filelight2Item->action(), &QAction::triggered, menu,
193  [filelightService, url](bool) {
194  KMountPoint::Ptr mountPoint
196  runApplication(filelightService, { QUrl::fromLocalFile(mountPoint->mountPoint()) });
197  });
198  }
199  }
200 
201  auto filelight3Item = menuBuilder->addMenuItem(filelightApp, KMoreTools::MenuSection_More);
202  if (filelightApp->isInstalled()) {
203  filelight3Item->action()->setText(filelightApp->formatString(
204  i18nc("@action:inmenu %1=\"$GenericName\"", "%1 - all devices", QStringLiteral("$GenericName"))));
205  const auto filelightService = filelightApp->installedService();
206  menu->connect(filelight3Item->action(), &QAction::triggered, menu,
207  [filelightService](bool) {
208  runApplication(filelightService, { });
209  });
210  }
211  } else {
212  qWarning() << "org.kde.filelight should be present in KMoreTools but it is not!";
213  }
214 
215  } else if (groupingName == QLatin1String("disk-partitions")) {
216  // better because the Partition editors all have the same GenericName
217  menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName ($Name)"));
218 
219  addItemsFromKmtServiceList(menuBuilder, menu, kmtServiceList, url, isMoreSection, firstMoreSectionDesktopEntryName);
220 
221  menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default
222 
223  return; // skip processing remaining list (would result in duplicates)
224 
225  } else if (groupingName == QLatin1String("git-clients-and-actions")) {
226  // Here we change the default item text and make sure that the url
227  // argument is properly handled.
228  //
229 
230  menuBuilder->setInitialItemTextTemplate(QStringLiteral("$Name")); // just use the application name
231 
232  for (auto kmtService : qAsConst(kmtServiceList)) {
233  // Check the pointer just in case a null pointer got in somewhere
234  if (!kmtService) continue;
235  QUrl argUrl = url;
236 
237  if (url.isLocalFile()) { // this can only be done for local files, remote urls probably won't work for git clients anyway
238  // by default we need an URL pointing to a directory
239  // (this impl currently leads to wrong behaviour if the root dir of a git repo is chosen because it always goes one level up)
240  argUrl = KmtUrlUtil::localFileAbsoluteDir(url); // needs local file
241 
242  if (kmtService->desktopEntryName() == _("git-cola-view-history.kmt-edition")) {
243  // in this case we need the file because we would like to see its history
244  argUrl = url;
245  }
246  }
247 
248  addItemFromKmtService(menuBuilder, menu, kmtService, argUrl, isMoreSection);
249  }
250 
251  menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default
252 
253  return; // skip processing remaining list (would result in duplicates)
254  }
255 
256  //
257  // default handling (or process remaining list)
258  //
259  menuBuilder->setInitialItemTextTemplate(QStringLiteral("$Name")); // just use the application name
260  addItemsFromKmtServiceList(menuBuilder, menu, kmtServiceList, url, isMoreSection, firstMoreSectionDesktopEntryName);
261  menuBuilder->setInitialItemTextTemplate(QStringLiteral("$GenericName")); // set back to default
262 }
263 
265  const QStringList& groupingNames,
266  const QUrl& url)
267 {
268  delete d->menu;
269 
270  auto menu = new KMoreToolsLazyMenu(d->parentWidget);
271  menu->setAboutToShowAction([this, groupingNames, url](QMenu* m) { fillMenuFromGroupingNames(m, groupingNames, url); });
272  d->menu = menu;
273 
274  return d->menu;
275 }
276 
277 void KMoreToolsMenuFactory::fillMenuFromGroupingNames(QMenu* menu, const QStringList& groupingNames, const QUrl& url)
278 {
279  const auto menuBuilder = d->kmt->menuBuilder();
280  menuBuilder->clear();
281 
282  bool isMoreSection = false;
283 
284  for (const auto &groupingName : groupingNames) {
285 
286  if (groupingName == QLatin1String("more:")) {
287  isMoreSection = true;
288  continue;
289  }
290 
291  QString firstMoreSectionDesktopEntryName;
292  auto kmtServiceList = KMoreToolsPresetsPrivate::registerServicesByGroupingNames(
293  &firstMoreSectionDesktopEntryName, d->kmt, { groupingName });
294 
295  addItemsForGroupingNameWithSpecialHandling(menuBuilder,
296  menu,
297  kmtServiceList,
298  groupingName,
299  url,
300  isMoreSection,
301  firstMoreSectionDesktopEntryName);
302  }
303 
304  menuBuilder->buildByAppendingToMenu(menu);
305 }
306 
308 {
309  d->parentWidget = widget;
310 }
void setText(const QString &text)
A service described in a .desktop file (kmt-desktopfile) which will be called "registered service"...
Definition: kmoretools.h:361
void triggered(bool checked)
Define how the default structure of the menu should look like.
Definition: kmoretools.h:534
static List currentMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
void setParentWidget(QWidget *widget)
Set widget as the parent widget of the QMenu that will be created by createMenuFromGroupingNames().
int maxUrlArgCount() const
Definition: kmoretools.cpp:308
KMoreToolsMenuFactory(const QString &uniqueId)
void clear()
Clears all added menu items.
Definition: kmoretools.cpp:609
The item is placed in the "More" submenu.
Definition: kmoretools.h:197
bool isInstalled() const
Definition: kmoretools.cpp:278
KService::Ptr installedService() const
Definition: kmoretools.cpp:283
bool isEmpty() const const
QAction * action() const
Case 1 KMoreToolsMenuBuilder::addMenuItem was called with KKmoreToolsService* argument.
Definition: kmoretools.cpp:782
void setInitialItemTextTemplate(const QString &templateText)
Affects addMenuItem() if called before it.
Definition: kmoretools.cpp:589
void aboutToShow()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString toLocalFile() const const
QList::iterator end()
KService::Ptr kmtProvidedService() const
Definition: kmoretools.cpp:288
KMoreToolsMenuItem * addMenuItem(KMoreToolsService *registeredService, KMoreTools::MenuSection defaultLocation=KMoreTools::MenuSection_Main)
Adds a registered service (which can installed or not) to the menu.
Definition: kmoretools.cpp:594
Ptr findByPath(const QString &path) const
QString desktopEntryName() const
Definition: kmoretools.cpp:273
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void fillMenuFromGroupingNames(QMenu *menu, const QStringList &groupingNames, const QUrl &url=QUrl())
See createMenuFromGroupingNames except that the menu is not created but you have to provide one yours...
bool removeOne(const T &value)
QMenu * createMenuFromGroupingNames(const QStringList &groupingNames, const QUrl &url=QUrl())
For each grouping name menu items will be created an appended to a lazy menu which is returned...
QList::iterator begin()
void buildByAppendingToMenu(QMenu *menu, KMoreTools::ConfigureDialogAccessibleSetting configureDialogAccessibleSetting=KMoreTools::ConfigureDialogAccessible_Always, QMenu **outMoreMenu=nullptr)
Builds the actual menu and appends all items (main items, more submenu with a potential "not installe...
Definition: kmoretools.cpp:646
Helps to create user-configurable menus with tools which are potentially not yet installed.
Definition: kmoretools.h:161
The item is placed in the main section (default)
Definition: kmoretools.h:192
QUrl fromLocalFile(const QString &localFile)
bool isLocalFile() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Aug 11 2020 22:43:23 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.