KNewStuff

kmoretools.cpp
1 /*
2  SPDX-FileCopyrightText: 2015 Gregor Mi <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "kmoretools.h"
8 
9 #include "kmoretools_p.h"
10 #include "kmoretoolsconfigdialog_p.h"
11 #include "knewstuff_debug.h"
12 
13 #include <QDebug>
14 #include <QStandardPaths>
15 #include <QApplication>
16 
17 #include <KLocalizedString>
18 #include <KConfig>
19 #include <KConfigGroup>
20 
21 class KMoreToolsPrivate
22 {
23 public:
24  QString uniqueId;
25 
26  // allocated via new, don't forget to delete
27  QList<KMoreToolsService*> serviceList;
28 
30 
31 public:
32  KMoreToolsPrivate(const QString& uniqueId)
33  : uniqueId(uniqueId)
34  {
35  }
36 
37  ~KMoreToolsPrivate()
38  {
39  qDeleteAll(menuBuilderMap);
40  qDeleteAll(serviceList);
41  }
42 
47  QString kmtDesktopfileSubdirOrUniqueId(const QString& kmtDesktopfileSubdir) {
48  if (kmtDesktopfileSubdir.isEmpty()) {
49  return uniqueId;
50  }
51 
52  return kmtDesktopfileSubdir;
53  }
54 
63  QString findFileInKmtDesktopfilesDir(const QString& filename)
64  {
65  return findFileInKmtDesktopfilesDir(uniqueId, filename);
66  }
67 
68  static QString findFileInKmtDesktopfilesDir(const QString& kmtDesktopfileSubdir, const QString& filename)
69  {
70  const QString kmtDesktopfilesFilename = QLatin1String("kf5/kmoretools/") + kmtDesktopfileSubdir + QLatin1Char('/') + filename;
71  const QString foundKmtFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, kmtDesktopfilesFilename);
72 
73  return foundKmtFile;
74  }
75 };
76 
78  : d(new KMoreToolsPrivate(uniqueId))
79 {
80 
81 }
82 
83 KMoreTools::~KMoreTools()
84 {
85  delete d;
86 }
87 
89  const QString& desktopEntryName,
90  const QString& kmtDesktopfileSubdir,
91  KMoreTools::ServiceLocatingMode serviceLocatingMode)
92 {
93  const QString foundKmtDesktopfilePath = d->findFileInKmtDesktopfilesDir(
94  d->kmtDesktopfileSubdirOrUniqueId(kmtDesktopfileSubdir),
95  desktopEntryName + QLatin1String(".desktop"));
96  const bool isKmtDesktopfileProvided = !foundKmtDesktopfilePath.isEmpty();
97 
98  KService::Ptr kmtDesktopfile;
99 
100  if (isKmtDesktopfileProvided) {
101  kmtDesktopfile = KService::Ptr(new KService(foundKmtDesktopfilePath));
102  // todo later: what exactly does "isValid" mean? Valid syntax? Or installed in system?
103  // right now we cannot use it
104  //Q_ASSERT_X(kmtDesktopfile->isValid(), "addServiceByDesktopFile", "the kmt-desktopfile is provided but not valid. This must be fixed.");
105  //qDebug() << " INFO: kmt-desktopfile provided and valid.";
106  if (kmtDesktopfile->exec().isEmpty()) {
107  qCCritical(KNEWSTUFF) << "KMoreTools::registerServiceByDesktopEntryName: the kmt-desktopfile " << desktopEntryName << " is provided but no Exec line is specified. The desktop file is probably faulty. Please fix. Return nullptr.";
108  return nullptr;
109  }
110  //qDebug() << " INFO: kmt-desktopfile provided.";
111  } else {
112  qCWarning(KNEWSTUFF) << "KMoreTools::registerServiceByDesktopEntryName: desktopEntryName " << desktopEntryName << " (kmtDesktopfileSubdir=" << kmtDesktopfileSubdir << ") not provided (or at the wrong place) in the installed kmt-desktopfiles directory. If the service is also not installed on the system the user won't get nice translated app name and description.";
113  qCDebug(KNEWSTUFF) << "`-- More info at findFileInKmtDesktopfilesDir, QStandardPaths::standardLocations = " << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); // /usr/share etc.
114  }
115 
116  bool isInstalled = false;
117  KService::Ptr installedService;
118  if (serviceLocatingMode == KMoreTools::ServiceLocatingMode_Default) { // == default behaviour: search for installed services
119  installedService = KService::serviceByDesktopName(desktopEntryName);
120  isInstalled = installedService != nullptr;
121  } else if (serviceLocatingMode == KMoreTools::ServiceLocatingMode_ByProvidedExecLine) { // only use provided kmt-desktopfile:
122  if (!isKmtDesktopfileProvided) {
123  qCCritical(KNEWSTUFF) << "KMoreTools::registerServiceByDesktopEntryName for " << desktopEntryName << ": If detectServiceExistenceViaProvidedExecLine is true then a kmt-desktopfile must be provided. Please fix. Return nullptr.";
124  return nullptr;
125  }
126 
127  auto tryExecProp = kmtDesktopfile->property(QStringLiteral("TryExec"), QVariant::String);
128  isInstalled = (tryExecProp.isValid() && !QStandardPaths::findExecutable(tryExecProp.toString()).isEmpty())
129  || !QStandardPaths::findExecutable(kmtDesktopfile->exec()).isEmpty();
130  } else {
131  Q_ASSERT(false); // case not handled
132  }
133 
134  auto registeredService = new KMoreToolsService(
135  d->kmtDesktopfileSubdirOrUniqueId(kmtDesktopfileSubdir),
136  desktopEntryName,
137  isInstalled,
138  installedService,
139  kmtDesktopfile);
140 
141  // add or replace item in serviceList
142  auto foundService = std::find_if(d->serviceList.begin(), d->serviceList.end(),
143  [desktopEntryName](KMoreToolsService* service) {
144  return service->desktopEntryName() == desktopEntryName;
145  });
146  if (foundService == d->serviceList.end()) {
147  d->serviceList.append(registeredService);
148  } else {
149  KMoreToolsService* foundServicePtr = *foundService;
150  int i = d->serviceList.indexOf(foundServicePtr);
151  delete foundServicePtr;
152  d->serviceList.replace(i, registeredService);
153  }
154 
155  return registeredService;
156 }
157 
158 KMoreToolsMenuBuilder* KMoreTools::menuBuilder(const QString& userConfigPostfix) const
159 {
160  if (d->menuBuilderMap.find(userConfigPostfix) == d->menuBuilderMap.end()) {
161  d->menuBuilderMap.insert(userConfigPostfix,
162  new KMoreToolsMenuBuilder(d->uniqueId, userConfigPostfix));
163  }
164  return d->menuBuilderMap[userConfigPostfix];
165 }
166 
167 // ------------------------------------------------------------------------------------------------
168 // ------------------------------------------------------------------------------------------------
169 
170 class KMoreToolsServicePrivate
171 {
172 public:
173  QString kmtDesktopfileSubdir;
174  QString desktopEntryName;
175  KService::Ptr installedService;
176  KService::Ptr kmtDesktopfile;
177  QUrl homepageUrl;
178  int maxUrlArgCount = 0;
179  bool isInstalled = false;
180  QString appstreamId;
181 
182 
183 public:
184  QString getServiceName()
185  {
186  if (installedService) {
187  return installedService->name();
188  } else {
189  if (kmtDesktopfile) {
190  return kmtDesktopfile->name();
191  } else {
192  return QString();
193  }
194  }
195  }
196 
197  QString getServiceGenericName()
198  {
199  if (installedService) {
200  return installedService->genericName();
201  } else {
202  if (kmtDesktopfile) {
203  return kmtDesktopfile->genericName();
204  } else {
205  return QString();
206  }
207  }
208  }
209 
213  QIcon getKmtProvidedIcon()
214  {
215  if (!kmtDesktopfile) {
216  return QIcon();
217  }
218 
219  QString iconPath = KMoreToolsPrivate::findFileInKmtDesktopfilesDir(kmtDesktopfileSubdir, kmtDesktopfile->icon() + QLatin1String(".svg"));
220  QIcon svgIcon(iconPath);
221  if (!svgIcon.isNull()) {
222  return svgIcon;
223  }
224 
225  iconPath = KMoreToolsPrivate::findFileInKmtDesktopfilesDir(kmtDesktopfileSubdir, kmtDesktopfile->icon() + QLatin1String(".png"));
226  QIcon pngIcon(iconPath);
227  if (!pngIcon.isNull()) {
228  return pngIcon;
229  }
230 
231  return QIcon();
232  }
233 };
234 
235 KMoreToolsService::KMoreToolsService(const QString& kmtDesktopfileSubdir,
236  const QString& desktopEntryName,
237  bool isInstalled,
238  KService::Ptr installedService,
239  KService::Ptr kmtDesktopfile)
240  : d(new KMoreToolsServicePrivate())
241 {
242  d->kmtDesktopfileSubdir = kmtDesktopfileSubdir;
243  d->desktopEntryName = desktopEntryName;
244  d->isInstalled = isInstalled;
245  d->installedService = installedService;
246  d->kmtDesktopfile = kmtDesktopfile;
247 }
248 
249 KMoreToolsService::~KMoreToolsService()
250 {
251  delete d;
252 }
253 
255 {
256  return d->desktopEntryName;
257 }
258 
260 {
261  return d->isInstalled;
262 }
263 
265 {
266  return d->installedService;
267 }
268 
270 {
271  return d->kmtDesktopfile;
272 }
273 
275 {
276  return d->getKmtProvidedIcon();
277 }
278 
280 {
281  return d->homepageUrl;
282 }
283 
285 {
286  d->homepageUrl = url;
287 }
288 
290 {
291  return d->maxUrlArgCount;
292 }
293 
294 void KMoreToolsService::setMaxUrlArgCount(int maxUrlArgCount)
295 {
296  d->maxUrlArgCount = maxUrlArgCount;
297 }
298 
300 {
301  QString result = formatString;
302 
303  QString genericName = d->getServiceGenericName();
304  if (genericName.isEmpty()) {
305  genericName = d->getServiceName();
306  if (genericName.isEmpty()) {
307  genericName = desktopEntryName();
308  }
309  }
310 
311  QString name = d->getServiceName();
312  if (name.isEmpty()) {
313  name = desktopEntryName();
314  }
315 
316  result.replace(QLatin1String("$GenericName"), genericName);
317  result.replace(QLatin1String("$Name"), name);
318  result.replace(QLatin1String("$DesktopEntryName"), desktopEntryName());
319 
320  return result;
321 }
322 
324 {
325  if (installedService() != nullptr) {
326  return QIcon::fromTheme(installedService()->icon());
327  } else if (kmtProvidedService() != nullptr) {
328  return d->getKmtProvidedIcon();
329  } else {
330  return QIcon();
331  }
332 }
333 
335 {
336  auto service = installedService();
337  if (service) {
338  service->setExec(exec);
339  }
340 }
341 
343 {
344  return d->appstreamId;
345 }
346 
348 {
349  d->appstreamId = id;
350 }
351 
352 
353 // ------------------------------------------------------------------------------------------------
354 // ------------------------------------------------------------------------------------------------
355 
356 const QString configFile = QStringLiteral("kmoretoolsrc");
357 const QString configKey = QStringLiteral("menu_structure");
358 
359 class KMoreToolsMenuBuilderPrivate
360 {
361 public:
362  QString uniqueId;
366  QString userConfigPostfix;
367  QList<KMoreToolsMenuItem*> menuItems;
368  KmtMenuItemIdGen menuItemIdGen;
369  QString initialItemTextTemplate = QStringLiteral("$GenericName");
370 
371 public:
372  KMoreToolsMenuBuilderPrivate()
373  {
374  }
375 
376  ~KMoreToolsMenuBuilderPrivate()
377  {
378  }
379 
380  void deleteAndClearMenuItems()
381  {
382  for (auto item : qAsConst(menuItems))
383  {
384  delete item;
385  }
386 
387  menuItems.clear();
388  }
389 
390  KmtMenuStructureDto readUserConfig() const
391  {
393  auto configGroup = config.group(uniqueId + userConfigPostfix);
394  QString json = configGroup.readEntry(configKey);
395  KmtMenuStructureDto configuredStructure;
396  configuredStructure.deserialize(json);
397  return configuredStructure;
398  }
399 
400  void writeUserConfig(const KmtMenuStructureDto& mstruct) const
401  {
403  auto configGroup = config.group(uniqueId + userConfigPostfix);
404  auto configValue = mstruct.serialize();
405  configGroup.writeEntry(configKey, configValue);
406  configGroup.sync();
407  }
408 
409  enum CreateMenuStructureOption
410  {
411  CreateMenuStructure_Default,
412  CreateMenuStructure_MergeWithUserConfig,
413  };
414 
445  KmtMenuStructure createMenuStructure(CreateMenuStructureOption createMenuStructureOption) const
446  {
447  KmtMenuStructureDto configuredStructure; // if this stays empty then the default structure will not be changed
448  if (createMenuStructureOption == CreateMenuStructure_MergeWithUserConfig) {
449  // fill if should be merged
450  configuredStructure = readUserConfig();
451  }
452 
453  KmtMenuStructure mstruct;
454 
455  QList<KMoreToolsMenuItem*> menuItemsSource = menuItems;
456  QList<KMoreToolsMenuItem*> menuItemsSortedAsConfigured;
457 
458  // presort as in configuredStructure
459  for (const auto& item : qAsConst(configuredStructure.list)) {
460  auto foundItem = std::find_if(menuItemsSource.begin(), menuItemsSource.end(),
461  [item](const KMoreToolsMenuItem* kMenuItem) {
462  return kMenuItem->id() == item.id;
463  });
464  if (foundItem != menuItemsSource.end()) {
465  menuItemsSortedAsConfigured.append(*foundItem); // add to final list
466  menuItemsSource.removeOne(*foundItem); // remove from source
467  }
468  }
469  // Add remaining items from source. These may be main and more section items
470  // so that the resulting list may have [ main items, more items, main items, more items ]
471  // instead of only [ main items, more items ]
472  // But in the next step this won't matter.
473  menuItemsSortedAsConfigured.append(menuItemsSource);
474 
475  // build MenuStructure from presorted list
476  for (auto item : qAsConst(menuItemsSortedAsConfigured)) {
477 
478  const auto registeredService = item->registeredService();
479 
480  if ((registeredService && registeredService->isInstalled())
481  || !registeredService) { // if a QAction was registered directly
482  auto confItem = configuredStructure.findInstalled(item->id());
483  if ((!confItem && item->defaultLocation() == KMoreTools::MenuSection_Main)
484  || (confItem && confItem->menuSection == KMoreTools::MenuSection_Main)) {
485  mstruct.mainItems.append(item);
486  } else if ((!confItem && item->defaultLocation() == KMoreTools::MenuSection_More)
487  || (confItem && confItem->menuSection == KMoreTools::MenuSection_More)) {
488  mstruct.moreItems.append(item);
489  } else {
490  Q_ASSERT_X(false, "buildAndAppendToMenu", "invalid enum"); // todo/later: apart from static programming error, if the config garbage this might happen
491  }
492  } else {
493  if (!mstruct.notInstalledServices.contains(item->registeredService())) {
494  mstruct.notInstalledServices.append(item->registeredService());
495  }
496  }
497  }
498 
499  return mstruct;
500  }
501 
505  void showConfigDialog(KmtMenuStructureDto defaultStructureDto, const QString& title = QString()) const
506  {
507  // read from config
508  auto currentStructure = createMenuStructure(CreateMenuStructure_MergeWithUserConfig);
509  auto currentStructureDto = currentStructure.toDto();
510 
511  KMoreToolsConfigDialog *dlg = new KMoreToolsConfigDialog(defaultStructureDto, currentStructureDto, title);
512  if (dlg->exec() == QDialog::Accepted) {
513  currentStructureDto = dlg->currentStructure();
514  writeUserConfig(currentStructureDto);
515  }
516 
517  delete dlg;
518  }
519 
524  void createMoreMenu(const KmtMenuStructure &mstruct, QMenu *parent)
525  {
526  for (auto item : qAsConst(mstruct.moreItems)) {
527  const auto action = item->action();
528  action->setParent(parent);
529  parent->addAction(action);
530  }
531 
532  if (!mstruct.notInstalledServices.isEmpty()) {
533  parent->addSection(i18nc("@action:inmenu", "Not installed:"));
534 
535  for (auto registeredService : qAsConst(mstruct.notInstalledServices)) {
536 
537  QMenu* submenuForNotInstalled = KmtNotInstalledUtil::createSubmenuForNotInstalledApp(
538  registeredService->formatString(QStringLiteral("$Name")), parent, registeredService->icon(), registeredService->homepageUrl(), registeredService->appstreamId());
539  parent->addMenu(submenuForNotInstalled);
540  }
541  }
542  }
543 };
544 
545 KMoreToolsMenuBuilder::KMoreToolsMenuBuilder()
546 {
547  Q_ASSERT(false);
548 }
549 
550 KMoreToolsMenuBuilder::KMoreToolsMenuBuilder(const QString& uniqueId, const QString& userConfigPostfix)
551  : d(new KMoreToolsMenuBuilderPrivate())
552 {
553  d->uniqueId = uniqueId;
554  d->userConfigPostfix = userConfigPostfix;
555 }
556 
557 KMoreToolsMenuBuilder::~KMoreToolsMenuBuilder()
558 {
559  d->deleteAndClearMenuItems();
560  delete d;
561 }
562 
564 {
565  d->initialItemTextTemplate = templateText;
566 }
567 
569 {
570  auto kmtMenuItem = new KMoreToolsMenuItem(registeredService, defaultLocation, d->initialItemTextTemplate);
571  kmtMenuItem->setId(d->menuItemIdGen.getId(registeredService->desktopEntryName()));
572  d->menuItems.append(kmtMenuItem);
573  return kmtMenuItem;
574 }
575 
577 {
578  auto kmtMenuItem = new KMoreToolsMenuItem(action, d->menuItemIdGen.getId(itemId), defaultLocation);
579  d->menuItems.append(kmtMenuItem);
580  return kmtMenuItem;
581 }
582 
584 {
585  d->deleteAndClearMenuItems();
586  d->menuItemIdGen.reset();
587 }
588 
589 QString KMoreToolsMenuBuilder::menuStructureAsString(bool mergeWithUserConfig) const
590 {
591  KmtMenuStructure mstruct = d->createMenuStructure(mergeWithUserConfig ?
592  KMoreToolsMenuBuilderPrivate::CreateMenuStructure_MergeWithUserConfig
593  : KMoreToolsMenuBuilderPrivate::CreateMenuStructure_Default);
594  QString s;
595  s += QLatin1String("|main|:");
596  for (auto item : qAsConst(mstruct.mainItems)) {
597  s += item->registeredService()->desktopEntryName() + QLatin1Char('.');
598  }
599  s += QLatin1String("|more|:");
600  for (auto item : qAsConst(mstruct.moreItems)) {
601  s += item->registeredService()->desktopEntryName() + QLatin1Char('.');
602  }
603  s += QLatin1String("|notinstalled|:");
604  for (auto regService : qAsConst(mstruct.notInstalledServices)) {
605  s += regService->desktopEntryName() + QLatin1Char('.');
606  }
607  return s;
608 }
609 
610 // TMP / for unit test
611 void KMoreToolsMenuBuilder::showConfigDialog(const QString& title)
612 {
613  d->showConfigDialog(d->createMenuStructure(KMoreToolsMenuBuilderPrivate::CreateMenuStructure_Default).toDto(), title);
614 }
615 
617  KMoreTools::ConfigureDialogAccessibleSetting configureDialogAccessibleSetting, QMenu** outMoreMenu)
618 {
619  KmtMenuStructure mstruct = d->createMenuStructure(KMoreToolsMenuBuilderPrivate::CreateMenuStructure_MergeWithUserConfig);
620 
621  for (auto item : qAsConst(mstruct.mainItems)) {
622  const auto action = item->action();
623  if (!action->parent()) { // if the action has no parent, set it to the menu to be filled
624  action->setParent(menu);
625  }
626  menu->addAction(action);
627  }
628 
629  QMenu* moreMenu = new QMenu(i18nc("@action:inmenu", "More"), menu);
630 
631  if (!mstruct.moreItems.isEmpty() || !mstruct.notInstalledServices.isEmpty()) {
632 
633  if (mstruct.mainItems.isEmpty()) {
634  d->createMoreMenu(mstruct, menu);
635  } else {
636  menu->addSeparator();
637  menu->addMenu(moreMenu);
638  d->createMoreMenu(mstruct, moreMenu);
639  }
640  }
641 
642  if (moreMenu->isEmpty()) {
643  if (outMoreMenu) {
644  *outMoreMenu = nullptr;
645  }
646  } else {
647  if (outMoreMenu) {
648  *outMoreMenu = moreMenu;
649  }
650  }
651 
652  QMenu* baseMenu;
653  // either the "Configure..." menu should be shown via setting or the Ctrl key is pressed
654  if (configureDialogAccessibleSetting == KMoreTools::ConfigureDialogAccessible_Always
656  || (configureDialogAccessibleSetting == KMoreTools::ConfigureDialogAccessible_Defensive && !mstruct.notInstalledServices.empty())) {
657  if (moreMenu->isEmpty()) { // "more" menu was not created...
658  // ...then we add the configure menu to the main menu
659  baseMenu = menu;
660  } else { // more menu has items
661  // ...then it was added to main menu and has got at least on item
662  baseMenu = moreMenu;
663  }
664 
665  if (!baseMenu->isEmpty()) {
666  baseMenu->addSeparator();
667  auto configureAction = baseMenu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu", "Configure..."));
668  configureAction->setData(QStringLiteral("configureItem")); // tag the action (currently only used in unit-test)
669  KmtMenuStructure mstructDefault = d->createMenuStructure(KMoreToolsMenuBuilderPrivate::CreateMenuStructure_Default);
670  KmtMenuStructureDto mstructDefaultDto = mstructDefault.toDto(); // makes sure the "Reset" button works as expected
671  QObject::connect(configureAction, &QAction::triggered, configureAction, [this, mstructDefaultDto](bool) {
672  this->d->showConfigDialog(mstructDefaultDto);
673  });
674  }
675  }
676 }
677 
678 // ------------------------------------------------------------------------------------------------
679 // ------------------------------------------------------------------------------------------------
680 
681 class KMoreToolsMenuItemPrivate
682 {
683 public:
684  QString id;
685  KMoreToolsService* registeredService = nullptr;
686  QString initialItemText;
687  QAction* action = nullptr;
688  KMoreTools::MenuSection defaultLocation;
689  bool actionAutoCreated = false; // action might stay nullptr even if actionCreated is true
690 };
691 
692 KMoreToolsMenuItem::KMoreToolsMenuItem(KMoreToolsService* registeredService, KMoreTools::MenuSection defaultLocation, const QString& initialItemTextTemplate)
693  : d(new KMoreToolsMenuItemPrivate())
694 {
695  d->registeredService = registeredService;
696  d->defaultLocation = defaultLocation;
697 
698  // set menu item caption (text)
699  QString defaultName = registeredService->formatString(initialItemTextTemplate); // e.g. "$GenericName", "$Name"
700  d->initialItemText = registeredService->formatString(defaultName);
701 }
702 
703 KMoreToolsMenuItem::KMoreToolsMenuItem(QAction* action, const QString& itemId, KMoreTools::MenuSection defaultLocation)
704  : d(new KMoreToolsMenuItemPrivate())
705 {
706  d->action = action;
707  d->id = itemId;
708  d->defaultLocation = defaultLocation;
709 }
710 
711 KMoreToolsMenuItem::~KMoreToolsMenuItem()
712 {
713  if (d->actionAutoCreated && d->action) { // Only do this if KMoreTools created the action. Other actions must be deleted by client.
714  // d->action can already be nullptr in some cases.
715  // Disconnects the 'connect' event (and potentially more; is this bad?)
716  // that was connected in action() to detect action deletion.
717  d->action->disconnect(d->action);
718  }
719  delete d;
720 }
721 
723 {
724  return d->id;
725 }
726 
728 {
729  d->id = id;
730 }
731 
733 {
734  return d->registeredService;
735 }
736 
738 {
739  return d->defaultLocation;
740 }
741 
743 {
744  return d->initialItemText;
745 }
746 
748 {
749  d->initialItemText = itemText;
750 }
751 
753 {
754  // currently we assume if a registeredService is given we auto-create the QAction once
755  if (d->registeredService && !d->actionAutoCreated) {
756  d->actionAutoCreated = true;
757 
758  if (d->registeredService->isInstalled()) {
759  d->action = new QAction(d->registeredService->icon(), d->initialItemText, nullptr);
760  // reset the action cache when action gets destroyed
761  // this happens in unit-tests where menu.clear() is called before another buildByAppendingToMenu call
762  // WARN: see also destructor! (might be a source of bugs?)
763  QObject::connect(d->action, &QObject::destroyed, d->action, [this]() {
764  this->d->actionAutoCreated = false;
765  this->d->action = nullptr;
766  });
767  } else {
768  d->action = nullptr;
769  }
770  }
771  // else:
772  // !d->registeredService => action will be provided by user
773  // or d->actionAutoCreated => action was autocreated (or set to nullptr if service not installed)
774 
775  return d->action;
776 }
777 
ConfigureDialogAccessibleSetting
Specify if the Configure dialog be accessible from the menu (via a "Configure..." menu item) ...
Definition: kmoretools.h:221
void clear()
ControlModifier
A service described in a .desktop file (kmt-desktopfile) which will be called "registered service"...
Definition: kmoretools.h:361
void triggered(bool checked)
QUrl homepageUrl() const
Definition: kmoretools.cpp:279
Define how the default structure of the menu should look like.
Definition: kmoretools.h:534
static Ptr serviceByDesktopName(const QString &_name)
QString appstreamId() const
Returns the associated appstream id that was previously set with setAppstreamId().
Definition: kmoretools.cpp:342
QIcon icon() const
Definition: kmoretools.cpp:323
Represents a menu item of a service (application, tool or variant of the same service with different ...
Definition: kmoretools.h:671
KMoreToolsService * registeredService() const
Definition: kmoretools.cpp:732
void setAppstreamId(const QString &)
Sets the appstream id of the service.
Definition: kmoretools.cpp:347
QString findExecutable(const QString &executableName, const QStringList &paths)
int maxUrlArgCount() const
Definition: kmoretools.cpp:289
void clear()
Clears all added menu items.
Definition: kmoretools.cpp:583
The item is placed in the "More" submenu.
Definition: kmoretools.h:197
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
bool isInstalled() const
Definition: kmoretools.cpp:259
KService::Ptr installedService() const
Definition: kmoretools.cpp:264
by existence of desktop file (discoverable by KService)
Definition: kmoretools.h:175
QExplicitlySharedDataPointer< KService > Ptr
Qt::KeyboardModifiers keyboardModifiers()
QAction * addAction(const QString &text)
Always show the "Configure..." menu item (default)
Definition: kmoretools.h:227
QStringList standardLocations(QStandardPaths::StandardLocation type)
QAction * addSection(const QString &text)
KMoreTools(const QString &uniqueId)
Definition: kmoretools.cpp:77
bool isEmpty() const const
QAction * action() const
Case 1 KMoreToolsMenuBuilder::addMenuItem was called with KKmoreToolsService* argument.
Definition: kmoretools.cpp:752
void setInitialItemTextTemplate(const QString &templateText)
Affects addMenuItem() if called before it.
Definition: kmoretools.cpp:563
KMoreToolsMenuBuilder * menuBuilder(const QString &userConfigPostfix=QString()) const
Definition: kmoretools.cpp:158
void append(const T &value)
void setHomepageUrl(const QUrl &url)
Sets the homepage url the user is shown when a service is not installed.
Definition: kmoretools.cpp:284
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setId(const QString &id)
(Optional) to help with stable ids (see id())
Definition: kmoretools.cpp:727
bool isEmpty() const const
QAction * addSeparator()
KMoreTools::MenuSection defaultLocation() const
see KMoreToolsMenuBuilder::addMenuItem
Definition: kmoretools.cpp:737
ServiceLocatingMode
Specify how should be determined if a service is installed or not.
Definition: kmoretools.h:170
void setData(const QVariant &userData)
MenuSection
Specify where a menu item be placed by default.
Definition: kmoretools.h:187
QList::iterator end()
KService::Ptr kmtProvidedService() const
Definition: kmoretools.cpp:269
KMoreToolsService * registerServiceByDesktopEntryName(const QString &desktopEntryName, const QString &kmtDesktopfileSubdir=QString(), ServiceLocatingMode serviceLocatingMode=ServiceLocatingMode_Default)
Registers a service with KMoreTools.
Definition: kmoretools.cpp:88
Defensively show the "Configure..." menu item.
Definition: kmoretools.h:241
KConfigGroup group(const QString &group)
QString & replace(int position, int n, QChar after)
void setExec(const QString &exec)
Will override the "Exec=" line of the service.
Definition: kmoretools.cpp:334
QString formatString(const QString &formatString) const
Definition: kmoretools.cpp:299
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:568
by existence of executable defined in the TryExec or Exec line of the provided kmt-desktopfile ...
Definition: kmoretools.h:181
QAction * addMenu(QMenu *menu)
QString desktopEntryName() const
Definition: kmoretools.cpp:254
QIcon fromTheme(const QString &name)
QString id() const
Auto-generated unique id that tries to be as stable as possible even if the menu gets restructured af...
Definition: kmoretools.cpp:722
QString initialItemText() const
see setInitialItemText()
Definition: kmoretools.cpp:742
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setMaxUrlArgCount(int maxUrlArgCount)
In KMoreToolsMenuFactory some minor magic is done.
Definition: kmoretools.cpp:294
bool removeOne(const T &value)
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:616
void destroyed(QObject *obj)
QIcon kmtProvidedIcon() const
Definition: kmoretools.cpp:274
void setInitialItemText(const QString &itemText)
Sets the initial text of a menu item.
Definition: kmoretools.cpp:747
The item is placed in the main section (default)
Definition: kmoretools.h:192
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Jan 18 2021 22:43:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.