File indexing completed on 2024-12-08 06:41:33

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #ifndef VFOLDER_MENU_H
0009 #define VFOLDER_MENU_H
0010 
0011 #include <QDomDocument>
0012 #include <QHash>
0013 #include <QObject>
0014 #include <QSet>
0015 #include <QStack>
0016 #include <QStringList>
0017 
0018 #include <kservice.h>
0019 
0020 class KBuildSycocaInterface;
0021 class KServiceFactory;
0022 
0023 class VFolderMenu : public QObject
0024 {
0025     Q_OBJECT
0026 public:
0027     class AppsInfo;
0028     class SubMenu
0029     {
0030     public:
0031         SubMenu()
0032             : isDeleted(false)
0033             , apps_info(nullptr)
0034         {
0035             items.reserve(43);
0036         }
0037         ~SubMenu()
0038         {
0039             qDeleteAll(subMenus);
0040         }
0041         SubMenu(const SubMenu &) = delete;
0042         SubMenu &operator=(const SubMenu &) = delete;
0043 
0044     public:
0045         QString name;
0046         QString directoryFile;
0047         QList<SubMenu *> subMenus;
0048         QHash<QString, KService::Ptr> items;
0049         QHash<QString, KService::Ptr> excludeItems; // Needed when merging due to Move.
0050         QDomElement defaultLayoutNode;
0051         QDomElement layoutNode;
0052         bool isDeleted;
0053         QStringList layoutList;
0054         AppsInfo *apps_info;
0055     };
0056 
0057     VFolderMenu(KServiceFactory *serviceFactory, KBuildSycocaInterface *kbuildsycocaInterface);
0058     ~VFolderMenu() override;
0059 
0060     /**
0061      * Parses VFolder menu definition and generates a menu layout.
0062      * The newService signals is used as callback to load
0063      * a specific service description.
0064      *
0065      * @param file Menu file to load
0066      */
0067     SubMenu *parseMenu(const QString &file);
0068 
0069     /**
0070      * Returns a list of all directories involved in the last call to
0071      * parseMenu().
0072      *
0073      * A change in any of these directories or in any of their child-
0074      * directories can result in changes to the menu.
0075      */
0076     QStringList allDirectories();
0077 
0078     /**
0079      * Debug function to enable tracking of what happens with a specific
0080      * menu item id
0081      */
0082     void setTrackId(const QString &id);
0083 
0084 public:
0085     struct MenuItem {
0086         enum Type { MI_Service, MI_SubMenu, MI_Separator };
0087         Type type;
0088         KService::Ptr service;
0089         SubMenu *submenu;
0090     };
0091 
0092 public:
0093     QStringList m_allDirectories; // A list of all the directories that we touch
0094 
0095     QStringList m_defaultAppDirs;
0096     QStringList m_defaultDirectoryDirs;
0097     QStringList m_defaultMergeDirs;
0098 
0099     QStringList m_directoryDirs; // Current set of applicable <DirectoryDir> dirs
0100     QHash<QString, SubMenu *> m_legacyNodes; // Dictionary that stores Menu nodes
0101     // associated with legacy tree.
0102 
0103     class DocInfo
0104     {
0105     public:
0106         QString baseDir; // Relative base dir of current menu file
0107         QString baseName; // Filename of current menu file without ".menu"
0108         QString path; // Full path of current menu file including ".menu"
0109     };
0110 
0111     DocInfo m_docInfo; // DocInfo for current doc
0112     QStack<VFolderMenu::DocInfo> m_docInfoStack;
0113 
0114     class AppsInfo
0115     {
0116     public:
0117         AppsInfo()
0118         {
0119             dictCategories.reserve(53);
0120             applications.reserve(997);
0121             appRelPaths.reserve(997);
0122         }
0123 
0124         ~AppsInfo()
0125         {
0126         }
0127 
0128         QHash<QString, KService::List> dictCategories; // category -> apps
0129         QHash<QString, KService::Ptr> applications; // rel path -> service
0130         QHash<KService::Ptr, QString> appRelPaths; // service -> rel path
0131     };
0132 
0133     AppsInfo *m_appsInfo; // AppsInfo for current menu
0134     QList<AppsInfo *> m_appsInfoStack; // All applicable AppsInfo for current menu
0135     QList<AppsInfo *> m_appsInfoList; // List of all AppsInfo objects.
0136     QSet<QString /*menuId*/> m_usedAppsDict; // all applications that have been allocated
0137 
0138     QDomDocument m_doc;
0139     SubMenu *m_rootMenu;
0140     SubMenu *m_currentMenu;
0141     bool m_track;
0142     QString m_trackId;
0143 
0144 private:
0145     /**
0146      * Lookup application by relative path
0147      */
0148     KService::Ptr findApplication(const QString &relPath);
0149 
0150     /**
0151      * Lookup applications by category
0152      */
0153     QList<KService::List *> findCategory(const QString &category);
0154 
0155     /**
0156      * Add new application
0157      */
0158     void addApplication(const QString &id, KService::Ptr service);
0159 
0160     /**
0161      * Build application indices
0162      */
0163     void buildApplicationIndex(bool unusedOnly);
0164 
0165     /**
0166      * Create a AppsInfo frame for current menu
0167      */
0168     void createAppsInfo();
0169 
0170     /**
0171      * Load additional AppsInfo frame for current menu
0172      */
0173     void loadAppsInfo();
0174 
0175     /**
0176      * Unload additional AppsInfo frame for current menu
0177      */
0178     void unloadAppsInfo();
0179 
0180     QDomDocument loadDoc();
0181     void mergeMenus(QDomElement &docElem, QString &name);
0182     void mergeFile(QDomElement &docElem, const QDomNode &mergeHere);
0183     void loadMenu(const QString &filename);
0184 
0185     /**
0186      * Merge the items2 set into the items1 set
0187      */
0188     void includeItems(QHash<QString, KService::Ptr> &items1, const QHash<QString, KService::Ptr> &items2);
0189 
0190     /**
0191      * Remove all items from the items1 set that aren't also in the items2 set
0192      */
0193     void matchItems(QHash<QString, KService::Ptr> &items1, const QHash<QString, KService::Ptr> &items2);
0194 
0195     /**
0196      * Remove all items in the items2 set from the items1 set
0197      */
0198     void excludeItems(QHash<QString, KService::Ptr> &items1, const QHash<QString, KService::Ptr> &items2);
0199 
0200     /**
0201      * Search the parentMenu tree for the menu menuName and takes it
0202      * out.
0203      *
0204      * This function returns a pointer to the menu if it was found
0205      * or @c nullptr if it was not found.
0206      */
0207     SubMenu *takeSubMenu(SubMenu *parentMenu, const QString &menuName);
0208 
0209     /**
0210      * Insert the menu newMenu with name menuName into the parentMenu.
0211      * If such menu already exist the result is merged, if any additional
0212      * submenus are required they are created.
0213      * If reversePriority is false, newMenu has priority over the existing
0214      * menu during merging.
0215      * If reversePriority is true, the existing menu has priority over newMenu
0216      * during merging.
0217      */
0218     void insertSubMenu(VFolderMenu::SubMenu *parentMenu, const QString &menuName, VFolderMenu::SubMenu *newMenu, bool reversePriority = false);
0219 
0220     /**
0221      * Merge menu2 and its submenus into menu1 and its submenus
0222      * If reversePriority is false, menu2 has priority over menu1
0223      * If reversePriority is true, menu1 has priority over menu2
0224      */
0225     void mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority = false);
0226 
0227     /**
0228      * Inserts service into the menu using name relative to parentMenu
0229      * Any missing sub-menus are created.
0230      */
0231     void insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService);
0232 
0233     /**
0234      * Register the directory that @p file is in.
0235      * @see allDirectories()
0236      */
0237     void registerFile(const QString &file);
0238 
0239     /**
0240      * Fill m_usedAppsDict with all applications from @p items
0241      */
0242     void markUsedApplications(const QHash<QString, KService::Ptr> &items);
0243 
0244     /**
0245      * Register @p directory
0246      * @see allDirectories()
0247      */
0248     void registerDirectory(const QString &directory);
0249 
0250     void processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix);
0251     void processMenu(QDomElement &docElem, int pass);
0252     void layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout);
0253     void processCondition(QDomElement &docElem, QHash<QString, KService::Ptr> &items);
0254 
0255     void initDirs();
0256 
0257     void pushDocInfo(const QString &fileName, const QString &baseDir = QString());
0258     void pushDocInfoParent(const QString &basePath, const QString &baseDir);
0259     void popDocInfo();
0260 
0261     QString absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg = false);
0262     QString locateMenuFile(const QString &fileName);
0263     QString locateDirectoryFile(const QString &fileName);
0264     void loadApplications(const QString &, const QString &);
0265     QStringList parseLayoutNode(const QDomElement &docElem) const;
0266 
0267 private:
0268     KServiceFactory *m_serviceFactory;
0269     KBuildSycocaInterface *m_kbuildsycocaInterface;
0270 };
0271 
0272 #endif