1 | /* This file is part of the KDE libraries |
2 | * Copyright (C) 2003 Waldo Bastian <bastian@kde.org> |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License version 2 as published by the Free Software Foundation; |
7 | * |
8 | * This library is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
11 | * Library General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU Library General Public License |
14 | * along with this library; see the file COPYING.LIB. If not, write to |
15 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
16 | * Boston, MA 02110-1301, USA. |
17 | **/ |
18 | |
19 | #include "vfolder_menu.h" |
20 | #include "kbuildservicefactory.h" |
21 | #include "kbuildsycocainterface.h" |
22 | |
23 | #include <sys/types.h> |
24 | #include <sys/stat.h> |
25 | #include <unistd.h> |
26 | #include <dirent.h> |
27 | #include <config.h> |
28 | |
29 | #include <kdebug.h> |
30 | #include <kglobal.h> |
31 | #include <kstandarddirs.h> |
32 | #include <kservice.h> |
33 | #include <kde_file.h> |
34 | |
35 | #include <QtCore/QMap> |
36 | #include <QtCore/QFile> |
37 | #include <QtCore/QDir> |
38 | #include <QtCore/QRegExp> |
39 | #include <QtCore/QDirIterator> |
40 | |
41 | static void foldNode(QDomElement &docElem, QDomElement &e, QMap<QString,QDomElement> &dupeList, QString s=QString()) //krazy:exclude=passbyvalue |
42 | { |
43 | if (s.isEmpty()) |
44 | s = e.text(); |
45 | QMap<QString,QDomElement>::iterator it = dupeList.find(s); |
46 | if (it != dupeList.end()) |
47 | { |
48 | kDebug(7021) << e.tagName() << "and" << s << "requires combining!" ; |
49 | |
50 | docElem.removeChild(*it); |
51 | dupeList.erase(it); |
52 | } |
53 | dupeList.insert(s, e); |
54 | } |
55 | |
56 | static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag) |
57 | { |
58 | for(QStringList::ConstIterator it = list.begin(); |
59 | it != list.end(); ++it) |
60 | { |
61 | QDomElement e = docElem.ownerDocument().createElement(tag); |
62 | QDomText txt = docElem.ownerDocument().createTextNode(*it); |
63 | e.appendChild(txt); |
64 | docElem.insertAfter(e, n); |
65 | } |
66 | |
67 | QDomNode next = n.nextSibling(); |
68 | docElem.removeChild(n); |
69 | n = next; |
70 | // kDebug(7021) << "Next tag = " << n.toElement().tagName(); |
71 | } |
72 | |
73 | void VFolderMenu::(const QString &file) |
74 | { |
75 | int i = file.lastIndexOf('/'); |
76 | if (i < 0) |
77 | return; |
78 | |
79 | QString dir = file.left(i+1); // Include trailing '/' |
80 | registerDirectory(dir); |
81 | } |
82 | |
83 | void VFolderMenu::(const QString &directory) |
84 | { |
85 | m_allDirectories.append(directory); |
86 | } |
87 | |
88 | QStringList VFolderMenu::() |
89 | { |
90 | if (m_allDirectories.isEmpty()) |
91 | return m_allDirectories; |
92 | m_allDirectories.sort(); |
93 | |
94 | QStringList::Iterator it = m_allDirectories.begin(); |
95 | QString previous = *it++; |
96 | for(;it != m_allDirectories.end();) |
97 | { |
98 | #ifndef Q_OS_WIN |
99 | if ((*it).startsWith(previous)) |
100 | #else |
101 | if ((*it).startsWith(previous, Qt::CaseInsensitive)) |
102 | #endif |
103 | { |
104 | it = m_allDirectories.erase(it); |
105 | } |
106 | else |
107 | { |
108 | previous = *it; |
109 | ++it; |
110 | } |
111 | } |
112 | return m_allDirectories; |
113 | } |
114 | |
115 | static void |
116 | track(const QString &, const QString &, const QHash<QString,KService::Ptr>& includeList, const QHash<QString,KService::Ptr>& excludeList, const QHash<QString,KService::Ptr>& itemList, const QString &) |
117 | { |
118 | if (itemList.contains(menuId)) |
119 | printf("%s: %s INCL %d EXCL %d\n" , qPrintable(menuName), qPrintable(comment), includeList.contains(menuId) ? 1 : 0, excludeList.contains(menuId) ? 1 : 0); |
120 | } |
121 | |
122 | void |
123 | VFolderMenu::(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2) |
124 | { |
125 | foreach (const KService::Ptr &p, items2) { |
126 | items1.insert(p->menuId(), p); |
127 | } |
128 | } |
129 | |
130 | void |
131 | VFolderMenu::(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2) |
132 | { |
133 | foreach (const KService::Ptr &p, items1) |
134 | { |
135 | QString id = p->menuId(); |
136 | if (!items2.contains(id)) |
137 | items1.remove(id); |
138 | } |
139 | } |
140 | |
141 | void |
142 | VFolderMenu::(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2) |
143 | { |
144 | foreach (const KService::Ptr &p, items2) |
145 | items1.remove(p->menuId()); |
146 | } |
147 | |
148 | VFolderMenu::SubMenu* |
149 | VFolderMenu::(SubMenu *, const QString &) |
150 | { |
151 | const int i = menuName.indexOf('/'); |
152 | const QString s1 = i > 0 ? menuName.left(i) : menuName; |
153 | const QString s2 = menuName.mid(i+1); |
154 | |
155 | // Look up menu |
156 | for (QList<SubMenu*>::Iterator it = parentMenu->subMenus.begin(); it != parentMenu->subMenus.end(); ++it) |
157 | { |
158 | SubMenu* = *it; |
159 | if (menu->name == s1) |
160 | { |
161 | if (i == -1) |
162 | { |
163 | // Take it out |
164 | parentMenu->subMenus.erase(it); |
165 | return menu; |
166 | } |
167 | else |
168 | { |
169 | return takeSubMenu(menu, s2); |
170 | } |
171 | } |
172 | } |
173 | return 0; // Not found |
174 | } |
175 | |
176 | void |
177 | VFolderMenu::(SubMenu *, SubMenu *, bool reversePriority) |
178 | { |
179 | if (m_track) |
180 | { |
181 | track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("Before MenuMerge w. %1 (incl)" ).arg(menu2->name)); |
182 | track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("Before MenuMerge w. %1 (excl)" ).arg(menu2->name)); |
183 | } |
184 | if (reversePriority) |
185 | { |
186 | // Merge menu1 with menu2, menu1 takes precedent |
187 | excludeItems(menu2->items, menu1->excludeItems); |
188 | includeItems(menu1->items, menu2->items); |
189 | excludeItems(menu2->excludeItems, menu1->items); |
190 | includeItems(menu1->excludeItems, menu2->excludeItems); |
191 | } |
192 | else |
193 | { |
194 | // Merge menu1 with menu2, menu2 takes precedent |
195 | excludeItems(menu1->items, menu2->excludeItems); |
196 | includeItems(menu1->items, menu2->items); |
197 | includeItems(menu1->excludeItems, menu2->excludeItems); |
198 | menu1->isDeleted = menu2->isDeleted; |
199 | } |
200 | while (!menu2->subMenus.isEmpty()) |
201 | { |
202 | SubMenu * = menu2->subMenus.takeFirst(); |
203 | insertSubMenu(menu1, subMenu->name, subMenu, reversePriority); |
204 | } |
205 | |
206 | if (reversePriority) |
207 | { |
208 | // Merge menu1 with menu2, menu1 takes precedent |
209 | if (menu1->directoryFile.isEmpty()) |
210 | menu1->directoryFile = menu2->directoryFile; |
211 | if (menu1->defaultLayoutNode.isNull()) |
212 | menu1->defaultLayoutNode = menu2->defaultLayoutNode; |
213 | if (menu1->layoutNode.isNull()) |
214 | menu1->layoutNode = menu2->layoutNode; |
215 | } |
216 | else |
217 | { |
218 | // Merge menu1 with menu2, menu2 takes precedent |
219 | if (!menu2->directoryFile.isEmpty()) |
220 | menu1->directoryFile = menu2->directoryFile; |
221 | if (!menu2->defaultLayoutNode.isNull()) |
222 | menu1->defaultLayoutNode = menu2->defaultLayoutNode; |
223 | if (!menu2->layoutNode.isNull()) |
224 | menu1->layoutNode = menu2->layoutNode; |
225 | } |
226 | |
227 | if (m_track) |
228 | { |
229 | track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("After MenuMerge w. %1 (incl)" ).arg(menu2->name)); |
230 | track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("After MenuMerge w. %1 (excl)" ).arg(menu2->name)); |
231 | } |
232 | |
233 | delete menu2; |
234 | } |
235 | |
236 | void |
237 | VFolderMenu::(SubMenu *, const QString &, SubMenu *, bool reversePriority) |
238 | { |
239 | const int i = menuName.indexOf('/'); |
240 | const QString s1 = menuName.left(i); |
241 | const QString s2 = menuName.mid(i+1); |
242 | |
243 | // Look up menu |
244 | foreach (SubMenu *, parentMenu->subMenus) |
245 | { |
246 | if (menu->name == s1) |
247 | { |
248 | if (i == -1) |
249 | { |
250 | mergeMenu(menu, newMenu, reversePriority); |
251 | return; |
252 | } |
253 | else |
254 | { |
255 | insertSubMenu(menu, s2, newMenu, reversePriority); |
256 | return; |
257 | } |
258 | } |
259 | } |
260 | if (i == -1) |
261 | { |
262 | // Add it here |
263 | newMenu->name = menuName; |
264 | parentMenu->subMenus.append(newMenu); |
265 | } |
266 | else |
267 | { |
268 | SubMenu * = new SubMenu; |
269 | menu->name = s1; |
270 | parentMenu->subMenus.append(menu); |
271 | insertSubMenu(menu, s2, newMenu); |
272 | } |
273 | } |
274 | |
275 | void |
276 | VFolderMenu::(SubMenu *, const QString &name, KService::Ptr newService) |
277 | { |
278 | const int i = name.indexOf('/'); |
279 | |
280 | if (i == -1) |
281 | { |
282 | // Add it here |
283 | parentMenu->items.insert(newService->menuId(), newService); |
284 | return; |
285 | } |
286 | |
287 | QString s1 = name.left(i); |
288 | QString s2 = name.mid(i+1); |
289 | |
290 | // Look up menu |
291 | foreach (SubMenu *, parentMenu->subMenus) |
292 | { |
293 | if (menu->name == s1) |
294 | { |
295 | insertService(menu, s2, newService); |
296 | return; |
297 | } |
298 | } |
299 | |
300 | SubMenu * = new SubMenu; |
301 | menu->name = s1; |
302 | parentMenu->subMenus.append(menu); |
303 | insertService(menu, s2, newService); |
304 | } |
305 | |
306 | |
307 | VFolderMenu::(KBuildServiceFactory* serviceFactory, KBuildSycocaInterface* kbuildsycocaInterface) |
308 | : m_track(false), |
309 | m_serviceFactory(serviceFactory), |
310 | m_kbuildsycocaInterface(kbuildsycocaInterface) |
311 | { |
312 | m_usedAppsDict.reserve(797); |
313 | m_rootMenu = 0; |
314 | initDirs(); |
315 | } |
316 | |
317 | VFolderMenu::() |
318 | { |
319 | delete m_rootMenu; |
320 | delete m_appsInfo; |
321 | } |
322 | |
323 | #define FOR_ALL_APPLICATIONS(it) \ |
324 | foreach (AppsInfo *info, m_appsInfoStack) \ |
325 | { \ |
326 | QHashIterator<QString,KService::Ptr> it = info->applications; \ |
327 | while (it.hasNext()) \ |
328 | { \ |
329 | it.next(); |
330 | #define FOR_ALL_APPLICATIONS_END } } |
331 | |
332 | #define FOR_CATEGORY(category, it) \ |
333 | foreach (AppsInfo *info, m_appsInfoStack) \ |
334 | { \ |
335 | const KService::List list = info->dictCategories.value(category); \ |
336 | for(KService::List::ConstIterator it = list.constBegin(); \ |
337 | it != list.constEnd(); ++it) \ |
338 | { |
339 | #define FOR_CATEGORY_END } } |
340 | |
341 | KService::Ptr |
342 | VFolderMenu::(const QString &relPath) |
343 | { |
344 | foreach(AppsInfo *info, m_appsInfoStack) |
345 | { |
346 | if (info->applications.contains(relPath)) { |
347 | KService::Ptr s = info->applications[relPath]; |
348 | if (s) |
349 | return s; |
350 | } |
351 | } |
352 | return KService::Ptr(); |
353 | } |
354 | |
355 | void |
356 | VFolderMenu::(const QString &id, KService::Ptr service) |
357 | { |
358 | service->setMenuId(id); |
359 | m_appsInfo->applications.insert(id, service); // replaces, if already there |
360 | m_serviceFactory->addEntry(KSycocaEntry::Ptr::staticCast(service)); |
361 | } |
362 | |
363 | void |
364 | VFolderMenu::(bool unusedOnly) |
365 | { |
366 | foreach (AppsInfo *info, m_appsInfoList) |
367 | { |
368 | info->dictCategories.clear(); |
369 | QMutableHashIterator<QString,KService::Ptr> it = info->applications; |
370 | while (it.hasNext()) |
371 | { |
372 | KService::Ptr s = it.next().value(); |
373 | if (unusedOnly && m_usedAppsDict.contains(s->menuId())) |
374 | { |
375 | // Remove and skip this one |
376 | it.remove(); |
377 | continue; |
378 | } |
379 | |
380 | Q_FOREACH(const QString& cat, s->categories()) { |
381 | info->dictCategories[cat].append(s); // find or insert entry in hash |
382 | } |
383 | } |
384 | } |
385 | } |
386 | |
387 | void |
388 | VFolderMenu::() |
389 | { |
390 | if (m_appsInfo) return; |
391 | |
392 | m_appsInfo = new AppsInfo; |
393 | m_appsInfoStack.prepend(m_appsInfo); |
394 | m_appsInfoList.append(m_appsInfo); |
395 | m_currentMenu->apps_info = m_appsInfo; |
396 | } |
397 | |
398 | void |
399 | VFolderMenu::() |
400 | { |
401 | m_appsInfo = m_currentMenu->apps_info; |
402 | if (!m_appsInfo) |
403 | return; // No appsInfo for this menu |
404 | |
405 | if (m_appsInfoStack.count() && m_appsInfoStack.first() == m_appsInfo) |
406 | return; // Already added (By createAppsInfo?) |
407 | |
408 | m_appsInfoStack.prepend(m_appsInfo); // Add |
409 | } |
410 | |
411 | void |
412 | VFolderMenu::() |
413 | { |
414 | m_appsInfo = m_currentMenu->apps_info; |
415 | if (!m_appsInfo) |
416 | return; // No appsInfo for this menu |
417 | |
418 | if (m_appsInfoStack.first() != m_appsInfo) |
419 | { |
420 | return; // Already removed (huh?) |
421 | } |
422 | |
423 | m_appsInfoStack.removeAll(m_appsInfo); // Remove |
424 | m_appsInfo = 0; |
425 | } |
426 | |
427 | QString |
428 | VFolderMenu::(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg) |
429 | { |
430 | QString dir = _dir; |
431 | if (QDir::isRelativePath(dir)) |
432 | { |
433 | dir = baseDir + dir; |
434 | } |
435 | if (!dir.endsWith('/')) |
436 | dir += '/'; |
437 | |
438 | bool relative = QDir::isRelativePath(dir); |
439 | if (relative && !keepRelativeToCfg) { |
440 | relative = false; |
441 | dir = KGlobal::dirs()->findResource("xdgconf-menu" , dir); |
442 | } |
443 | |
444 | if (!relative) |
445 | dir = KGlobal::dirs()->realPath(dir); |
446 | |
447 | return dir; |
448 | } |
449 | |
450 | static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir) |
451 | { |
452 | QDomNodeList mergeFileList = doc.elementsByTagName(tag); |
453 | for(int i = 0; i < (int)mergeFileList.count(); i++) |
454 | { |
455 | QDomAttr attr = doc.createAttribute("__BaseDir" ); |
456 | attr.setValue(dir); |
457 | mergeFileList.item(i).toElement().setAttributeNode(attr); |
458 | } |
459 | } |
460 | |
461 | static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path) |
462 | { |
463 | QDomNodeList mergeFileList = doc.elementsByTagName(tag); |
464 | for(int i = 0; i < (int)mergeFileList.count(); i++) |
465 | { |
466 | QDomAttr attr = doc.createAttribute("__BasePath" ); |
467 | attr.setValue(path); |
468 | mergeFileList.item(i).toElement().setAttributeNode(attr); |
469 | } |
470 | } |
471 | |
472 | QDomDocument |
473 | VFolderMenu::() |
474 | { |
475 | QDomDocument doc; |
476 | if ( m_docInfo.path.isEmpty() ) |
477 | { |
478 | return doc; |
479 | } |
480 | QFile file( m_docInfo.path ); |
481 | if ( !file.open( QIODevice::ReadOnly ) ) |
482 | { |
483 | kWarning(7021) << "Could not open " << m_docInfo.path; |
484 | return doc; |
485 | } |
486 | QString errorMsg; |
487 | int errorRow; |
488 | int errorCol; |
489 | if ( !doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) { |
490 | kWarning(7021) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg; |
491 | file.close(); |
492 | return doc; |
493 | } |
494 | file.close(); |
495 | |
496 | tagBaseDir(doc, "MergeFile" , m_docInfo.baseDir); |
497 | tagBasePath(doc, "MergeFile" , m_docInfo.path); |
498 | tagBaseDir(doc, "MergeDir" , m_docInfo.baseDir); |
499 | tagBaseDir(doc, "DirectoryDir" , m_docInfo.baseDir); |
500 | tagBaseDir(doc, "AppDir" , m_docInfo.baseDir); |
501 | tagBaseDir(doc, "LegacyDir" , m_docInfo.baseDir); |
502 | |
503 | return doc; |
504 | } |
505 | |
506 | |
507 | void |
508 | VFolderMenu::(QDomElement &parent, const QDomNode &mergeHere) |
509 | { |
510 | kDebug(7021) << "VFolderMenu::mergeFile:" << m_docInfo.path; |
511 | QDomDocument doc = loadDoc(); |
512 | |
513 | QDomElement docElem = doc.documentElement(); |
514 | QDomNode n = docElem.firstChild(); |
515 | QDomNode last = mergeHere; |
516 | while( !n.isNull() ) |
517 | { |
518 | QDomElement e = n.toElement(); // try to convert the node to an element. |
519 | QDomNode next = n.nextSibling(); |
520 | |
521 | if (e.isNull()) |
522 | { |
523 | // Skip |
524 | } |
525 | // The spec says we must ignore any Name nodes |
526 | else if (e.tagName() != "Name" ) |
527 | { |
528 | parent.insertAfter(n, last); |
529 | last = n; |
530 | } |
531 | |
532 | docElem.removeChild(n); |
533 | n = next; |
534 | } |
535 | } |
536 | |
537 | |
538 | void |
539 | VFolderMenu::(QDomElement &docElem, QString &name) |
540 | { |
541 | QMap<QString,QDomElement> ; |
542 | QMap<QString,QDomElement> directoryNodes; |
543 | QMap<QString,QDomElement> appDirNodes; |
544 | QMap<QString,QDomElement> directoryDirNodes; |
545 | QMap<QString,QDomElement> legacyDirNodes; |
546 | QDomElement defaultLayoutNode; |
547 | QDomElement layoutNode; |
548 | |
549 | QDomNode n = docElem.firstChild(); |
550 | while( !n.isNull() ) { |
551 | QDomElement e = n.toElement(); // try to convert the node to an element. |
552 | if( e.isNull() ) { |
553 | // kDebug(7021) << "Empty node"; |
554 | } |
555 | else if( e.tagName() == "DefaultAppDirs" ) { |
556 | // Replace with m_defaultAppDirs |
557 | replaceNode(docElem, n, m_defaultAppDirs, "AppDir" ); |
558 | continue; |
559 | } |
560 | else if( e.tagName() == "DefaultDirectoryDirs" ) { |
561 | // Replace with m_defaultDirectoryDirs |
562 | replaceNode(docElem, n, m_defaultDirectoryDirs, "DirectoryDir" ); |
563 | continue; |
564 | } |
565 | else if( e.tagName() == "DefaultMergeDirs" ) { |
566 | // Replace with m_defaultMergeDirs |
567 | replaceNode(docElem, n, m_defaultMergeDirs, "MergeDir" ); |
568 | continue; |
569 | } |
570 | else if( e.tagName() == "AppDir" ) { |
571 | // Filter out dupes |
572 | foldNode(docElem, e, appDirNodes); |
573 | } |
574 | else if( e.tagName() == "DirectoryDir" ) { |
575 | // Filter out dupes |
576 | foldNode(docElem, e, directoryDirNodes); |
577 | } |
578 | else if( e.tagName() == "LegacyDir" ) { |
579 | // Filter out dupes |
580 | foldNode(docElem, e, legacyDirNodes); |
581 | } |
582 | else if( e.tagName() == "Directory" ) { |
583 | // Filter out dupes |
584 | foldNode(docElem, e, directoryNodes); |
585 | } |
586 | else if( e.tagName() == "Move" ) { |
587 | // Filter out dupes |
588 | QString orig; |
589 | QDomNode n2 = e.firstChild(); |
590 | while( !n2.isNull() ) { |
591 | QDomElement e2 = n2.toElement(); // try to convert the node to an element. |
592 | if( e2.tagName() == "Old" ) |
593 | { |
594 | orig = e2.text(); |
595 | break; |
596 | } |
597 | n2 = n2.nextSibling(); |
598 | } |
599 | foldNode(docElem, e, appDirNodes, orig); |
600 | } |
601 | else if( e.tagName() == "Menu" ) { |
602 | QString name; |
603 | mergeMenus(e, name); |
604 | QMap<QString,QDomElement>::iterator it = menuNodes.find(name); |
605 | if (it != menuNodes.end()) |
606 | { |
607 | QDomElement docElem2 = *it; |
608 | QDomNode n2 = docElem2.firstChild(); |
609 | QDomNode first = e.firstChild(); |
610 | while( !n2.isNull() ) { |
611 | QDomElement e2 = n2.toElement(); // try to convert the node to an element. |
612 | QDomNode n3 = n2.nextSibling(); |
613 | e.insertBefore(n2, first); |
614 | docElem2.removeChild(n2); |
615 | n2 = n3; |
616 | } |
617 | // We still have duplicated Name entries |
618 | // but we don't care about that |
619 | |
620 | docElem.removeChild(docElem2); |
621 | menuNodes.erase(it); |
622 | } |
623 | menuNodes.insert(name, e); |
624 | } |
625 | else if( e.tagName() == "MergeFile" ) { |
626 | if ((e.attribute("type" ) == "parent" )) |
627 | pushDocInfoParent(e.attribute("__BasePath" ), e.attribute("__BaseDir" )); |
628 | else |
629 | pushDocInfo(e.text(), e.attribute("__BaseDir" )); |
630 | |
631 | if (!m_docInfo.path.isEmpty()) |
632 | mergeFile(docElem, n); |
633 | popDocInfo(); |
634 | |
635 | QDomNode last = n; |
636 | n = n.nextSibling(); |
637 | docElem.removeChild(last); // Remove the MergeFile node |
638 | continue; |
639 | } |
640 | else if( e.tagName() == "MergeDir" ) { |
641 | QString dir = absoluteDir(e.text(), e.attribute("__BaseDir" ), true); |
642 | |
643 | const QStringList dirs = KGlobal::dirs()->findDirs("xdgconf-menu" , dir); |
644 | for(QStringList::ConstIterator it=dirs.begin(); |
645 | it != dirs.end(); ++it) |
646 | { |
647 | registerDirectory(*it); |
648 | } |
649 | |
650 | QStringList fileList; |
651 | if (!QDir::isRelativePath(dir)) |
652 | { |
653 | // Absolute |
654 | fileList = KGlobal::dirs()->findAllResources("xdgconf-menu" , dir+"*.menu" ); |
655 | } |
656 | else |
657 | { |
658 | // Relative |
659 | (void) KGlobal::dirs()->findAllResources("xdgconf-menu" , dir+"*.menu" , |
660 | KStandardDirs::NoDuplicates, fileList); |
661 | } |
662 | |
663 | for(QStringList::ConstIterator it=fileList.constBegin(); |
664 | it != fileList.constEnd(); ++it) |
665 | { |
666 | pushDocInfo(*it); |
667 | mergeFile(docElem, n); |
668 | popDocInfo(); |
669 | } |
670 | |
671 | QDomNode last = n; |
672 | n = n.nextSibling(); |
673 | docElem.removeChild(last); // Remove the MergeDir node |
674 | |
675 | continue; |
676 | } |
677 | else if( e.tagName() == "Name" ) { |
678 | name = e.text(); |
679 | } |
680 | else if( e.tagName() == "DefaultLayout" ) { |
681 | if (!defaultLayoutNode.isNull()) |
682 | docElem.removeChild(defaultLayoutNode); |
683 | defaultLayoutNode = e; |
684 | } |
685 | else if( e.tagName() == "Layout" ) { |
686 | if (!layoutNode.isNull()) |
687 | docElem.removeChild(layoutNode); |
688 | layoutNode = e; |
689 | } |
690 | n = n.nextSibling(); |
691 | } |
692 | } |
693 | |
694 | void |
695 | VFolderMenu::(const QString &fileName, const QString &baseDir) |
696 | { |
697 | m_docInfoStack.push(m_docInfo); |
698 | if (!baseDir.isEmpty()) |
699 | { |
700 | if (!QDir::isRelativePath(baseDir)) |
701 | m_docInfo.baseDir = KGlobal::dirs()->relativeLocation("xdgconf-menu" , baseDir); |
702 | else |
703 | m_docInfo.baseDir = baseDir; |
704 | } |
705 | |
706 | QString baseName = fileName; |
707 | if (!QDir::isRelativePath(baseName)) |
708 | registerFile(baseName); |
709 | else |
710 | baseName = m_docInfo.baseDir + baseName; |
711 | |
712 | m_docInfo.path = locateMenuFile(fileName); |
713 | if (m_docInfo.path.isEmpty()) |
714 | { |
715 | m_docInfo.baseDir.clear(); |
716 | m_docInfo.baseName.clear(); |
717 | kDebug(7021) << "Menu" << fileName << "not found." ; |
718 | return; |
719 | } |
720 | int i; |
721 | i = baseName.lastIndexOf('/'); |
722 | if (i > 0) |
723 | { |
724 | m_docInfo.baseDir = baseName.left(i+1); |
725 | m_docInfo.baseName = baseName.mid(i+1, baseName.length() - i - 6); |
726 | } |
727 | else |
728 | { |
729 | m_docInfo.baseDir.clear(); |
730 | m_docInfo.baseName = baseName.left( baseName.length() - 5 ); |
731 | } |
732 | } |
733 | |
734 | void |
735 | VFolderMenu::(const QString &basePath, const QString &baseDir) |
736 | { |
737 | m_docInfoStack.push(m_docInfo); |
738 | |
739 | m_docInfo.baseDir = baseDir; |
740 | |
741 | QString fileName = basePath.mid(basePath.lastIndexOf('/')+1); |
742 | m_docInfo.baseName = fileName.left( fileName.length() - 5 ); |
743 | QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName); |
744 | |
745 | QStringList result = KGlobal::dirs()->findAllResources("xdgconf-menu" , baseName); |
746 | |
747 | while( !result.isEmpty() && (result[0] != basePath)) |
748 | result.erase(result.begin()); |
749 | |
750 | if (result.count() <= 1) |
751 | { |
752 | m_docInfo.path.clear(); // No parent found |
753 | return; |
754 | } |
755 | m_docInfo.path = result[1]; |
756 | } |
757 | |
758 | void |
759 | VFolderMenu::() |
760 | { |
761 | m_docInfo = m_docInfoStack.pop(); |
762 | } |
763 | |
764 | QString |
765 | VFolderMenu::(const QString &fileName) |
766 | { |
767 | if (!QDir::isRelativePath(fileName)) |
768 | { |
769 | if (KStandardDirs::exists(fileName)) |
770 | return fileName; |
771 | return QString(); |
772 | } |
773 | |
774 | QString result; |
775 | |
776 | QString = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX" )); |
777 | if (!xdgMenuPrefix.isEmpty()) |
778 | { |
779 | QFileInfo fileInfo(fileName); |
780 | |
781 | QString fileNameOnly = fileInfo.fileName(); |
782 | if (!fileNameOnly.startsWith(xdgMenuPrefix)) |
783 | fileNameOnly = xdgMenuPrefix + fileNameOnly; |
784 | |
785 | QString baseName = QDir::cleanPath(m_docInfo.baseDir + |
786 | fileInfo.path() + '/' + fileNameOnly); |
787 | result = KStandardDirs::locate("xdgconf-menu" , baseName); |
788 | } |
789 | |
790 | if (result.isEmpty()) |
791 | { |
792 | QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName); |
793 | result = KStandardDirs::locate("xdgconf-menu" , baseName); |
794 | } |
795 | |
796 | return result; |
797 | } |
798 | |
799 | QString |
800 | VFolderMenu::(const QString &fileName) |
801 | { |
802 | if (fileName.isEmpty()) |
803 | return QString(); |
804 | |
805 | if (!QDir::isRelativePath(fileName)) |
806 | { |
807 | if (KStandardDirs::exists(fileName)) |
808 | return fileName; |
809 | return QString(); |
810 | } |
811 | |
812 | // First location in the list wins |
813 | for(QStringList::ConstIterator it = m_directoryDirs.constBegin(); |
814 | it != m_directoryDirs.constEnd(); |
815 | ++it) |
816 | { |
817 | QString tmp = (*it)+fileName; |
818 | if (KStandardDirs::exists(tmp)) |
819 | return tmp; |
820 | } |
821 | |
822 | return QString(); |
823 | } |
824 | |
825 | void |
826 | VFolderMenu::() |
827 | { |
828 | m_defaultDataDirs = KGlobal::dirs()->kfsstnd_prefixes().split(':', QString::SkipEmptyParts); |
829 | const QString localDir = m_defaultDataDirs.first(); |
830 | m_defaultDataDirs.removeAll(localDir); // Remove local dir |
831 | |
832 | m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps" , QString()); |
833 | m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs" , QString()); |
834 | m_defaultLegacyDirs = KGlobal::dirs()->resourceDirs("apps" ); |
835 | } |
836 | |
837 | void |
838 | VFolderMenu::(const QString &fileName) |
839 | { |
840 | m_defaultMergeDirs.clear(); |
841 | |
842 | if (!fileName.endsWith(QLatin1String(".menu" ))) |
843 | return; |
844 | |
845 | pushDocInfo(fileName); |
846 | m_defaultMergeDirs << m_docInfo.baseName+"-merged/" ; |
847 | m_doc = loadDoc(); |
848 | popDocInfo(); |
849 | |
850 | if (m_doc.isNull()) |
851 | { |
852 | if (m_docInfo.path.isEmpty()) |
853 | kError(7021) << fileName << " not found in " << m_allDirectories << endl; |
854 | else |
855 | kWarning(7021) << "Load error (" << m_docInfo.path << ")" ; |
856 | return; |
857 | } |
858 | |
859 | QDomElement e = m_doc.documentElement(); |
860 | QString name; |
861 | mergeMenus(e, name); |
862 | } |
863 | |
864 | void |
865 | VFolderMenu::(QDomElement &domElem, QHash<QString,KService::Ptr>& items) |
866 | { |
867 | if (domElem.tagName() == "And" ) |
868 | { |
869 | QDomNode n = domElem.firstChild(); |
870 | // Look for the first child element |
871 | while (!n.isNull()) // loop in case of comments |
872 | { |
873 | QDomElement e = n.toElement(); |
874 | n = n.nextSibling(); |
875 | if ( !e.isNull() ) { |
876 | processCondition(e, items); |
877 | break; // we only want the first one |
878 | } |
879 | } |
880 | |
881 | QHash<QString,KService::Ptr> andItems; |
882 | while( !n.isNull() ) { |
883 | QDomElement e = n.toElement(); |
884 | if (e.tagName() == "Not" ) |
885 | { |
886 | // Special handling for "and not" |
887 | QDomNode n2 = e.firstChild(); |
888 | while( !n2.isNull() ) { |
889 | QDomElement e2 = n2.toElement(); |
890 | andItems.clear(); |
891 | processCondition(e2, andItems); |
892 | excludeItems(items, andItems); |
893 | n2 = n2.nextSibling(); |
894 | } |
895 | } |
896 | else |
897 | { |
898 | andItems.clear(); |
899 | processCondition(e, andItems); |
900 | matchItems(items, andItems); |
901 | } |
902 | n = n.nextSibling(); |
903 | } |
904 | } |
905 | else if (domElem.tagName() == "Or" ) |
906 | { |
907 | QDomNode n = domElem.firstChild(); |
908 | // Look for the first child element |
909 | while (!n.isNull()) // loop in case of comments |
910 | { |
911 | QDomElement e = n.toElement(); |
912 | n = n.nextSibling(); |
913 | if ( !e.isNull() ) { |
914 | processCondition(e, items); |
915 | break; // we only want the first one |
916 | } |
917 | } |
918 | |
919 | QHash<QString,KService::Ptr> orItems; |
920 | while( !n.isNull() ) { |
921 | QDomElement e = n.toElement(); |
922 | if ( !e.isNull() ) { |
923 | orItems.clear(); |
924 | processCondition(e, orItems); |
925 | includeItems(items, orItems); |
926 | } |
927 | n = n.nextSibling(); |
928 | } |
929 | } |
930 | else if (domElem.tagName() == "Not" ) |
931 | { |
932 | FOR_ALL_APPLICATIONS(it) |
933 | { |
934 | KService::Ptr s = it.value(); |
935 | items.insert(s->menuId(), s); |
936 | } |
937 | FOR_ALL_APPLICATIONS_END |
938 | |
939 | QHash<QString,KService::Ptr> notItems; |
940 | QDomNode n = domElem.firstChild(); |
941 | while( !n.isNull() ) { |
942 | QDomElement e = n.toElement(); |
943 | if ( !e.isNull() ) { |
944 | notItems.clear(); |
945 | processCondition(e, notItems); |
946 | excludeItems(items, notItems); |
947 | } |
948 | n = n.nextSibling(); |
949 | } |
950 | } |
951 | else if (domElem.tagName() == "Category" ) |
952 | { |
953 | FOR_CATEGORY(domElem.text(), it) |
954 | { |
955 | KService::Ptr s = *it; |
956 | items.insert(s->menuId(), s); |
957 | } |
958 | FOR_CATEGORY_END |
959 | } |
960 | else if (domElem.tagName() == "All" ) |
961 | { |
962 | FOR_ALL_APPLICATIONS(it) |
963 | { |
964 | KService::Ptr s = it.value(); |
965 | items.insert(s->menuId(), s); |
966 | } |
967 | FOR_ALL_APPLICATIONS_END |
968 | } |
969 | else if (domElem.tagName() == "Filename" ) |
970 | { |
971 | const QString filename = domElem.text(); |
972 | //kDebug(7021) << "Adding file" << filename; |
973 | KService::Ptr s = findApplication(filename); |
974 | if (s) |
975 | items.insert(filename, s); |
976 | } |
977 | } |
978 | |
979 | void |
980 | VFolderMenu::(const QString &dir, const QString &prefix) |
981 | { |
982 | kDebug(7021) << "Looking up applications under" << dir; |
983 | |
984 | QDirIterator it(dir); |
985 | while (it.hasNext()) { |
986 | it.next(); |
987 | const QFileInfo fi = it.fileInfo(); |
988 | const QString fn = fi.fileName(); |
989 | if (fi.isDir()) { |
990 | if(fn == QLatin1String("." ) || fn == QLatin1String(".." )) |
991 | continue; |
992 | loadApplications(fi.filePath(), prefix + fn + '-'); |
993 | continue; |
994 | } |
995 | if (fi.isFile()) { |
996 | if (!fn.endsWith(QLatin1String(".desktop" ))) |
997 | continue; |
998 | KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath()); |
999 | if (service) |
1000 | addApplication(prefix + fn, service); |
1001 | } |
1002 | } |
1003 | } |
1004 | |
1005 | void |
1006 | VFolderMenu::() |
1007 | { |
1008 | kDebug(7021); |
1009 | |
1010 | QHash<QString,KService::Ptr> items; |
1011 | QString prefix = "kde4-" ; |
1012 | |
1013 | QStringList relFiles; |
1014 | |
1015 | (void) KGlobal::dirs()->findAllResources( "apps" , |
1016 | QString(), |
1017 | KStandardDirs::Recursive | |
1018 | KStandardDirs::NoDuplicates, |
1019 | relFiles); |
1020 | for(QStringList::ConstIterator it = relFiles.constBegin(); |
1021 | it != relFiles.constEnd(); ++it) |
1022 | { |
1023 | if (!m_forcedLegacyLoad && (*it).endsWith(QLatin1String(".directory" ))) |
1024 | { |
1025 | QString name = *it; |
1026 | if (!name.endsWith(QLatin1String("/.directory" ))) |
1027 | continue; // Probably ".directory", skip it. |
1028 | |
1029 | name = name.left(name.length()-11); |
1030 | |
1031 | SubMenu * = new SubMenu; |
1032 | newMenu->directoryFile = KStandardDirs::locate("apps" , *it); |
1033 | |
1034 | insertSubMenu(m_currentMenu, name, newMenu); |
1035 | continue; |
1036 | } |
1037 | |
1038 | if ((*it).endsWith(QLatin1String(".desktop" ))) |
1039 | { |
1040 | QString name = *it; |
1041 | KService::Ptr service = m_kbuildsycocaInterface->createService(name); |
1042 | |
1043 | if (service && !m_forcedLegacyLoad) |
1044 | { |
1045 | QString id = name; |
1046 | // Strip path from id |
1047 | int i = id.lastIndexOf('/'); |
1048 | if (i >= 0) |
1049 | id = id.mid(i+1); |
1050 | |
1051 | id.prepend(prefix); |
1052 | |
1053 | // TODO: add Legacy category |
1054 | addApplication(id, service); |
1055 | items.insert(service->menuId(), service); |
1056 | if (service->categories().isEmpty()) |
1057 | insertService(m_currentMenu, name, service); |
1058 | |
1059 | } |
1060 | } |
1061 | } |
1062 | markUsedApplications(items); |
1063 | m_legacyLoaded = true; |
1064 | } |
1065 | |
1066 | void |
1067 | VFolderMenu::(const QString &dir, const QString &relDir, const QString &prefix) |
1068 | { |
1069 | kDebug(7021).nospace() << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")" ; |
1070 | |
1071 | QHash<QString,KService::Ptr> items; |
1072 | QDirIterator it(dir); |
1073 | while (it.hasNext()) { |
1074 | it.next(); |
1075 | const QFileInfo fi = it.fileInfo(); |
1076 | const QString fn = fi.fileName(); |
1077 | if (fi.isDir()) { |
1078 | if(fn == QLatin1String("." ) || fn == QLatin1String(".." )) |
1079 | continue; |
1080 | SubMenu * = m_currentMenu; |
1081 | |
1082 | m_currentMenu = new SubMenu; |
1083 | m_currentMenu->name = fn; |
1084 | m_currentMenu->directoryFile = fi.absoluteFilePath() + "/.directory" ; |
1085 | |
1086 | parentMenu->subMenus.append(m_currentMenu); |
1087 | |
1088 | processLegacyDir(fi.filePath(), relDir + fn + '/', prefix); |
1089 | m_currentMenu = parentMenu; |
1090 | continue; |
1091 | } |
1092 | if (fi.isFile() /*&& !fi.isSymLink() ?? */) { |
1093 | if (!fn.endsWith(QLatin1String(".desktop" ))) |
1094 | continue; |
1095 | KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath()); |
1096 | if (service) |
1097 | { |
1098 | const QString id = prefix + fn; |
1099 | |
1100 | // TODO: Add legacy category |
1101 | addApplication(id, service); |
1102 | items.insert(service->menuId(), service); |
1103 | |
1104 | if (service->categories().isEmpty()) |
1105 | m_currentMenu->items.insert(id, service); |
1106 | } |
1107 | } |
1108 | } |
1109 | markUsedApplications(items); |
1110 | } |
1111 | |
1112 | |
1113 | |
1114 | void |
1115 | VFolderMenu::(QDomElement &docElem, int pass) |
1116 | { |
1117 | SubMenu * = m_currentMenu; |
1118 | int oldDirectoryDirsCount = m_directoryDirs.count(); |
1119 | |
1120 | QString name; |
1121 | QString directoryFile; |
1122 | bool onlyUnallocated = false; |
1123 | bool isDeleted = false; |
1124 | bool kdeLegacyDirsDone = false; |
1125 | QDomElement defaultLayoutNode; |
1126 | QDomElement layoutNode; |
1127 | |
1128 | QDomElement query; |
1129 | QDomNode n = docElem.firstChild(); |
1130 | while( !n.isNull() ) { |
1131 | QDomElement e = n.toElement(); // try to convert the node to an element. |
1132 | if (e.tagName() == "Name" ) |
1133 | { |
1134 | name = e.text(); |
1135 | } |
1136 | else if (e.tagName() == "Directory" ) |
1137 | { |
1138 | directoryFile = e.text(); |
1139 | } |
1140 | else if (e.tagName() == "DirectoryDir" ) |
1141 | { |
1142 | QString dir = absoluteDir(e.text(), e.attribute("__BaseDir" )); |
1143 | |
1144 | m_directoryDirs.prepend(dir); |
1145 | } |
1146 | else if (e.tagName() == "OnlyUnallocated" ) |
1147 | { |
1148 | onlyUnallocated = true; |
1149 | } |
1150 | else if (e.tagName() == "NotOnlyUnallocated" ) |
1151 | { |
1152 | onlyUnallocated = false; |
1153 | } |
1154 | else if (e.tagName() == "Deleted" ) |
1155 | { |
1156 | isDeleted = true; |
1157 | } |
1158 | else if (e.tagName() == "NotDeleted" ) |
1159 | { |
1160 | isDeleted = false; |
1161 | } |
1162 | else if (e.tagName() == "DefaultLayout" ) |
1163 | { |
1164 | defaultLayoutNode = e; |
1165 | } |
1166 | else if (e.tagName() == "Layout" ) |
1167 | { |
1168 | layoutNode = e; |
1169 | } |
1170 | n = n.nextSibling(); |
1171 | } |
1172 | |
1173 | // Setup current menu entry |
1174 | if (pass == 0) |
1175 | { |
1176 | m_currentMenu = 0; |
1177 | // Look up menu |
1178 | if (parentMenu) |
1179 | { |
1180 | foreach (SubMenu *, parentMenu->subMenus) |
1181 | { |
1182 | if (menu->name == name) |
1183 | { |
1184 | m_currentMenu = menu; |
1185 | break; |
1186 | } |
1187 | } |
1188 | } |
1189 | |
1190 | if (!m_currentMenu) // Not found? |
1191 | { |
1192 | // Create menu |
1193 | m_currentMenu = new SubMenu; |
1194 | m_currentMenu->name = name; |
1195 | |
1196 | if (parentMenu) |
1197 | parentMenu->subMenus.append(m_currentMenu); |
1198 | else |
1199 | m_rootMenu = m_currentMenu; |
1200 | } |
1201 | if (directoryFile.isEmpty()) |
1202 | { |
1203 | kDebug(7021) << "Menu" << name << "does not specify a directory file." ; |
1204 | } |
1205 | |
1206 | // Override previous directoryFile iff available |
1207 | QString tmp = locateDirectoryFile(directoryFile); |
1208 | if (! tmp.isEmpty()) |
1209 | m_currentMenu->directoryFile = tmp; |
1210 | m_currentMenu->isDeleted = isDeleted; |
1211 | |
1212 | m_currentMenu->defaultLayoutNode = defaultLayoutNode; |
1213 | m_currentMenu->layoutNode = layoutNode; |
1214 | } |
1215 | else |
1216 | { |
1217 | // Look up menu |
1218 | if (parentMenu) |
1219 | { |
1220 | foreach (SubMenu *, parentMenu->subMenus) |
1221 | { |
1222 | if (menu->name == name) |
1223 | { |
1224 | m_currentMenu = menu; |
1225 | break; |
1226 | } |
1227 | } |
1228 | } |
1229 | else |
1230 | { |
1231 | m_currentMenu = m_rootMenu; |
1232 | } |
1233 | } |
1234 | |
1235 | // Process AppDir and LegacyDir |
1236 | if (pass == 0) |
1237 | { |
1238 | QDomElement query; |
1239 | QDomNode n = docElem.firstChild(); |
1240 | while( !n.isNull() ) { |
1241 | QDomElement e = n.toElement(); // try to convert the node to an element. |
1242 | if (e.tagName() == "AppDir" ) |
1243 | { |
1244 | createAppsInfo(); |
1245 | QString dir = absoluteDir(e.text(), e.attribute("__BaseDir" )); |
1246 | |
1247 | registerDirectory(dir); |
1248 | |
1249 | loadApplications(dir, QString()); |
1250 | } |
1251 | else if (e.tagName() == "KDELegacyDirs" ) |
1252 | { |
1253 | createAppsInfo(); |
1254 | if (!kdeLegacyDirsDone) |
1255 | { |
1256 | kDebug(7021) << "Processing KDE Legacy dirs for <KDE>" ; |
1257 | SubMenu * = m_currentMenu; |
1258 | m_currentMenu = new SubMenu; |
1259 | |
1260 | processKDELegacyDirs(); |
1261 | |
1262 | m_legacyNodes.insert("<KDE>" , m_currentMenu); |
1263 | m_currentMenu = oldMenu; |
1264 | |
1265 | kdeLegacyDirsDone = true; |
1266 | } |
1267 | } |
1268 | else if (e.tagName() == "LegacyDir" ) |
1269 | { |
1270 | createAppsInfo(); |
1271 | QString dir = absoluteDir(e.text(), e.attribute("__BaseDir" )); |
1272 | |
1273 | QString prefix = e.attributes().namedItem("prefix" ).toAttr().value(); |
1274 | |
1275 | #ifndef Q_OS_WIN |
1276 | if (m_defaultLegacyDirs.contains(dir)) |
1277 | #else |
1278 | if (m_defaultLegacyDirs.contains(dir, Qt::CaseInsensitive)) |
1279 | #endif |
1280 | { |
1281 | if (!kdeLegacyDirsDone) |
1282 | { |
1283 | kDebug(7021) << "Processing KDE Legacy dirs for" << dir; |
1284 | SubMenu * = m_currentMenu; |
1285 | m_currentMenu = new SubMenu; |
1286 | |
1287 | processKDELegacyDirs(); |
1288 | |
1289 | m_legacyNodes.insert("<KDE>" , m_currentMenu); |
1290 | m_currentMenu = oldMenu; |
1291 | |
1292 | kdeLegacyDirsDone = true; |
1293 | } |
1294 | } |
1295 | else |
1296 | { |
1297 | SubMenu * = m_currentMenu; |
1298 | m_currentMenu = new SubMenu; |
1299 | |
1300 | registerDirectory(dir); |
1301 | |
1302 | processLegacyDir(dir, QString(), prefix); |
1303 | |
1304 | m_legacyNodes.insert(dir, m_currentMenu); |
1305 | m_currentMenu = oldMenu; |
1306 | } |
1307 | } |
1308 | n = n.nextSibling(); |
1309 | } |
1310 | } |
1311 | |
1312 | loadAppsInfo(); // Update the scope wrt the list of applications |
1313 | |
1314 | if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated)) |
1315 | { |
1316 | n = docElem.firstChild(); |
1317 | |
1318 | while( !n.isNull() ) { |
1319 | QDomElement e = n.toElement(); // try to convert the node to an element. |
1320 | if (e.tagName() == "Include" ) |
1321 | { |
1322 | QHash<QString,KService::Ptr> items; |
1323 | |
1324 | QDomNode n2 = e.firstChild(); |
1325 | while( !n2.isNull() ) { |
1326 | QDomElement e2 = n2.toElement(); |
1327 | items.clear(); |
1328 | processCondition(e2, items); |
1329 | if (m_track) |
1330 | track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Include>" ); |
1331 | includeItems(m_currentMenu->items, items); |
1332 | excludeItems(m_currentMenu->excludeItems, items); |
1333 | markUsedApplications(items); |
1334 | |
1335 | if (m_track) |
1336 | track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Include>" ); |
1337 | |
1338 | n2 = n2.nextSibling(); |
1339 | } |
1340 | } |
1341 | |
1342 | else if (e.tagName() == "Exclude" ) |
1343 | { |
1344 | QHash<QString,KService::Ptr> items; |
1345 | |
1346 | QDomNode n2 = e.firstChild(); |
1347 | while( !n2.isNull() ) { |
1348 | QDomElement e2 = n2.toElement(); |
1349 | items.clear(); |
1350 | processCondition(e2, items); |
1351 | if (m_track) |
1352 | track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Exclude>" ); |
1353 | excludeItems(m_currentMenu->items, items); |
1354 | includeItems(m_currentMenu->excludeItems, items); |
1355 | if (m_track) |
1356 | track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Exclude>" ); |
1357 | n2 = n2.nextSibling(); |
1358 | } |
1359 | } |
1360 | |
1361 | n = n.nextSibling(); |
1362 | } |
1363 | } |
1364 | |
1365 | n = docElem.firstChild(); |
1366 | while( !n.isNull() ) { |
1367 | QDomElement e = n.toElement(); // try to convert the node to an element. |
1368 | if (e.tagName() == "Menu" ) |
1369 | { |
1370 | processMenu(e, pass); |
1371 | } |
1372 | // We insert legacy dir in pass 0, this way the order in the .menu-file determines |
1373 | // which .directory file gets used, but the menu-entries of legacy-menus will always |
1374 | // have the lowest priority. |
1375 | // else if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated)) |
1376 | else if (pass == 0) |
1377 | { |
1378 | if (e.tagName() == "LegacyDir" ) |
1379 | { |
1380 | // Add legacy nodes to Menu structure |
1381 | QString dir = absoluteDir(e.text(), e.attribute("__BaseDir" )); |
1382 | SubMenu * = m_legacyNodes[dir]; |
1383 | if (legacyMenu) |
1384 | { |
1385 | mergeMenu(m_currentMenu, legacyMenu); |
1386 | } |
1387 | } |
1388 | |
1389 | else if (e.tagName() == "KDELegacyDirs" ) |
1390 | { |
1391 | // Add legacy nodes to Menu structure |
1392 | QString dir = "<KDE>" ; |
1393 | SubMenu * = m_legacyNodes[dir]; |
1394 | if (legacyMenu) |
1395 | { |
1396 | mergeMenu(m_currentMenu, legacyMenu); |
1397 | } |
1398 | } |
1399 | } |
1400 | n = n.nextSibling(); |
1401 | } |
1402 | |
1403 | if (pass == 2) |
1404 | { |
1405 | n = docElem.firstChild(); |
1406 | while( !n.isNull() ) { |
1407 | QDomElement e = n.toElement(); // try to convert the node to an element. |
1408 | if (e.tagName() == "Move" ) |
1409 | { |
1410 | QString orig; |
1411 | QString dest; |
1412 | QDomNode n2 = e.firstChild(); |
1413 | while( !n2.isNull() ) { |
1414 | QDomElement e2 = n2.toElement(); // try to convert the node to an element. |
1415 | if( e2.tagName() == "Old" ) |
1416 | orig = e2.text(); |
1417 | if( e2.tagName() == "New" ) |
1418 | dest = e2.text(); |
1419 | n2 = n2.nextSibling(); |
1420 | } |
1421 | kDebug(7021) << "Moving" << orig << "to" << dest; |
1422 | if (!orig.isEmpty() && !dest.isEmpty()) |
1423 | { |
1424 | SubMenu * = takeSubMenu(m_currentMenu, orig); |
1425 | if (menu) |
1426 | { |
1427 | insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority |
1428 | } |
1429 | } |
1430 | } |
1431 | n = n.nextSibling(); |
1432 | } |
1433 | |
1434 | } |
1435 | |
1436 | unloadAppsInfo(); // Update the scope wrt the list of applications |
1437 | |
1438 | while (m_directoryDirs.count() > oldDirectoryDirsCount) |
1439 | m_directoryDirs.pop_front(); |
1440 | |
1441 | m_currentMenu = parentMenu; |
1442 | } |
1443 | |
1444 | |
1445 | |
1446 | static QString parseAttribute( const QDomElement &e) |
1447 | { |
1448 | QString option; |
1449 | if ( e.hasAttribute( "show_empty" ) ) |
1450 | { |
1451 | QString str = e.attribute( "show_empty" ); |
1452 | if ( str=="true" ) |
1453 | option= "ME " ; |
1454 | else if ( str=="false" ) |
1455 | option= "NME " ; |
1456 | else |
1457 | kDebug()<<" Error in parsing show_empty attribute :" <<str; |
1458 | } |
1459 | if ( e.hasAttribute( "inline" ) ) |
1460 | { |
1461 | QString str = e.attribute( "inline" ); |
1462 | if ( str=="true" ) |
1463 | option+="I " ; |
1464 | else if ( str=="false" ) |
1465 | option+="NI " ; |
1466 | else |
1467 | kDebug()<<" Error in parsing inline attribute :" <<str; |
1468 | } |
1469 | if ( e.hasAttribute( "inline_limit" ) ) |
1470 | { |
1471 | bool ok; |
1472 | int value = e.attribute( "inline_limit" ).toInt(&ok); |
1473 | if ( ok ) |
1474 | option+=QString( "IL[%1] " ).arg( value ); |
1475 | } |
1476 | if ( e.hasAttribute( "inline_header" ) ) |
1477 | { |
1478 | QString str = e.attribute( "inline_header" ); |
1479 | if ( str=="true" ) |
1480 | option+="IH " ; |
1481 | else if ( str == "false" ) |
1482 | option+="NIH " ; |
1483 | else |
1484 | kDebug()<<" Error in parsing of inline_header attribute :" <<str; |
1485 | |
1486 | } |
1487 | if ( e.hasAttribute( "inline_alias" ) && e.attribute( "inline_alias" )=="true" ) |
1488 | { |
1489 | QString str = e.attribute( "inline_alias" ); |
1490 | if ( str=="true" ) |
1491 | option+="IA" ; |
1492 | else if ( str=="false" ) |
1493 | option+="NIA" ; |
1494 | else |
1495 | kDebug()<<" Error in parsing inline_alias attribute :" <<str; |
1496 | } |
1497 | if( !option.isEmpty()) |
1498 | { |
1499 | option = option.prepend(":O" ); |
1500 | } |
1501 | return option; |
1502 | |
1503 | } |
1504 | |
1505 | static QStringList parseLayoutNode(const QDomElement &docElem) |
1506 | { |
1507 | QStringList layout; |
1508 | |
1509 | QString optionDefaultLayout; |
1510 | if( docElem.tagName()=="DefaultLayout" ) |
1511 | optionDefaultLayout = parseAttribute( docElem); |
1512 | if ( !optionDefaultLayout.isEmpty() ) |
1513 | layout.append( optionDefaultLayout ); |
1514 | |
1515 | bool mergeTagExists = false; |
1516 | QDomNode n = docElem.firstChild(); |
1517 | while( !n.isNull() ) { |
1518 | QDomElement e = n.toElement(); // try to convert the node to an element. |
1519 | if (e.tagName() == "Separator" ) |
1520 | { |
1521 | layout.append(":S" ); |
1522 | } |
1523 | else if (e.tagName() == "Filename" ) |
1524 | { |
1525 | layout.append(e.text()); |
1526 | } |
1527 | else if (e.tagName() == "Menuname" ) |
1528 | { |
1529 | layout.append('/'+e.text()); |
1530 | QString option = parseAttribute( e ); |
1531 | if( !option.isEmpty()) |
1532 | layout.append( option ); |
1533 | } |
1534 | else if (e.tagName() == "Merge" ) |
1535 | { |
1536 | QString type = e.attributeNode("type" ).value(); |
1537 | if (type == "files" ) |
1538 | layout.append(":F" ); |
1539 | else if (type == "menus" ) |
1540 | layout.append(":M" ); |
1541 | else if (type == "all" ) |
1542 | layout.append(":A" ); |
1543 | mergeTagExists = true; |
1544 | } |
1545 | |
1546 | n = n.nextSibling(); |
1547 | } |
1548 | |
1549 | if ( !mergeTagExists ) { |
1550 | layout.append(":M" ); |
1551 | layout.append(":F" ); |
1552 | kWarning() << "The menu spec file contains a Layout or DefaultLayout tag without the mandatory Merge tag inside. Please fix your file." ; |
1553 | } |
1554 | return layout; |
1555 | } |
1556 | |
1557 | void |
1558 | VFolderMenu::(VFolderMenu::SubMenu *, QStringList defaultLayout) //krazy:exclude=passbyvalue |
1559 | { |
1560 | if (!menu->defaultLayoutNode.isNull()) |
1561 | { |
1562 | defaultLayout = parseLayoutNode(menu->defaultLayoutNode); |
1563 | } |
1564 | |
1565 | if (menu->layoutNode.isNull()) |
1566 | { |
1567 | menu->layoutList = defaultLayout; |
1568 | } |
1569 | else |
1570 | { |
1571 | menu->layoutList = parseLayoutNode(menu->layoutNode); |
1572 | if (menu->layoutList.isEmpty()) |
1573 | menu->layoutList = defaultLayout; |
1574 | } |
1575 | |
1576 | foreach (VFolderMenu::SubMenu *, menu->subMenus) |
1577 | { |
1578 | layoutMenu(subMenu, defaultLayout); |
1579 | } |
1580 | } |
1581 | |
1582 | void |
1583 | VFolderMenu::(const QHash<QString,KService::Ptr>& items) |
1584 | { |
1585 | foreach(const KService::Ptr &p, items) |
1586 | m_usedAppsDict.insert(p->menuId()); |
1587 | } |
1588 | |
1589 | VFolderMenu::SubMenu * |
1590 | VFolderMenu::(const QString &file, bool forceLegacyLoad) |
1591 | { |
1592 | m_forcedLegacyLoad = false; |
1593 | m_legacyLoaded = false; |
1594 | m_appsInfo = 0; |
1595 | |
1596 | const QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu" ); |
1597 | for(QStringList::ConstIterator it=dirs.begin(); |
1598 | it != dirs.end(); ++it) |
1599 | { |
1600 | registerDirectory(*it); |
1601 | } |
1602 | |
1603 | loadMenu(file); |
1604 | |
1605 | delete m_rootMenu; |
1606 | m_rootMenu = m_currentMenu = 0; |
1607 | |
1608 | QDomElement docElem = m_doc.documentElement(); |
1609 | |
1610 | for (int pass = 0; pass <= 2; pass++) |
1611 | { |
1612 | // pass 0: load application desktop files |
1613 | // pass 1: the normal processing |
1614 | // pass 2: process <OnlyUnallocated> to put unused files into "Lost & Found". |
1615 | processMenu(docElem, pass); |
1616 | |
1617 | switch (pass) { |
1618 | case 0: |
1619 | // Fill the dictCategories for each AppsInfo in m_appsInfoList, |
1620 | // in preparation for processMenu pass 1. |
1621 | buildApplicationIndex(false); |
1622 | break; |
1623 | case 1: |
1624 | // Fill the dictCategories for each AppsInfo in m_appsInfoList, |
1625 | // with only the unused apps, in preparation for processMenu pass 2. |
1626 | buildApplicationIndex(true /* unusedOnly */); |
1627 | break; |
1628 | case 2: |
1629 | { |
1630 | QStringList defaultLayout; |
1631 | defaultLayout << ":M" ; // Sub-Menus |
1632 | defaultLayout << ":F" ; // Individual entries |
1633 | layoutMenu(m_rootMenu, defaultLayout); |
1634 | break; |
1635 | } |
1636 | default: |
1637 | break; |
1638 | } |
1639 | } |
1640 | |
1641 | if (!m_legacyLoaded && forceLegacyLoad) |
1642 | { |
1643 | m_forcedLegacyLoad = true; |
1644 | processKDELegacyDirs(); |
1645 | } |
1646 | |
1647 | return m_rootMenu; |
1648 | } |
1649 | |
1650 | void |
1651 | VFolderMenu::(const QString &id) |
1652 | { |
1653 | m_track = !id.isEmpty(); |
1654 | m_trackId = id; |
1655 | } |
1656 | |
1657 | #include "vfolder_menu.moc" |
1658 | |