KMoreTools

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

KDE's Doxygen guidelines are available online.