File indexing completed on 2024-04-28 15:29:00

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 <KNS3/knewstuff_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  * @since 5.10
0162  */
0163 class KNEWSTUFF_EXPORT KMoreTools
0164 {
0165     friend class KMoreToolsService;
0166     friend class KMoreToolsServicePrivate;
0167 
0168 public:
0169     /**
0170      * Specify how should be determined if a service is installed or not
0171      */
0172     enum ServiceLocatingMode {
0173         /**
0174          * by existence of desktop file (discoverable by KService)
0175          */
0176         ServiceLocatingMode_Default,
0177 
0178         /**
0179          * by existence of executable defined in the TryExec or Exec line of
0180          * the provided kmt-desktopfile
0181          */
0182         ServiceLocatingMode_ByProvidedExecLine,
0183     };
0184 
0185     /**
0186      * Specify where a menu item be placed by default
0187      */
0188     enum MenuSection {
0189         /**
0190          * The item is placed in the main section (default)
0191          */
0192         MenuSection_Main,
0193 
0194         /**
0195          * The item is placed in the "More" submenu.
0196          */
0197         MenuSection_More,
0198     };
0199 
0200     //     /* *
0201     //      * todo/later: introduce when needed
0202     //      */
0203     //     enum NotInstalledSectionOption
0204     //     {
0205     //         /* *
0206     //          * default
0207     //          */
0208     //         NotInstalledSection_Show,
0209     //
0210     //         /* *
0211     //          * Even if there are non-installed apps the Not-Installed section will
0212     //          * not be shown
0213     //          */
0214     //         NotInstalledSection_Hide
0215     //     };
0216 
0217     /**
0218      * Specify if the Configure dialog be accessible from the menu
0219      * (via a "Configure..." menu item)
0220      */
0221     enum ConfigureDialogAccessibleSetting {
0222         /**
0223          * Always show the "Configure..." menu item
0224          * (default)
0225          */
0226         ConfigureDialogAccessible_Always,
0227 
0228         /**
0229          * Defensively show the "Configure..." menu item
0230          *
0231          * The "Configure..." menu item will only be shown if there are non-installed
0232          * apps.
0233          * Rationale (suggestion): Do not clutter menu more than needed in standard
0234          *    cases. But when there are not-installed apps the configure dialog can
0235          *    be used to find out more about these apps.
0236          *
0237          * Note, that the "Configure..." menu item still becomes visible when the
0238          * user holds the Ctrl key while opening the menu.
0239          */
0240         ConfigureDialogAccessible_Defensive,
0241     };
0242 
0243 public:
0244     /**
0245      * @param uniqueId defines two things
0246      * 1) the config section name where the user settings done by the Configure
0247      *    dialog will be stored.
0248      * 2) the location where the kmt-desktopfiles should be installed because
0249      *    there they will be searched by default.
0250      *    If @p uniqueId contains slashes they will result in subdirectories.
0251      *    The default location can be overridden by
0252      *    registerServiceByDesktopEntryName's kmtDesktopfileSubdir parameter.
0253      *    This is currently used in KMoreToolsPresets implementation to
0254      *    separate the kmt-desktopfiles location from the user's config section
0255      *    name.
0256      *
0257      * Install Desktopfiles
0258      * --------------------
0259      * Example 1 (CMakeLists.txt if uniqueId = "dolphin/statusbar-diskspace-menu"):
0260      * \verbatim
0261         # note the trailing slash       ------------. (it makes sure only the contents of the directory is copied)
0262         #                                           |                                 ----fix---
0263         #                                           v                                            ------ uniqueId-----------------
0264         install(DIRECTORY statusbar/kmt-desktopfiles/ DESTINATION ${KDE_INSTALL_DATADIR_KF5}/kmoretools/dolphin/statusbar-diskspace-menu)
0265         \endverbatim
0266 
0267         Example 2:
0268         \verbatim
0269                                                                                        ------ uniqueId--------------
0270         install(DIRECTORY kmt-desktopfiles/ DESTINATION ${KDE_INSTALL_DATADIR_KF5}/kmoretools/kate/addons/project/git-tools)
0271         \endverbatim
0272      *
0273      * ### About ${KDE_INSTALL_DATADIR_KF5}
0274      *
0275      * In general, ${KDE_INSTALL_DATADIR_KF5}/kmoretools/hallo ends up in /usr/share/kf5/kmoretools/hallo.
0276      *
0277      * To use it, you need to add \verbatim include(KDEInstallDirs) \endverbatim to your CMakeLists.txt.
0278      */
0279     explicit KMoreTools(const QString &uniqueId);
0280 
0281     ~KMoreTools();
0282 
0283     KMoreTools(const KMoreTools &) = delete;
0284     KMoreTools &operator=(const KMoreTools &) = delete;
0285 
0286     /**
0287      * Registers a service with KMoreTools.
0288      *
0289      * If the method is called more than once for the same desktopEntryName
0290      * the service is located again and the old service is replaced with the
0291      * new one.
0292      *
0293      * @param desktopEntryName is the name of the desktopfile (without the
0294      * .desktop extension)
0295      * The desktop file is
0296      * 1. either already installed. Then the information of the installed file
0297      *    is used.
0298      * 2. or not installed and kmt-desktopfile is present. Then the information
0299      *     of the app-local copy of desktopfile located in the kmt-desktopfiles
0300      *     directory is used
0301      * 3. or not installed and no kmt-desktopfile provided. In this case
0302      *    KMoreToolsService::setHomepageUrl should be used so that at least a
0303      *    website link can be displayed.
0304      *
0305      * @param kmtDesktopfileSubdir when not empty overrides the @p uniqueId
0306      * parameter from the ctor when it comes to searching a kmt-desktopfile.
0307      * Default value is the empty string.
0308      *
0309      * @param serviceLocatingMode == ServiceLocatingMode_ByProvidedExecLine:
0310      *   Some programs don't install a desktop file of their own (e.g. gitk).
0311      *   If set to true then installed desktop files are not searched
0312      *   but the provided in kmt-desktopfiles will be used to extract exec line.
0313      *   The exec line will be used to determine if the executable is installed.
0314      *
0315      * @return a KMoreToolsService pointer which lives as long as KMoreTools, so
0316      *         do not store it for later use.
0317      * @return nullptr if the kmt provided desktop file is faulty.
0318      *         This kind of error must be fixed before you ship your application.
0319      *         This case is only used for unit tests.
0320      */
0321     KMoreToolsService *registerServiceByDesktopEntryName(const QString &desktopEntryName,
0322                                                          const QString &kmtDesktopfileSubdir = QString(),
0323                                                          ServiceLocatingMode serviceLocatingMode = ServiceLocatingMode_Default);
0324 
0325     /**
0326      * @returns the interface to build the menu. It is a singleton instance
0327      * for each different @p userConfigPostfix (which is "" by default).
0328      * So repeated calls with same parameter will return the same object.
0329      *
0330      * The pointer lives as long as KMoreTools.
0331      *
0332      * @param userConfigPostfix is empty by default. You can use it to specify
0333      * a postfix for the user config section. So you can build different menus
0334      * which can be configured separately. (This is used in unit tests to
0335      * separated test cases.)
0336      *
0337      * @sa KMoreToolsMenuBuilder::clear()
0338      */
0339     KMoreToolsMenuBuilder *menuBuilder(const QString &userConfigPostfix = QString()) const;
0340 
0341 private:
0342     const std::unique_ptr<KMoreToolsPrivate> d;
0343 };
0344 
0345 // --------------------------------------------------------------------------------------
0346 // --------------------------------------------------------------------------------------
0347 
0348 class KMoreToolsServicePrivate;
0349 
0350 /**
0351  * A service described in a .desktop file (kmt-desktopfile) which will be
0352  * called "registered service".
0353  *
0354  * A registered service can either be installed (isInstalled() == true)
0355  * or - if not found on the system - not installed (isInstalled() == false).
0356  *
0357  * @since 5.10
0358  */
0359 class KNEWSTUFF_EXPORT KMoreToolsService
0360 {
0361     friend class KMoreTools;
0362     friend class KMoreToolsPrivate;
0363 
0364 public:
0365     ~KMoreToolsService();
0366 
0367     KMoreToolsService(const KMoreToolsService &) = delete;
0368     KMoreToolsService &operator=(const KMoreToolsService &) = delete;
0369 
0370     /**
0371      * @return the desktop entry name which the service is identified by and with which
0372      * it was registered (see registerServiceByDesktopEntryName).
0373      *
0374      * Filename without .desktop: e.g. if the desktop file is named
0375      * "org.kde.ksnapshot.desktop" then the desktop entry name is
0376      * "org.kde.ksnapshot".
0377      */
0378     QString desktopEntryName() const;
0379 
0380     /**
0381      * @returns true if the desktopfile with the given
0382      * desktopname (name of the .desktop file without the .desktop)
0383      * is installed on the system
0384      */
0385     bool isInstalled() const;
0386 
0387     /**
0388      * @returns the KService represented by an installed desktop file.
0389      *
0390      * @note that this might be nullptr even if isInstalled() is true.
0391      * This can only happen when ServiceLocatingMode::ServiceLocatingMode_ByProvidedExecLine
0392      * is used in registerServiceByDesktopEntryName. (Then the kmt-desktopfile's
0393      * Exec line is used to determine if a program is installed)
0394      */
0395     KService::Ptr installedService() const;
0396 
0397     /**
0398      * @returns a non-null KService::Ptr if app-local kmt-desktopfile is
0399      * found and valid
0400      */
0401     KService::Ptr kmtProvidedService() const;
0402 
0403     /**
0404      * @return the icon provided by the KMoreTools' user and not the installed one.
0405      * (e.g. QGit currently has got a blank icon installed)
0406      */
0407     QIcon kmtProvidedIcon() const;
0408 
0409     /**
0410      * @see setHomepageUrl()
0411      */
0412     QUrl homepageUrl() const;
0413 
0414     /**
0415      * Sets the homepage url the user is shown when a service is not installed.
0416      * This way the user gets some information of how to install the
0417      * application.
0418      */
0419     void setHomepageUrl(const QUrl &url);
0420 
0421     /**
0422      * @see setMaxUrlArgCount()
0423      */
0424     int maxUrlArgCount() const;
0425 
0426     /**
0427      * In KMoreToolsMenuFactory some minor magic is done. In the context of
0428      * connecting the action trigger signal we need to know the maximum number
0429      * of URL arguments a given service can accept. Usually a number between
0430      * 0 and 1. Sometimes 2.
0431      * E.g. kdf must not be called with any positional argument.
0432      * E.g. gitg can be called with zero or one arguments.
0433      */
0434     void setMaxUrlArgCount(int maxUrlArgCount);
0435 
0436     /**
0437      * @param formatString supports the following placeholders:
0438      *
0439      * 1. $GenericName
0440      * 2. $Name
0441      * 3. $DesktopEntryName
0442      *
0443      * which are replaced by the corresponding desktop file entries.
0444      *
0445      * If a value for a placeholder is not available (or empty)
0446      * (e.g. if no desktop file is available (not installed or not provided
0447      * via kmt-desktopfiles)) then the next one is used until 3. is reached which
0448      * is always available. Example: the formatString is "$GenericName", but
0449      * the GenericName field is not available. So $Name is used. If this is
0450      * also not available, $DesktopEntryName is used.
0451      *
0452      * @sa KMoreToolsMenuItem::setInitialItemText
0453      * @sa KMoreToolsMenuBuilder::setInitialItemTextTemplate
0454      */
0455     QString formatString(const QString &formatString) const;
0456 
0457     /**
0458      * 1. Icon from installed desktop file
0459      * If 1. is not found not found then...
0460      * 2. icon from kmt desktop file (which is then searched in the kmt-desktopfiles
0461      *    directory, must have extension .svg or .png)
0462      * If 2. is not not found then...
0463      * 3. no icon
0464      */
0465     QIcon icon() const;
0466 
0467     /**
0468      * Will override the "Exec=" line of the service. Will only apply if the
0469      * service is installed.
0470      *
0471      * @see KService::setExec(...)
0472      */
0473     void setExec(const QString &exec);
0474 
0475     /**
0476      * Returns the associated appstream id that was previously set with setAppstreamId().
0477      * If no appstream id was set, an empty string is returned.
0478      *
0479      * @return The service's appstream id.
0480      *
0481      * @since 5.48
0482      */
0483     QString appstreamId() const;
0484 
0485     /**
0486      * Sets the appstream id of the service. This is used to create a
0487      * appstream url for installing the service via a software store
0488      * (e.g. Discover). For instance, the appstream id for filelight is
0489      * "org.kde.filelight.desktop".
0490      *
0491      * @param id the appstream id
0492      *
0493      * @since 5.48
0494      */
0495     void setAppstreamId(const QString &);
0496 
0497 private:
0498     /**
0499      * @param kmtDesktopfileSubdir where to find kmt-desktopfiles
0500      * @param desktopEntryName name of the desktopfile without the .desktop extension
0501      * @param isInstalled true if desktop file is installed
0502      * @param installedService not nullptr if @p isInstalled is true
0503      * @param kmtDesktopfile not null if app-local kmt-desktopfile is found and valid
0504      */
0505     KMoreToolsService(const QString &kmtDesktopfileSubdir,
0506                       const QString &desktopEntryName,
0507                       bool isInstalled,
0508                       KService::Ptr installedService,
0509                       KService::Ptr kmtDesktopfile);
0510 
0511     /**
0512      * No copy semantic => private and no implementation
0513      */
0514     KMoreToolsService(const KMoreTools &);
0515 
0516     const std::unique_ptr<KMoreToolsServicePrivate> d;
0517 };
0518 
0519 // --------------------------------------------------------------------------------------
0520 // --------------------------------------------------------------------------------------
0521 
0522 class KMoreToolsMenuItem;
0523 class KMoreToolsMenuBuilderPrivate;
0524 
0525 /**
0526  * Define how the default structure of the menu should look like.
0527  *
0528  * Depending on if the added service is installed or not a "Not installed" section
0529  * will be automatically added to the generated menu.
0530  *
0531  * @since 5.10
0532  */
0533 class KNEWSTUFF_EXPORT KMoreToolsMenuBuilder
0534 {
0535     friend class KMoreToolsPrivate;
0536     friend class KMoreTools;
0537     friend class KMoreToolsTest;
0538     friend class KMoreToolsTest2;
0539     friend class KMoreToolsTestInteractive;
0540 
0541 public:
0542     ~KMoreToolsMenuBuilder();
0543 
0544     KMoreToolsMenuBuilder(const KMoreToolsMenuBuilder &) = delete;
0545     KMoreToolsMenuBuilder &operator=(const KMoreToolsMenuBuilder &) = delete;
0546 
0547     /**
0548      * Affects addMenuItem() if called before it.
0549      *
0550      * see KMoreToolsService::formatString, see KMoreToolsMenuItem::setInitialItemText
0551      *
0552      * The default template text is "$GenericName".
0553      */
0554     void setInitialItemTextTemplate(const QString &templateText);
0555 
0556     /**
0557      * Adds a registered service (which can installed or not) to the menu.
0558      * If the service is not installed it will be shown in the "Not installed"
0559      * section.
0560      *
0561      * @param registeredService will be added to a the menu. A unique menu
0562      * itemId will be generated automatically from the desktopEntryName.
0563      * See also KMoreToolsMenuItem::id().
0564      *
0565      * @param defaultLocation is KMoreTools::MenuSection_Main by default.
0566      *
0567      * The registeredService->isInstalled() result will be respected. E.g. if the service
0568      * is not installed it will be placed in the "Not installed" section in the more
0569      * location of the menu even if @p defaultLocation was main location.
0570      *
0571      * See also KMoreToolsMenuItem ctor
0572      *
0573      * @sa KMoreToolsMenuItem::action()
0574      */
0575     KMoreToolsMenuItem *addMenuItem(KMoreToolsService *registeredService, KMoreTools::MenuSection defaultLocation = KMoreTools::MenuSection_Main);
0576 
0577     /**
0578      * Adds an action to the menu which is created and managed by the caller.
0579      *
0580      * @param action to be added to the menu.
0581      *
0582      * @param itemId is a unique (for this menu) id for the item. The itemId
0583      * _may_ be not unique. Then a unique id is generated automatically by
0584      * using some postfix. But it is better if you specify something sensible
0585      * because the itemId is used to find the items in the user config.
0586      * Otherwise the user config can be messed up if the order or number
0587      * of default menu items changes. NOTE, that the QAction::text is NOT
0588      * used to generate the unique id because the text is translated and
0589      * therefore not stable.
0590      *
0591      * @sa KMoreToolsMenuItem::action()
0592      */
0593     KMoreToolsMenuItem *addMenuItem(QAction *action, const QString &itemId, KMoreTools::MenuSection defaultLocation = KMoreTools::MenuSection_Main);
0594 
0595     /**
0596      * Clears all added menu items. This can be useful if the menuBuilder is reused more than once.
0597      *
0598      * @sa KMoreToolsService::menuBuilder
0599      */
0600     void clear();
0601 
0602     /**
0603      * Builds the actual menu and appends all items (main items,
0604      * more submenu with a potential "not installed" section) to the @p menu.
0605      *
0606      * @param menu the menu where the items should be appended to
0607      *
0608      * @param configureDialogAccessibleSetting determines when the
0609      * "Configure..." menu item should be added to the menu
0610      *
0611      * @param moreMenu if not nullptr then it will be set to the pointer to the
0612      * "More" menu in case it was created.
0613      * Otherwise the pointer will set to nullptr.
0614      * This can be used to add some custom items to the @p menu.
0615      */
0616     void buildByAppendingToMenu(QMenu *menu,
0617                                 KMoreTools::ConfigureDialogAccessibleSetting configureDialogAccessibleSetting = KMoreTools::ConfigureDialogAccessible_Always,
0618                                 QMenu **outMoreMenu = nullptr);
0619 
0620 private:
0621     /**
0622      * for unit testing / get as debug string
0623      */
0624     QString menuStructureAsString(bool mergeWithUserConfig) const;
0625 
0626     /**
0627      * for unit testing
0628      */
0629     void showConfigDialog(const QString &title);
0630 
0631     /**
0632      * (needed because QMap needs a default ctor)
0633      */
0634     KMoreToolsMenuBuilder();
0635 
0636     /**
0637      * internal usage
0638      */
0639     KMoreToolsMenuBuilder(const QString &uniqueId, const QString &userConfigPostfix);
0640 
0641     /**
0642      * No copy semantic => private and no implementation
0643      */
0644     KMoreToolsMenuBuilder(const KMoreTools &);
0645 
0646     const std::unique_ptr<KMoreToolsMenuBuilderPrivate> d;
0647 };
0648 
0649 // --------------------------------------------------------------------------------------
0650 // --------------------------------------------------------------------------------------
0651 
0652 class KMoreToolsMenuItemPrivate;
0653 
0654 /**
0655  * Represents a menu item of a service (application, tool or variant of the same
0656  * service with different parameters).
0657  *
0658  * The service might be installed or not.
0659  *
0660  * The corresponding QAction will be created for installed services.
0661  *
0662  * @note that for not-installed services action() returns nullptr.
0663  *
0664  * @since 5.10
0665  */
0666 class KNEWSTUFF_EXPORT KMoreToolsMenuItem
0667 {
0668     friend class KMoreToolsMenuBuilderPrivate;
0669     friend class KMoreToolsMenuBuilder;
0670 
0671 public:
0672     KMoreToolsMenuItem(const KMoreToolsMenuItem &) = delete;
0673     KMoreToolsMenuItem &operator=(const KMoreToolsMenuItem &) = delete;
0674 
0675     /**
0676      * Auto-generated unique id that tries to be as stable as possible even if the
0677      * menu gets restructured after the user did some customization that was
0678      * persisted in a config file.
0679      *
0680      * @note It is possible to add the same service more than once (and then
0681      * hopefully change the action text). When the order of those are changed,
0682      * the id will not be consistent (because internally an increasing number is used)
0683      * If you have issues with this you can solve this by manually
0684      * calling setId (e.g. 'desktopEntryName' + 'x').
0685      */
0686     QString id() const;
0687 
0688     /**
0689      * (Optional) to help with stable ids (see id())
0690      *
0691      * todo: make sure that if this is called, uniqueness of ids will be assured.
0692      * todo: make sure to show error if the id contains characters other than
0693      *       alphanumerica, dashes and underscores etc.
0694      */
0695     void setId(const QString &id);
0696 
0697     /**
0698      * @return the underlying KMoreToolsService instance,
0699      * see KMoreToolsMenuBuilder::addMenuItem (with KKmoreToolsService* argument).
0700      * Or nullptr when KMoreToolsMenuBuilder::addMenuItem (with QAction* argument
0701      * was used).
0702      */
0703     KMoreToolsService *registeredService() const;
0704 
0705     /**
0706      * see KMoreToolsMenuBuilder::addMenuItem
0707      */
0708     KMoreTools::MenuSection defaultLocation() const;
0709 
0710     /**
0711      * see setInitialItemText()
0712      */
0713     QString initialItemText() const;
0714 
0715     /**
0716      * Sets the initial text of a menu item.
0717      *
0718      * Menu items of a non-installed service will get this text.
0719      * If the service is installed and you would like to change the item text,
0720      * you can retrieve the created QAction (action())
0721      * and modify the text using QAction's methods (QAction::setText()).
0722      *
0723      * @see
0724      * - initialItemText()
0725      * - action()
0726      * - You can use the static method KMoreToolsService::formatString here.
0727      */
0728     void setInitialItemText(const QString &itemText);
0729 
0730     /**
0731      * Case 1
0732      * ------
0733      * KMoreToolsMenuBuilder::addMenuItem was called with KKmoreToolsService* argument.
0734      *
0735      * the corresponding QAction which will be added to the actual menu when
0736      * underlying service is installed or else - if not installed - nullptr.
0737      *
0738      * So you can change the created action as you desire.
0739      *
0740      * We return nullptr because not-installed services will get a submenu with
0741      * other items like opening a website instead of an single action.
0742      *
0743      * To change the item's text even for not-installed services use initialItemText()
0744      *
0745      * Note, that once the method was invoked the first time the action is created
0746      * an then reused.
0747      *
0748      * Case 2
0749      * ------
0750      * KMoreToolsMenuBuilder::addMenuItem was called with QAction* argument.
0751      * The added action will be returned.
0752      *
0753      * @see KMoreToolsService::isInstalled
0754      */
0755     QAction *action() const;
0756 
0757 private: // internal usage
0758     /**
0759      * Sets the initial item text.
0760      */
0761     KMoreToolsMenuItem(KMoreToolsService *registeredService, KMoreTools::MenuSection defaultLocation, const QString &initialItemTextTemplate);
0762 
0763     KMoreToolsMenuItem(QAction *action, const QString &itemId, KMoreTools::MenuSection defaultLocation);
0764 
0765     ~KMoreToolsMenuItem();
0766 
0767 private:
0768     const std::unique_ptr<KMoreToolsMenuItemPrivate> d;
0769 };
0770 
0771 #endif // KMORETOOLS_H