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

KDE's Doxygen guidelines are available online.