KNewStuff

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

KDE's Doxygen guidelines are available online.