1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#ifndef VFOLDER_MENU_H
9#define VFOLDER_MENU_H
10
11#include <QDomDocument>
12#include <QHash>
13#include <QObject>
14#include <QSet>
15#include <QStack>
16#include <QStringList>
17
18#include <kservice.h>
19
20class KBuildSycocaInterface;
21class KServiceFactory;
22
23class VFolderMenu : public QObject
24{
25 Q_OBJECT
26public:
27 class AppsInfo;
28 class SubMenu
29 {
30 public:
31 SubMenu()
32 : isDeleted(false)
33 , apps_info(nullptr)
34 {
35 items.reserve(size: 43);
36 }
37 ~SubMenu()
38 {
39 qDeleteAll(c: subMenus);
40 }
41 SubMenu(const SubMenu &) = delete;
42 SubMenu &operator=(const SubMenu &) = delete;
43
44 public:
45 QString name;
46 QString directoryFile;
47 QList<SubMenu *> subMenus;
48 QHash<QString, KService::Ptr> items;
49 QHash<QString, KService::Ptr> excludeItems; // Needed when merging due to Move.
50 QDomElement defaultLayoutNode;
51 QDomElement layoutNode;
52 bool isDeleted;
53 QStringList layoutList;
54 AppsInfo *apps_info;
55 };
56
57 VFolderMenu(KServiceFactory *serviceFactory, KBuildSycocaInterface *kbuildsycocaInterface);
58 ~VFolderMenu() override;
59
60 /**
61 * Parses VFolder menu definition and generates a menu layout.
62 * The newService signals is used as callback to load
63 * a specific service description.
64 *
65 * @param file Menu file to load
66 */
67 SubMenu *parseMenu(const QString &file);
68
69 /**
70 * Returns a list of all directories involved in the last call to
71 * parseMenu().
72 *
73 * A change in any of these directories or in any of their child-
74 * directories can result in changes to the menu.
75 */
76 QStringList allDirectories();
77
78 /**
79 * Debug function to enable tracking of what happens with a specific
80 * menu item id
81 */
82 void setTrackId(const QString &id);
83
84public:
85 struct MenuItem {
86 enum Type { MI_Service, MI_SubMenu, MI_Separator };
87 Type type;
88 KService::Ptr service;
89 SubMenu *submenu;
90 };
91
92public:
93 QStringList m_allDirectories; // A list of all the directories that we touch
94
95 QStringList m_defaultAppDirs;
96 QStringList m_defaultDirectoryDirs;
97 QStringList m_defaultMergeDirs;
98
99 QStringList m_directoryDirs; // Current set of applicable <DirectoryDir> dirs
100 QHash<QString, SubMenu *> m_legacyNodes; // Dictionary that stores Menu nodes
101 // associated with legacy tree.
102
103 class DocInfo
104 {
105 public:
106 QString baseDir; // Relative base dir of current menu file
107 QString baseName; // Filename of current menu file without ".menu"
108 QString path; // Full path of current menu file including ".menu"
109 };
110
111 DocInfo m_docInfo; // DocInfo for current doc
112 QStack<VFolderMenu::DocInfo> m_docInfoStack;
113
114 class AppsInfo
115 {
116 public:
117 AppsInfo()
118 {
119 dictCategories.reserve(size: 53);
120 applications.reserve(size: 997);
121 appRelPaths.reserve(size: 997);
122 }
123
124 ~AppsInfo()
125 {
126 }
127
128 QHash<QString, KService::List> dictCategories; // category -> apps
129 QHash<QString, KService::Ptr> applications; // rel path -> service
130 QHash<KService::Ptr, QString> appRelPaths; // service -> rel path
131 };
132
133 AppsInfo *m_appsInfo; // AppsInfo for current menu
134 QList<AppsInfo *> m_appsInfoStack; // All applicable AppsInfo for current menu
135 QList<AppsInfo *> m_appsInfoList; // List of all AppsInfo objects.
136 QSet<QString /*menuId*/> m_usedAppsDict; // all applications that have been allocated
137
138 QDomDocument m_doc;
139 SubMenu *m_rootMenu;
140 SubMenu *m_currentMenu;
141 bool m_track;
142 QString m_trackId;
143
144private:
145 /**
146 * Lookup application by relative path
147 */
148 KService::Ptr findApplication(const QString &relPath);
149
150 /**
151 * Lookup applications by category
152 */
153 QList<KService::List *> findCategory(const QString &category);
154
155 /**
156 * Add new application
157 */
158 void addApplication(const QString &id, KService::Ptr service);
159
160 /**
161 * Build application indices
162 */
163 void buildApplicationIndex(bool unusedOnly);
164
165 /**
166 * Create a AppsInfo frame for current menu
167 */
168 void createAppsInfo();
169
170 /**
171 * Load additional AppsInfo frame for current menu
172 */
173 void loadAppsInfo();
174
175 /**
176 * Unload additional AppsInfo frame for current menu
177 */
178 void unloadAppsInfo();
179
180 QDomDocument loadDoc();
181 void mergeMenus(QDomElement &docElem, QString &name);
182 void mergeFile(QDomElement &docElem, const QDomNode &mergeHere);
183 void loadMenu(const QString &filename);
184
185 /**
186 * Merge the items2 set into the items1 set
187 */
188 void includeItems(QHash<QString, KService::Ptr> &items1, const QHash<QString, KService::Ptr> &items2);
189
190 /**
191 * Remove all items from the items1 set that aren't also in the items2 set
192 */
193 void matchItems(QHash<QString, KService::Ptr> &items1, const QHash<QString, KService::Ptr> &items2);
194
195 /**
196 * Remove all items in the items2 set from the items1 set
197 */
198 void excludeItems(QHash<QString, KService::Ptr> &items1, const QHash<QString, KService::Ptr> &items2);
199
200 /**
201 * Search the parentMenu tree for the menu menuName and takes it
202 * out.
203 *
204 * This function returns a pointer to the menu if it was found
205 * or @c nullptr if it was not found.
206 */
207 SubMenu *takeSubMenu(SubMenu *parentMenu, const QString &menuName);
208
209 /**
210 * Insert the menu newMenu with name menuName into the parentMenu.
211 * If such menu already exist the result is merged, if any additional
212 * submenus are required they are created.
213 * If reversePriority is false, newMenu has priority over the existing
214 * menu during merging.
215 * If reversePriority is true, the existing menu has priority over newMenu
216 * during merging.
217 */
218 void insertSubMenu(VFolderMenu::SubMenu *parentMenu, const QString &menuName, VFolderMenu::SubMenu *newMenu, bool reversePriority = false);
219
220 /**
221 * Merge menu2 and its submenus into menu1 and its submenus
222 * If reversePriority is false, menu2 has priority over menu1
223 * If reversePriority is true, menu1 has priority over menu2
224 */
225 void mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority = false);
226
227 /**
228 * Inserts service into the menu using name relative to parentMenu
229 * Any missing sub-menus are created.
230 */
231 void insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService);
232
233 /**
234 * Register the directory that @p file is in.
235 * @see allDirectories()
236 */
237 void registerFile(const QString &file);
238
239 /**
240 * Fill m_usedAppsDict with all applications from @p items
241 */
242 void markUsedApplications(const QHash<QString, KService::Ptr> &items);
243
244 /**
245 * Register @p directory
246 * @see allDirectories()
247 */
248 void registerDirectory(const QString &directory);
249
250 void processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix);
251 void processMenu(QDomElement &docElem, int pass);
252 void layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout);
253 void processCondition(QDomElement &docElem, QHash<QString, KService::Ptr> &items);
254
255 void initDirs();
256
257 void pushDocInfo(const QString &fileName, const QString &baseDir = QString());
258 void pushDocInfoParent(const QString &basePath, const QString &baseDir);
259 void popDocInfo();
260
261 QString absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg = false);
262 QString locateMenuFile(const QString &fileName);
263 QString locateDirectoryFile(const QString &fileName);
264 void loadApplications(const QString &, const QString &);
265 QStringList parseLayoutNode(const QDomElement &docElem) const;
266
267private:
268 KServiceFactory *m_serviceFactory;
269 KBuildSycocaInterface *m_kbuildsycocaInterface;
270};
271
272#endif
273

source code of kservice/src/sycoca/vfolder_menu_p.h