File indexing completed on 2024-05-12 04:42:02

0001 /*
0002     SPDX-FileCopyrightText: 2015 Gregor Mi <codestruct@posteo.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #ifndef KMORETOOLS_H
0008 #define KMORETOOLS_H
0009 
0010 #include <QMenu>
0011 #include <QString>
0012 #include <QUrl>
0013 
0014 #include <KService>
0015 
0016 #include <memory>
0017 
0018 #include <kmoretools_export.h>
0019 
0020 class KMoreToolsService;
0021 class KMoreToolsMenuBuilder;
0022 class KMoreToolsPrivate;
0023 
0024 /**
0025  * Helps to create user-configurable menus with tools which are potentially not yet installed.
0026  *
0027  * This class is one entry point of the KMoreTools API.
0028  * See also KMoreToolsMenuFactory.
0029  *
0030  * @note This is a new API (published within KNewStuff since April 2015). Its current
0031  * target are KDE applications which are part of the kdesrcbuild infrastructure.
0032  * Here, it is possible to find all usages and to adapt to API changes when needed.
0033  * So, if you use this in your own application, beware that there might be API
0034  * changes when more use cases are developed.
0035  *
0036  *
0037  * Introduction
0038  * ------------
0039  * KMoreTools helps to build user-configurable menus with tools which
0040  * might not installed yet. These tools may also take URL arguments supplied
0041  * by the application.
0042  *
0043  * The user will see a menu item for a tool even if it is not installed (in the
0044  * 'More' section). Furthermore, it makes long menus shorter by providing a
0045  * main and more section.
0046  * It provides a 'Configure menu' dialog to make the menu user-configurable.
0047  *
0048  * It does this in the following ways:
0049  * - Provide an API to define external applications for a given context.
0050  * - If a defined application is not installed (yet) the application is (optionally)
0051  *    still presented to the user with a hint that it is not installed and a link
0052  *    to the homepage (later with integration to package management).
0053  *    This increases the discoverability of useful applications the user never
0054  *    heard about yet.
0055  * - In case of many applications for a given context, it provides a GUI to the
0056  *    user to hand-pick favorite tools.
0057  *    This makes it easier for application developers to add alternative
0058  *    application/tool suggestions without worrying about cluttered menus.
0059  * - Menu items can be (automatically) moved to the "More" submenu.
0060  * - Reduce translation effort by re-using .desktop files of the services added
0061  *    to the menu.
0062  *
0063  *
0064  * Details
0065  * -------
0066  * The term "kmt-desktopfile" refers to a 1:1 copy of a .desktop file. The
0067  * kmt-desktopfile is provided by the application that uses KMoreTools
0068  * and must be installed to subdirectories of /usr/share/kf5/kmoretools/
0069  * - e.g. /usr/share/kf5/kmoretools/dolphin/statusbar-diskspace-menu/
0070  * - e.g. /usr/share/kf5/kmoretools/kate/addons/project/git-tools/
0071  * - generally, 'QStandardPaths::GenericDataLocation'/kf5/kmoretools/'uniqueId'
0072  *
0073  * See KMoreTools::KMoreTools for hints of how to install this correctly
0074  * using cmake.
0075  *
0076  * The kmt-desktopfiles are used to get ready-made translations for application
0077  * name and description even if the application is not installed. You can
0078  * also provide an icon which is used in the not-installed section when the
0079  * application is not installed yet.
0080  *
0081  * For details about the resulting menu structure, see KMoreToolsMenuBuilder.
0082  *
0083  * See also https://community.kde.org/Scratchpad/KMoreToolsFramework (outdated)
0084  *
0085  *
0086  * Rationale for the "Not installed" section
0087  * -----------------------------------------
0088  * - Increase discoverability and visibility of useful free software that have
0089  *   inherently low budget for marketing.
0090  * - Make interconnection of different free software packages as effortless as
0091  *   possible (in terms of creating and maintaining the menu).
0092  * - Provide expert (i.e. your) knowledge to useful free software alternatives
0093  *   to solve a certain task.
0094  * - Give novice users hints about tools that are useful in a particular
0095  *   context even if they are not installed.
0096  * - Improve self-documentation of applications.
0097  *
0098  *
0099  * Presets
0100  * -------
0101  * Before installing desktop files in your application you might take a look
0102  * at KMoreToolsPresets or KMoreToolsMenuFactory which might already contain
0103  * the needed tools.
0104  *
0105  *
0106  * Screenshots
0107  * -----------
0108  * This section shows screenshots of usage examples.
0109  *
0110  * ### KSnapshot's Send To... menu
0111  *
0112  * Last updated: 2015-04-17, uncommitted demo, source code:
0113  * src/kde/kdegraphics/ksnapshot/ksnapshotsendtoactions.cpp
0114  *
0115  * Note, that the last item in the 'More' menu in the following screenshot was
0116  * added by KSnapshot's code.
0117  *
0118  * \image html kmoretools-ksnapshot-sendto-1.png "Send To menu" width=100px
0119  *
0120  * ### Dolphins's Space info menu
0121  *
0122  * Last updated: 2015-04-17, uncommitted demo, source code: src/kde/applications/dolphin/src/statusbar/spaceinfotoolsmenu.cpp
0123  *
0124  * \image html kmoretools-dolphin-spaceinfo-1.png "Space info menu" width=100px
0125  *
0126  * ### Kate's Project plugin git menu
0127  *
0128  * Last updated: 2015-03-25, uncommitted demo, source code:
0129  * src/kde/applications/kate/addons/project/kateprojecttreeviewcontextmenu.cpp
0130  *
0131  * \image html kmoretools-kate-project-1-all-installed.png "All git tools installed" width=100px
0132  *
0133  * \image html kmoretools-kate-project-2-two-not-installed.png "Not all git tools installed" width=100px
0134  *
0135  * \image html kmoretools-kate-project-3-config-dialog-all-installed.png "'Configure menu' dialog" width=100px
0136  *
0137  * ### Kate's Project plugin git menu
0138  *
0139  * Last updated: 2015-04-17, source code: src/frameworks/knewstuff/tests/kmoretools/kmoretoolstest.cpp
0140  *
0141  * \image html kmoretools-tests-configure-dialog-notinstalledapps.png "Configure dialog when there are non-installed apps" width=100px
0142  *
0143  *
0144  * FAQ
0145  * ---
0146  * ### Why is everything based on desktopfiles?
0147  *
0148  * - With desktopfiles translation can be reused.
0149  * - Definition of application icon can be reused.
0150  * - They provide a unified interface for dealing with program arguments.
0151  *
0152  *
0153  * todo later
0154  * ----------
0155  * - question: KMoreTools::registerServiceByDesktopEntryName():
0156  *      - warn if service is not of Type=Application (KService::isApplication()) or just leave it?
0157  * Add support for package managers to install software (e.g. muon discover)
0158  * - maybe: kmt-desktopfiles: add a config file that can configure the homepage URLs
0159  *   and e.g. the package name if needed for package manager support
0160  *
0161  */
0162 class KMORETOOLS_EXPORT KMoreTools
0163 {
0164     friend class KMoreToolsService;
0165     friend class KMoreToolsServicePrivate;
0166 
0167 public:
0168     /**
0169      * Specify how should be determined if a service is installed or not
0170      */
0171     enum ServiceLocatingMode {
0172         /**
0173          * by existence of desktop file (discoverable by KService)
0174          */
0175         ServiceLocatingMode_Default,
0176 
0177         /**
0178          * by existence of executable defined in the TryExec or Exec line of
0179          * the provided kmt-desktopfile
0180          */
0181         ServiceLocatingMode_ByProvidedExecLine,
0182     };
0183 
0184     /**
0185      * Specify where a menu item be placed by default
0186      */
0187     enum MenuSection {
0188         /**
0189          * The item is placed in the main section (default)
0190          */
0191         MenuSection_Main,
0192 
0193         /**
0194          * The item is placed in the "More" submenu.
0195          */
0196         MenuSection_More,
0197     };
0198 
0199     //     /* *
0200     //      * todo/later: introduce when needed
0201     //      */
0202     //     enum NotInstalledSectionOption
0203     //     {
0204     //         /* *
0205     //          * default
0206     //          */
0207     //         NotInstalledSection_Show,
0208     //
0209     //         /* *
0210     //          * Even if there are non-installed apps the Not-Installed section will
0211     //          * not be shown
0212     //          */
0213     //         NotInstalledSection_Hide
0214     //     };
0215 
0216     /**
0217      * Specify if the Configure dialog be accessible from the menu
0218      * (via a "Configure..." menu item)
0219      */
0220     enum ConfigureDialogAccessibleSetting {
0221         /**
0222          * Always show the "Configure..." menu item
0223          * (default)
0224          */
0225         ConfigureDialogAccessible_Always,
0226 
0227         /**
0228          * Defensively show the "Configure..." menu item
0229          *
0230          * The "Configure..." menu item will only be shown if there are non-installed
0231          * apps.
0232          * Rationale (suggestion): Do not clutter menu more than needed in standard
0233          *    cases. But when there are not-installed apps the configure dialog can
0234          *    be used to find out more about these apps.
0235          *
0236          * Note, that the "Configure..." menu item still becomes visible when the
0237          * user holds the Ctrl key while opening the menu.
0238          */
0239         ConfigureDialogAccessible_Defensive,
0240     };
0241 
0242 public:
0243     /**
0244      * @param uniqueId defines two things
0245      * 1) the config section name where the user settings done by the Configure
0246      *    dialog will be stored.
0247      * 2) the location where the kmt-desktopfiles should be installed because
0248      *    there they will be searched by default.
0249      *    If @p uniqueId contains slashes they will result in subdirectories.
0250      *    The default location can be overridden by
0251      *    registerServiceByDesktopEntryName's kmtDesktopfileSubdir parameter.
0252      *    This is currently used in KMoreToolsPresets implementation to
0253      *    separate the kmt-desktopfiles location from the user's config section
0254      *    name.
0255      *
0256      * Install Desktopfiles
0257      * --------------------
0258      * Example 1 (CMakeLists.txt if uniqueId = "dolphin/statusbar-diskspace-menu"):
0259      * \verbatim
0260         # note the trailing slash       ------------. (it makes sure only the contents of the directory is copied)
0261         #                                           |                                 ----fix---
0262         #                                           v                                            ------ uniqueId-----------------
0263         install(DIRECTORY statusbar/kmt-desktopfiles/ DESTINATION ${KDE_INSTALL_DATADIR_KF5}/kmoretools/dolphin/statusbar-diskspace-menu)
0264         \endverbatim
0265 
0266         Example 2:
0267         \verbatim
0268                                                                                        ------ uniqueId--------------
0269         install(DIRECTORY kmt-desktopfiles/ DESTINATION ${KDE_INSTALL_DATADIR_KF5}/kmoretools/kate/addons/project/git-tools)
0270         \endverbatim
0271      *
0272      * ### About ${KDE_INSTALL_DATADIR_KF5}
0273      *
0274      * In general, ${KDE_INSTALL_DATADIR_KF5}/kmoretools/hallo ends up in /usr/share/kf5/kmoretools/hallo.
0275      *
0276      * To use it, you need to add \verbatim include(KDEInstallDirs) \endverbatim to your CMakeLists.txt.
0277      */
0278     explicit KMoreTools(const QString &uniqueId);
0279 
0280     ~KMoreTools();
0281 
0282     KMoreTools(const KMoreTools &) = delete;
0283     KMoreTools &operator=(const KMoreTools &) = delete;
0284 
0285     /**
0286      * Registers a service with KMoreTools.
0287      *
0288      * If the method is called more than once for the same desktopEntryName
0289      * the service is located again and the old service is replaced with the
0290      * new one.
0291      *
0292      * @param desktopEntryName is the name of the desktopfile (without the
0293      * .desktop extension)
0294      * The desktop file is
0295      * 1. either already installed. Then the information of the installed file
0296      *    is used.
0297      * 2. or not installed and kmt-desktopfile is present. Then the information
0298      *     of the app-local copy of desktopfile located in the kmt-desktopfiles
0299      *     directory is used
0300      * 3. or not installed and no kmt-desktopfile provided. In this case
0301      *    KMoreToolsService::setHomepageUrl should be used so that at least a
0302      *    website link can be displayed.
0303      *
0304      * @param kmtDesktopfileSubdir when not empty overrides the @p uniqueId
0305      * parameter from the ctor when it comes to searching a kmt-desktopfile.
0306      * Default value is the empty string.
0307      *
0308      * @param serviceLocatingMode == ServiceLocatingMode_ByProvidedExecLine:
0309      *   Some programs don't install a desktop file of their own (e.g. gitk).
0310      *   If set to true then installed desktop files are not searched
0311      *   but the provided in kmt-desktopfiles will be used to extract exec line.
0312      *   The exec line will be used to determine if the executable is installed.
0313      *
0314      * @return a KMoreToolsService pointer which lives as long as KMoreTools, so
0315      *         do not store it for later use.
0316      * @return nullptr if the kmt provided desktop file is faulty.
0317      *         This kind of error must be fixed before you ship your application.
0318      *         This case is only used for unit tests.
0319      */
0320     KMoreToolsService *registerServiceByDesktopEntryName(const QString &desktopEntryName,
0321                                                          const QString &kmtDesktopfileSubdir = QString(),
0322                                                          ServiceLocatingMode serviceLocatingMode = ServiceLocatingMode_Default);
0323 
0324     /**
0325      * @returns the interface to build the menu. It is a singleton instance
0326      * for each different @p userConfigPostfix (which is "" by default).
0327      * So repeated calls with same parameter will return the same object.
0328      *
0329      * The pointer lives as long as KMoreTools.
0330      *
0331      * @param userConfigPostfix is empty by default. You can use it to specify
0332      * a postfix for the user config section. So you can build different menus
0333      * which can be configured separately. (This is used in unit tests to
0334      * separated test cases.)
0335      *
0336      * @sa KMoreToolsMenuBuilder::clear()
0337      */
0338     KMoreToolsMenuBuilder *menuBuilder(const QString &userConfigPostfix = QString()) const;
0339 
0340 private:
0341     const std::unique_ptr<KMoreToolsPrivate> d;
0342 };
0343 
0344 // --------------------------------------------------------------------------------------
0345 // --------------------------------------------------------------------------------------
0346 
0347 class KMoreToolsServicePrivate;
0348 
0349 /**
0350  * A service described in a .desktop file (kmt-desktopfile) which will be
0351  * called "registered service".
0352  *
0353  * A registered service can either be installed (isInstalled() == true)
0354  * or - if not found on the system - not installed (isInstalled() == false).
0355  */
0356 class KMORETOOLS_EXPORT KMoreToolsService
0357 {
0358     friend class KMoreTools;
0359     friend class KMoreToolsPrivate;
0360 
0361 public:
0362     ~KMoreToolsService();
0363 
0364     KMoreToolsService(const KMoreToolsService &) = delete;
0365     KMoreToolsService &operator=(const KMoreToolsService &) = delete;
0366 
0367     /**
0368      * @return the desktop entry name which the service is identified by and with which
0369      * it was registered (see registerServiceByDesktopEntryName).
0370      *
0371      * Filename without .desktop: e.g. if the desktop file is named
0372      * "org.kde.ksnapshot.desktop" then the desktop entry name is
0373      * "org.kde.ksnapshot".
0374      */
0375     QString desktopEntryName() const;
0376 
0377     /**
0378      * @returns true if the desktopfile with the given
0379      * desktopname (name of the .desktop file without the .desktop)
0380      * is installed on the system
0381      */
0382     bool isInstalled() const;
0383 
0384     /**
0385      * @returns the KService represented by an installed desktop file.
0386      *
0387      * @note that this might be nullptr even if isInstalled() is true.
0388      * This can only happen when ServiceLocatingMode::ServiceLocatingMode_ByProvidedExecLine
0389      * is used in registerServiceByDesktopEntryName. (Then the kmt-desktopfile's
0390      * Exec line is used to determine if a program is installed)
0391      */
0392     KService::Ptr installedService() const;
0393 
0394     /**
0395      * @returns a non-null KService::Ptr if app-local kmt-desktopfile is
0396      * found and valid
0397      */
0398     KService::Ptr kmtProvidedService() const;
0399 
0400     /**
0401      * @return the icon provided by the KMoreTools' user and not the installed one.
0402      * (e.g. QGit currently has got a blank icon installed)
0403      */
0404     QIcon kmtProvidedIcon() const;
0405 
0406     /**
0407      * @see setHomepageUrl()
0408      */
0409     QUrl homepageUrl() const;
0410 
0411     /**
0412      * Sets the homepage url the user is shown when a service is not installed.
0413      * This way the user gets some information of how to install the
0414      * application.
0415      */
0416     void setHomepageUrl(const QUrl &url);
0417 
0418     /**
0419      * @see setMaxUrlArgCount()
0420      */
0421     int maxUrlArgCount() const;
0422 
0423     /**
0424      * In KMoreToolsMenuFactory some minor magic is done. In the context of
0425      * connecting the action trigger signal we need to know the maximum number
0426      * of URL arguments a given service can accept. Usually a number between
0427      * 0 and 1. Sometimes 2.
0428      * E.g. kdf must not be called with any positional argument.
0429      * E.g. gitg can be called with zero or one arguments.
0430      */
0431     void setMaxUrlArgCount(int maxUrlArgCount);
0432 
0433     /**
0434      * @param formatString supports the following placeholders:
0435      *
0436      * 1. $GenericName
0437      * 2. $Name
0438      * 3. $DesktopEntryName
0439      *
0440      * which are replaced by the corresponding desktop file entries.
0441      *
0442      * If a value for a placeholder is not available (or empty)
0443      * (e.g. if no desktop file is available (not installed or not provided
0444      * via kmt-desktopfiles)) then the next one is used until 3. is reached which
0445      * is always available. Example: the formatString is "$GenericName", but
0446      * the GenericName field is not available. So $Name is used. If this is
0447      * also not available, $DesktopEntryName is used.
0448      *
0449      * @sa KMoreToolsMenuItem::setInitialItemText
0450      * @sa KMoreToolsMenuBuilder::setInitialItemTextTemplate
0451      */
0452     QString formatString(const QString &formatString) const;
0453 
0454     /**
0455      * 1. Icon from installed desktop file
0456      * If 1. is not found not found then...
0457      * 2. icon from kmt desktop file (which is then searched in the kmt-desktopfiles
0458      *    directory, must have extension .svg or .png)
0459      * If 2. is not not found then...
0460      * 3. no icon
0461      */
0462     QIcon icon() const;
0463 
0464     /**
0465      * Will override the "Exec=" line of the service. Will only apply if the
0466      * service is installed.
0467      *
0468      * @see KService::setExec(...)
0469      */
0470     void setExec(const QString &exec);
0471 
0472     /**
0473      * Returns the associated appstream id that was previously set with setAppstreamId().
0474      * If no appstream id was set, an empty string is returned.
0475      *
0476      * @return The service's appstream id.
0477      */
0478     QString appstreamId() const;
0479 
0480     /**
0481      * Sets the appstream id of the service. This is used to create a
0482      * appstream url for installing the service via a software store
0483      * (e.g. Discover). For instance, the appstream id for filelight is
0484      * "org.kde.filelight.desktop".
0485      *
0486      * @param id the appstream id
0487      */
0488     void setAppstreamId(const QString &);
0489 
0490 private:
0491     /**
0492      * @param kmtDesktopfileSubdir where to find kmt-desktopfiles
0493      * @param desktopEntryName name of the desktopfile without the .desktop extension
0494      * @param isInstalled true if desktop file is installed
0495      * @param installedService not nullptr if @p isInstalled is true
0496      * @param kmtDesktopfile not null if app-local kmt-desktopfile is found and valid
0497      */
0498     KMoreToolsService(const QString &kmtDesktopfileSubdir,
0499                       const QString &desktopEntryName,
0500                       bool isInstalled,
0501                       KService::Ptr installedService,
0502                       KService::Ptr kmtDesktopfile);
0503 
0504     /**
0505      * No copy semantic => private and no implementation
0506      */
0507     KMoreToolsService(const KMoreTools &);
0508 
0509     const std::unique_ptr<KMoreToolsServicePrivate> d;
0510 };
0511 
0512 // --------------------------------------------------------------------------------------
0513 // --------------------------------------------------------------------------------------
0514 
0515 class KMoreToolsMenuItem;
0516 class KMoreToolsMenuBuilderPrivate;
0517 
0518 /**
0519  * Define how the default structure of the menu should look like.
0520  *
0521  * Depending on if the added service is installed or not a "Not installed" section
0522  * will be automatically added to the generated menu.
0523  */
0524 class KMORETOOLS_EXPORT KMoreToolsMenuBuilder
0525 {
0526     friend class KMoreToolsPrivate;
0527     friend class KMoreTools;
0528     friend class KMoreToolsTest;
0529     friend class KMoreToolsTest2;
0530     friend class KMoreToolsTestInteractive;
0531 
0532 public:
0533     ~KMoreToolsMenuBuilder();
0534 
0535     KMoreToolsMenuBuilder(const KMoreToolsMenuBuilder &) = delete;
0536     KMoreToolsMenuBuilder &operator=(const KMoreToolsMenuBuilder &) = delete;
0537 
0538     /**
0539      * Affects addMenuItem() if called before it.
0540      *
0541      * see KMoreToolsService::formatString, see KMoreToolsMenuItem::setInitialItemText
0542      *
0543      * The default template text is "$GenericName".
0544      */
0545     void setInitialItemTextTemplate(const QString &templateText);
0546 
0547     /**
0548      * Adds a registered service (which can installed or not) to the menu.
0549      * If the service is not installed it will be shown in the "Not installed"
0550      * section.
0551      *
0552      * @param registeredService will be added to a the menu. A unique menu
0553      * itemId will be generated automatically from the desktopEntryName.
0554      * See also KMoreToolsMenuItem::id().
0555      *
0556      * @param defaultLocation is KMoreTools::MenuSection_Main by default.
0557      *
0558      * The registeredService->isInstalled() result will be respected. E.g. if the service
0559      * is not installed it will be placed in the "Not installed" section in the more
0560      * location of the menu even if @p defaultLocation was main location.
0561      *
0562      * See also KMoreToolsMenuItem ctor
0563      *
0564      * @sa KMoreToolsMenuItem::action()
0565      */
0566     KMoreToolsMenuItem *addMenuItem(KMoreToolsService *registeredService, KMoreTools::MenuSection defaultLocation = KMoreTools::MenuSection_Main);
0567 
0568     /**
0569      * Adds an action to the menu which is created and managed by the caller.
0570      *
0571      * @param action to be added to the menu.
0572      *
0573      * @param itemId is a unique (for this menu) id for the item. The itemId
0574      * _may_ be not unique. Then a unique id is generated automatically by
0575      * using some postfix. But it is better if you specify something sensible
0576      * because the itemId is used to find the items in the user config.
0577      * Otherwise the user config can be messed up if the order or number
0578      * of default menu items changes. NOTE, that the QAction::text is NOT
0579      * used to generate the unique id because the text is translated and
0580      * therefore not stable.
0581      *
0582      * @sa KMoreToolsMenuItem::action()
0583      */
0584     KMoreToolsMenuItem *addMenuItem(QAction *action, const QString &itemId, KMoreTools::MenuSection defaultLocation = KMoreTools::MenuSection_Main);
0585 
0586     /**
0587      * Clears all added menu items. This can be useful if the menuBuilder is reused more than once.
0588      *
0589      * @sa KMoreToolsService::menuBuilder
0590      */
0591     void clear();
0592 
0593     /**
0594      * Builds the actual menu and appends all items (main items,
0595      * more submenu with a potential "not installed" section) to the @p menu.
0596      *
0597      * @param menu the menu where the items should be appended to
0598      *
0599      * @param configureDialogAccessibleSetting determines when the
0600      * "Configure..." menu item should be added to the menu
0601      *
0602      * @param moreMenu if not nullptr then it will be set to the pointer to the
0603      * "More" menu in case it was created.
0604      * Otherwise the pointer will set to nullptr.
0605      * This can be used to add some custom items to the @p menu.
0606      */
0607     void buildByAppendingToMenu(QMenu *menu,
0608                                 KMoreTools::ConfigureDialogAccessibleSetting configureDialogAccessibleSetting = KMoreTools::ConfigureDialogAccessible_Always,
0609                                 QMenu **outMoreMenu = nullptr);
0610 
0611 private:
0612     /**
0613      * for unit testing / get as debug string
0614      */
0615     QString menuStructureAsString(bool mergeWithUserConfig) const;
0616 
0617     /**
0618      * for unit testing
0619      */
0620     void showConfigDialog(const QString &title);
0621 
0622     /**
0623      * (needed because QMap needs a default ctor)
0624      */
0625     KMoreToolsMenuBuilder();
0626 
0627     /**
0628      * internal usage
0629      */
0630     KMoreToolsMenuBuilder(const QString &uniqueId, const QString &userConfigPostfix);
0631 
0632     /**
0633      * No copy semantic => private and no implementation
0634      */
0635     KMoreToolsMenuBuilder(const KMoreTools &);
0636 
0637     const std::unique_ptr<KMoreToolsMenuBuilderPrivate> d;
0638 };
0639 
0640 // --------------------------------------------------------------------------------------
0641 // --------------------------------------------------------------------------------------
0642 
0643 class KMoreToolsMenuItemPrivate;
0644 
0645 /**
0646  * Represents a menu item of a service (application, tool or variant of the same
0647  * service with different parameters).
0648  *
0649  * The service might be installed or not.
0650  *
0651  * The corresponding QAction will be created for installed services.
0652  *
0653  * @note that for not-installed services action() returns nullptr.
0654  */
0655 class KMORETOOLS_EXPORT KMoreToolsMenuItem
0656 {
0657     friend class KMoreToolsMenuBuilderPrivate;
0658     friend class KMoreToolsMenuBuilder;
0659 
0660 public:
0661     KMoreToolsMenuItem(const KMoreToolsMenuItem &) = delete;
0662     KMoreToolsMenuItem &operator=(const KMoreToolsMenuItem &) = delete;
0663 
0664     /**
0665      * Auto-generated unique id that tries to be as stable as possible even if the
0666      * menu gets restructured after the user did some customization that was
0667      * persisted in a config file.
0668      *
0669      * @note It is possible to add the same service more than once (and then
0670      * hopefully change the action text). When the order of those are changed,
0671      * the id will not be consistent (because internally an increasing number is used)
0672      * If you have issues with this you can solve this by manually
0673      * calling setId (e.g. 'desktopEntryName' + 'x').
0674      */
0675     QString id() const;
0676 
0677     /**
0678      * (Optional) to help with stable ids (see id())
0679      *
0680      * todo: make sure that if this is called, uniqueness of ids will be assured.
0681      * todo: make sure to show error if the id contains characters other than
0682      *       alphanumerica, dashes and underscores etc.
0683      */
0684     void setId(const QString &id);
0685 
0686     /**
0687      * @return the underlying KMoreToolsService instance,
0688      * see KMoreToolsMenuBuilder::addMenuItem (with KKmoreToolsService* argument).
0689      * Or nullptr when KMoreToolsMenuBuilder::addMenuItem (with QAction* argument
0690      * was used).
0691      */
0692     KMoreToolsService *registeredService() const;
0693 
0694     /**
0695      * see KMoreToolsMenuBuilder::addMenuItem
0696      */
0697     KMoreTools::MenuSection defaultLocation() const;
0698 
0699     /**
0700      * see setInitialItemText()
0701      */
0702     QString initialItemText() const;
0703 
0704     /**
0705      * Sets the initial text of a menu item.
0706      *
0707      * Menu items of a non-installed service will get this text.
0708      * If the service is installed and you would like to change the item text,
0709      * you can retrieve the created QAction (action())
0710      * and modify the text using QAction's methods (QAction::setText()).
0711      *
0712      * @see
0713      * - initialItemText()
0714      * - action()
0715      * - You can use the static method KMoreToolsService::formatString here.
0716      */
0717     void setInitialItemText(const QString &itemText);
0718 
0719     /**
0720      * Case 1
0721      * ------
0722      * KMoreToolsMenuBuilder::addMenuItem was called with KKmoreToolsService* argument.
0723      *
0724      * the corresponding QAction which will be added to the actual menu when
0725      * underlying service is installed or else - if not installed - nullptr.
0726      *
0727      * So you can change the created action as you desire.
0728      *
0729      * We return nullptr because not-installed services will get a submenu with
0730      * other items like opening a website instead of an single action.
0731      *
0732      * To change the item's text even for not-installed services use initialItemText()
0733      *
0734      * Note, that once the method was invoked the first time the action is created
0735      * an then reused.
0736      *
0737      * Case 2
0738      * ------
0739      * KMoreToolsMenuBuilder::addMenuItem was called with QAction* argument.
0740      * The added action will be returned.
0741      *
0742      * @see KMoreToolsService::isInstalled
0743      */
0744     QAction *action() const;
0745 
0746 private: // internal usage
0747     /**
0748      * Sets the initial item text.
0749      */
0750     KMoreToolsMenuItem(KMoreToolsService *registeredService, KMoreTools::MenuSection defaultLocation, const QString &initialItemTextTemplate);
0751 
0752     KMoreToolsMenuItem(QAction *action, const QString &itemId, KMoreTools::MenuSection defaultLocation);
0753 
0754     ~KMoreToolsMenuItem();
0755 
0756 private:
0757     const std::unique_ptr<KMoreToolsMenuItemPrivate> d;
0758 };
0759 
0760 #endif // KMORETOOLS_H