/* This file is part of the KDE libraries * Copyright (C) 2003 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "vfolder_menu.h" #include "kbuildservicefactory.h" #include "kbuildsycocainterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void foldNode(QDomElement &docElem, QDomElement &e, QMap &dupeList, QString s=QString()) //krazy:exclude=passbyvalue { if (s.isEmpty()) s = e.text(); QMap::iterator it = dupeList.find(s); if (it != dupeList.end()) { kDebug(7021) << e.tagName() << "and" << s << "requires combining!"; docElem.removeChild(*it); dupeList.erase(it); } dupeList.insert(s, e); } static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag) { foreach(const QString &it, list) { QDomElement e = docElem.ownerDocument().createElement(tag); QDomText txt = docElem.ownerDocument().createTextNode(it); e.appendChild(txt); docElem.insertAfter(e, n); } QDomNode next = n.nextSibling(); docElem.removeChild(n); n = next; // kDebug(7021) << "Next tag = " << n.toElement().tagName(); } void VFolderMenu::registerFile(const QString &file) { int i = file.lastIndexOf('/'); if (i < 0) return; QString dir = file.left(i+1); // Include trailing '/' registerDirectory(dir); } void VFolderMenu::registerDirectory(const QString &directory) { m_allDirectories.append(directory); } QStringList VFolderMenu::allDirectories() { if (m_allDirectories.isEmpty()) return m_allDirectories; m_allDirectories.removeDuplicates(); m_allDirectories.sort(); return m_allDirectories; } static void track(const QString &menuId, const QString &menuName, const QHash& includeList, const QHash& excludeList, const QHash& itemList, const QString &comment) { if (itemList.contains(menuId)) printf("%s: %s INCL %d EXCL %d\n", qPrintable(menuName), qPrintable(comment), includeList.contains(menuId) ? 1 : 0, excludeList.contains(menuId) ? 1 : 0); } void VFolderMenu::includeItems(QHash& items1, const QHash& items2) { foreach (const KService::Ptr &p, items2) { items1.insert(p->menuId(), p); } } void VFolderMenu::matchItems(QHash& items1, const QHash& items2) { QHash::iterator it = items1.begin(); while (it != items1.end()) { QString id = (*it)->menuId(); if (!items2.contains(id)) { it = items1.erase(it); } else { it++; } } } void VFolderMenu::excludeItems(QHash& items1, const QHash& items2) { foreach (const KService::Ptr &p, items2) { items1.remove(p->menuId()); } } VFolderMenu::SubMenu* VFolderMenu::takeSubMenu(SubMenu *parentMenu, const QString &menuName) { const int i = menuName.indexOf('/'); const QString s1 = i > 0 ? menuName.left(i) : menuName; const QString s2 = menuName.mid(i+1); // Look up menu for (QList::Iterator it = parentMenu->subMenus.begin(); it != parentMenu->subMenus.end(); ++it) { SubMenu* menu = *it; if (menu->name == s1) { if (i == -1) { // Take it out parentMenu->subMenus.erase(it); return menu; } else { return takeSubMenu(menu, s2); } } } return 0; // Not found } void VFolderMenu::mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority) { if (m_track) { track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("Before MenuMerge w. %1 (incl)").arg(menu2->name)); track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("Before MenuMerge w. %1 (excl)").arg(menu2->name)); } if (reversePriority) { // Merge menu1 with menu2, menu1 takes precedent excludeItems(menu2->items, menu1->excludeItems); includeItems(menu1->items, menu2->items); excludeItems(menu2->excludeItems, menu1->items); includeItems(menu1->excludeItems, menu2->excludeItems); } else { // Merge menu1 with menu2, menu2 takes precedent excludeItems(menu1->items, menu2->excludeItems); includeItems(menu1->items, menu2->items); includeItems(menu1->excludeItems, menu2->excludeItems); menu1->isDeleted = menu2->isDeleted; } while (!menu2->subMenus.isEmpty()) { SubMenu *subMenu = menu2->subMenus.takeFirst(); insertSubMenu(menu1, subMenu->name, subMenu, reversePriority); } if (reversePriority) { // Merge menu1 with menu2, menu1 takes precedent if (menu1->directoryFile.isEmpty()) menu1->directoryFile = menu2->directoryFile; if (menu1->defaultLayoutNode.isNull()) menu1->defaultLayoutNode = menu2->defaultLayoutNode; if (menu1->layoutNode.isNull()) menu1->layoutNode = menu2->layoutNode; } else { // Merge menu1 with menu2, menu2 takes precedent if (!menu2->directoryFile.isEmpty()) menu1->directoryFile = menu2->directoryFile; if (!menu2->defaultLayoutNode.isNull()) menu1->defaultLayoutNode = menu2->defaultLayoutNode; if (!menu2->layoutNode.isNull()) menu1->layoutNode = menu2->layoutNode; } if (m_track) { track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("After MenuMerge w. %1 (incl)").arg(menu2->name)); track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("After MenuMerge w. %1 (excl)").arg(menu2->name)); } delete menu2; } void VFolderMenu::insertSubMenu(SubMenu *parentMenu, const QString &menuName, SubMenu *newMenu, bool reversePriority) { const int i = menuName.indexOf('/'); const QString s1 = menuName.left(i); const QString s2 = menuName.mid(i+1); // Look up menu foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == s1) { if (i == -1) { mergeMenu(menu, newMenu, reversePriority); return; } else { insertSubMenu(menu, s2, newMenu, reversePriority); return; } } } if (i == -1) { // Add it here newMenu->name = menuName; parentMenu->subMenus.append(newMenu); } else { SubMenu *menu = new SubMenu; menu->name = s1; parentMenu->subMenus.append(menu); insertSubMenu(menu, s2, newMenu); } } void VFolderMenu::insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService) { const int i = name.indexOf('/'); if (i == -1) { // Add it here parentMenu->items.insert(newService->menuId(), newService); return; } QString s1 = name.left(i); QString s2 = name.mid(i+1); // Look up menu foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == s1) { insertService(menu, s2, newService); return; } } SubMenu *menu = new SubMenu; menu->name = s1; parentMenu->subMenus.append(menu); insertService(menu, s2, newService); } VFolderMenu::VFolderMenu(KBuildServiceFactory* serviceFactory, KBuildSycocaInterface* kbuildsycocaInterface) : m_track(false), m_serviceFactory(serviceFactory), m_kbuildsycocaInterface(kbuildsycocaInterface) { m_usedAppsDict.reserve(797); m_rootMenu = 0; initDirs(); } VFolderMenu::~VFolderMenu() { delete m_rootMenu; delete m_appsInfo; } #define FOR_ALL_APPLICATIONS(it) \ foreach (AppsInfo *info, m_appsInfoStack) \ { \ QHashIterator it = info->applications; \ while (it.hasNext()) \ { \ it.next(); #define FOR_ALL_APPLICATIONS_END } } #define FOR_CATEGORY(category, it) \ foreach (AppsInfo *info, m_appsInfoStack) \ { \ const KService::List list = info->dictCategories.value(category); \ for(KService::List::ConstIterator it = list.constBegin(); \ it != list.constEnd(); ++it) \ { #define FOR_CATEGORY_END } } KService::Ptr VFolderMenu::findApplication(const QString &relPath) { foreach(AppsInfo *info, m_appsInfoStack) { if (info->applications.contains(relPath)) { KService::Ptr s = info->applications[relPath]; if (s) return s; } } return KService::Ptr(); } void VFolderMenu::addApplication(const QString &id, KService::Ptr service) { service->setMenuId(id); m_appsInfo->applications.insert(id, service); // replaces, if already there m_serviceFactory->addEntry(KSycocaEntry::Ptr::staticCast(service)); } void VFolderMenu::buildApplicationIndex(bool unusedOnly) { foreach (AppsInfo *info, m_appsInfoList) { info->dictCategories.clear(); QMutableHashIterator it = info->applications; while (it.hasNext()) { KService::Ptr s = it.next().value(); if (unusedOnly && m_usedAppsDict.contains(s->menuId())) { // Remove and skip this one it.remove(); continue; } Q_FOREACH(const QString& cat, s->categories()) { info->dictCategories[cat].append(s); // find or insert entry in hash } } } } void VFolderMenu::createAppsInfo() { if (m_appsInfo) return; m_appsInfo = new AppsInfo; m_appsInfoStack.prepend(m_appsInfo); m_appsInfoList.append(m_appsInfo); m_currentMenu->apps_info = m_appsInfo; } void VFolderMenu::loadAppsInfo() { m_appsInfo = m_currentMenu->apps_info; if (!m_appsInfo) return; // No appsInfo for this menu if (m_appsInfoStack.count() && m_appsInfoStack.first() == m_appsInfo) return; // Already added (By createAppsInfo?) m_appsInfoStack.prepend(m_appsInfo); // Add } void VFolderMenu::unloadAppsInfo() { m_appsInfo = m_currentMenu->apps_info; if (!m_appsInfo) return; // No appsInfo for this menu if (m_appsInfoStack.first() != m_appsInfo) { return; // Already removed (huh?) } m_appsInfoStack.removeAll(m_appsInfo); // Remove m_appsInfo = 0; } QString VFolderMenu::absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg) { QString dir = _dir; if (QDir::isRelativePath(dir)) { dir = baseDir + dir; } if (!dir.endsWith('/')) dir += '/'; bool relative = QDir::isRelativePath(dir); if (relative && !keepRelativeToCfg) { relative = false; dir = KGlobal::dirs()->findResource("xdgconf-menu", dir); } if (!relative) dir = KStandardDirs::realPath(dir); return dir; } static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir) { QDomNodeList mergeFileList = doc.elementsByTagName(tag); for(int i = 0; i < (int)mergeFileList.count(); i++) { QDomAttr attr = doc.createAttribute("__BaseDir"); attr.setValue(dir); mergeFileList.item(i).toElement().setAttributeNode(attr); } } static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path) { QDomNodeList mergeFileList = doc.elementsByTagName(tag); for(int i = 0; i < (int)mergeFileList.count(); i++) { QDomAttr attr = doc.createAttribute("__BasePath"); attr.setValue(path); mergeFileList.item(i).toElement().setAttributeNode(attr); } } QDomDocument VFolderMenu::loadDoc() { QDomDocument doc; if ( m_docInfo.path.isEmpty() ) { return doc; } QFile file( m_docInfo.path ); if ( !file.open( QIODevice::ReadOnly ) ) { kWarning(7021) << "Could not open " << m_docInfo.path; return doc; } QString errorMsg; int errorRow; int errorCol; if ( !doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) { kWarning(7021) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg; file.close(); return doc; } file.close(); tagBaseDir(doc, "MergeFile", m_docInfo.baseDir); tagBasePath(doc, "MergeFile", m_docInfo.path); tagBaseDir(doc, "MergeDir", m_docInfo.baseDir); tagBaseDir(doc, "DirectoryDir", m_docInfo.baseDir); tagBaseDir(doc, "AppDir", m_docInfo.baseDir); return doc; } void VFolderMenu::mergeFile(QDomElement &parent, const QDomNode &mergeHere) { kDebug(7021) << "VFolderMenu::mergeFile:" << m_docInfo.path; QDomDocument doc = loadDoc(); QDomElement docElem = doc.documentElement(); QDomNode n = docElem.firstChild(); QDomNode last = mergeHere; while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. QDomNode next = n.nextSibling(); if (e.isNull()) { // Skip } // The spec says we must ignore any Name nodes else if (e.tagName() != "Name") { parent.insertAfter(n, last); last = n; } docElem.removeChild(n); n = next; } } void VFolderMenu::mergeMenus(QDomElement &docElem, QString &name) { QMap menuNodes; QMap directoryNodes; QMap appDirNodes; QMap directoryDirNodes; QDomElement defaultLayoutNode; QDomElement layoutNode; QDomNode n = docElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. if( e.isNull() ) { // kDebug(7021) << "Empty node"; } else if( e.tagName() == "DefaultAppDirs") { // Replace with m_defaultAppDirs replaceNode(docElem, n, m_defaultAppDirs, "AppDir"); continue; } else if( e.tagName() == "DefaultDirectoryDirs") { // Replace with m_defaultDirectoryDirs replaceNode(docElem, n, m_defaultDirectoryDirs, "DirectoryDir"); continue; } else if( e.tagName() == "DefaultMergeDirs") { // Replace with m_defaultMergeDirs replaceNode(docElem, n, m_defaultMergeDirs, "MergeDir"); continue; } else if( e.tagName() == "AppDir") { // Filter out dupes foldNode(docElem, e, appDirNodes); } else if( e.tagName() == "DirectoryDir") { // Filter out dupes foldNode(docElem, e, directoryDirNodes); } else if( e.tagName() == "Directory") { // Filter out dupes foldNode(docElem, e, directoryNodes); } else if( e.tagName() == "Move") { // Filter out dupes QString orig; QDomNode n2 = e.firstChild(); while( !n2.isNull() ) { QDomElement e2 = n2.toElement(); // try to convert the node to an element. if( e2.tagName() == "Old") { orig = e2.text(); break; } n2 = n2.nextSibling(); } foldNode(docElem, e, appDirNodes, orig); } else if( e.tagName() == "Menu") { QString name; mergeMenus(e, name); QMap::iterator it = menuNodes.find(name); if (it != menuNodes.end()) { QDomElement docElem2 = *it; QDomNode n2 = docElem2.firstChild(); QDomNode first = e.firstChild(); while( !n2.isNull() ) { QDomElement e2 = n2.toElement(); // try to convert the node to an element. QDomNode n3 = n2.nextSibling(); e.insertBefore(n2, first); docElem2.removeChild(n2); n2 = n3; } // We still have duplicated Name entries // but we don't care about that docElem.removeChild(docElem2); menuNodes.erase(it); } menuNodes.insert(name, e); } else if( e.tagName() == "MergeFile") { if ((e.attribute("type") == "parent")) pushDocInfoParent(e.attribute("__BasePath"), e.attribute("__BaseDir")); else pushDocInfo(e.text(), e.attribute("__BaseDir")); if (!m_docInfo.path.isEmpty()) mergeFile(docElem, n); popDocInfo(); QDomNode last = n; n = n.nextSibling(); docElem.removeChild(last); // Remove the MergeFile node continue; } else if( e.tagName() == "MergeDir") { QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"), true); const QStringList dirs = KGlobal::dirs()->findDirs("xdgconf-menu", dir); foreach(const QString &it, dirs) { registerDirectory(it); } QStringList fileList; if (!QDir::isRelativePath(dir)) { // Absolute fileList = KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu"); } else { // Relative (void) KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu", KStandardDirs::NoDuplicates, fileList); } foreach(const QString &it, fileList) { pushDocInfo(it); mergeFile(docElem, n); popDocInfo(); } QDomNode last = n; n = n.nextSibling(); docElem.removeChild(last); // Remove the MergeDir node continue; } else if( e.tagName() == "Name") { name = e.text(); } else if( e.tagName() == "DefaultLayout") { if (!defaultLayoutNode.isNull()) docElem.removeChild(defaultLayoutNode); defaultLayoutNode = e; } else if( e.tagName() == "Layout") { if (!layoutNode.isNull()) docElem.removeChild(layoutNode); layoutNode = e; } n = n.nextSibling(); } } void VFolderMenu::pushDocInfo(const QString &fileName, const QString &baseDir) { m_docInfoStack.push(m_docInfo); if (!baseDir.isEmpty()) { if (!QDir::isRelativePath(baseDir)) m_docInfo.baseDir = KGlobal::dirs()->relativeLocation("xdgconf-menu", baseDir); else m_docInfo.baseDir = baseDir; } QString baseName = fileName; if (!QDir::isRelativePath(baseName)) registerFile(baseName); else baseName = m_docInfo.baseDir + baseName; m_docInfo.path = locateMenuFile(fileName); if (m_docInfo.path.isEmpty()) { m_docInfo.baseDir.clear(); m_docInfo.baseName.clear(); kDebug(7021) << "Menu" << fileName << "not found."; return; } int i; i = baseName.lastIndexOf('/'); if (i > 0) { m_docInfo.baseDir = baseName.left(i+1); m_docInfo.baseName = baseName.mid(i+1, baseName.length() - i - 6); } else { m_docInfo.baseDir.clear(); m_docInfo.baseName = baseName.left( baseName.length() - 5 ); } } void VFolderMenu::pushDocInfoParent(const QString &basePath, const QString &baseDir) { m_docInfoStack.push(m_docInfo); m_docInfo.baseDir = baseDir; QString fileName = basePath.mid(basePath.lastIndexOf('/')+1); m_docInfo.baseName = fileName.left( fileName.length() - 5 ); QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName); QStringList result = KGlobal::dirs()->findAllResources("xdgconf-menu", baseName); while( !result.isEmpty() && (result[0] != basePath)) result.erase(result.begin()); if (result.count() <= 1) { m_docInfo.path.clear(); // No parent found return; } m_docInfo.path = result[1]; } void VFolderMenu::popDocInfo() { m_docInfo = m_docInfoStack.pop(); } QString VFolderMenu::locateMenuFile(const QString &fileName) { if (!QDir::isRelativePath(fileName)) { if (KStandardDirs::exists(fileName)) return fileName; return QString(); } QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName); return KStandardDirs::locate("xdgconf-menu", baseName); } QString VFolderMenu::locateDirectoryFile(const QString &fileName) { if (fileName.isEmpty()) return QString(); if (!QDir::isRelativePath(fileName)) { if (KStandardDirs::exists(fileName)) return fileName; return QString(); } // First location in the list wins foreach(const QString &it, m_directoryDirs) { const QString tmp = it+fileName; if (KStandardDirs::exists(tmp)) return tmp; } return QString(); } void VFolderMenu::initDirs() { m_defaultDataDirs = KGlobal::dirs()->kfsstnd_prefixes().split(':', QString::SkipEmptyParts); const QString localDir = m_defaultDataDirs.first(); m_defaultDataDirs.removeAll(localDir); // Remove local dir m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps", QString()); m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs", QString()); } void VFolderMenu::loadMenu(const QString &fileName) { m_defaultMergeDirs.clear(); if (!fileName.endsWith(QLatin1String(".menu"))) return; pushDocInfo(fileName); m_defaultMergeDirs << m_docInfo.baseName+"-merged/"; m_doc = loadDoc(); popDocInfo(); if (m_doc.isNull()) { if (m_docInfo.path.isEmpty()) kError(7021) << fileName << " not found in " << m_allDirectories; else kWarning(7021) << "Load error (" << m_docInfo.path << ")"; return; } QDomElement e = m_doc.documentElement(); QString name; mergeMenus(e, name); } void VFolderMenu::processCondition(QDomElement &domElem, QHash& items) { if (domElem.tagName() == "And") { QDomNode n = domElem.firstChild(); // Look for the first child element while (!n.isNull()) // loop in case of comments { QDomElement e = n.toElement(); n = n.nextSibling(); if ( !e.isNull() ) { processCondition(e, items); break; // we only want the first one } } QHash andItems; while( !n.isNull() ) { QDomElement e = n.toElement(); if (e.tagName() == "Not") { // Special handling for "and not" QDomNode n2 = e.firstChild(); while( !n2.isNull() ) { QDomElement e2 = n2.toElement(); andItems.clear(); processCondition(e2, andItems); excludeItems(items, andItems); n2 = n2.nextSibling(); } } else { andItems.clear(); processCondition(e, andItems); matchItems(items, andItems); } n = n.nextSibling(); } } else if (domElem.tagName() == "Or") { QDomNode n = domElem.firstChild(); // Look for the first child element while (!n.isNull()) // loop in case of comments { QDomElement e = n.toElement(); n = n.nextSibling(); if ( !e.isNull() ) { processCondition(e, items); break; // we only want the first one } } QHash orItems; while( !n.isNull() ) { QDomElement e = n.toElement(); if ( !e.isNull() ) { orItems.clear(); processCondition(e, orItems); includeItems(items, orItems); } n = n.nextSibling(); } } else if (domElem.tagName() == "Not") { FOR_ALL_APPLICATIONS(it) { KService::Ptr s = it.value(); items.insert(s->menuId(), s); } FOR_ALL_APPLICATIONS_END QHash notItems; QDomNode n = domElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); if ( !e.isNull() ) { notItems.clear(); processCondition(e, notItems); excludeItems(items, notItems); } n = n.nextSibling(); } } else if (domElem.tagName() == "Category") { FOR_CATEGORY(domElem.text(), it) { KService::Ptr s = *it; items.insert(s->menuId(), s); } FOR_CATEGORY_END } else if (domElem.tagName() == "All") { FOR_ALL_APPLICATIONS(it) { KService::Ptr s = it.value(); items.insert(s->menuId(), s); } FOR_ALL_APPLICATIONS_END } else if (domElem.tagName() == "Filename") { const QString filename = domElem.text(); //kDebug(7021) << "Adding file" << filename; KService::Ptr s = findApplication(filename); if (s) items.insert(filename, s); } } void VFolderMenu::loadApplications(const QString &dir, const QString &prefix) { kDebug(7021) << "Looking up applications under" << dir; QDirIterator it(dir); while (it.hasNext()) { it.next(); const QFileInfo fi = it.fileInfo(); const QString fn = fi.fileName(); if (fi.isDir()) { if(fn == QLatin1String(".") || fn == QLatin1String("..")) continue; loadApplications(fi.filePath(), prefix + fn + '-'); continue; } if (fi.isFile()) { if (!fn.endsWith(QLatin1String(".desktop"))) continue; KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath()); if (service) addApplication(prefix + fn, service); } } } void VFolderMenu::processMenu(QDomElement &docElem, int pass) { SubMenu *parentMenu = m_currentMenu; int oldDirectoryDirsCount = m_directoryDirs.count(); QString name; QString directoryFile; bool onlyUnallocated = false; bool isDeleted = false; QDomElement defaultLayoutNode; QDomElement layoutNode; QDomElement query; QDomNode n = docElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == "Name") { name = e.text(); } else if (e.tagName() == "Directory") { directoryFile = e.text(); } else if (e.tagName() == "DirectoryDir") { QString dir = absoluteDir(e.text(), e.attribute("__BaseDir")); m_directoryDirs.prepend(dir); } else if (e.tagName() == "OnlyUnallocated") { onlyUnallocated = true; } else if (e.tagName() == "NotOnlyUnallocated") { onlyUnallocated = false; } else if (e.tagName() == "Deleted") { isDeleted = true; } else if (e.tagName() == "NotDeleted") { isDeleted = false; } else if (e.tagName() == "DefaultLayout") { defaultLayoutNode = e; } else if (e.tagName() == "Layout") { layoutNode = e; } n = n.nextSibling(); } // Setup current menu entry if (pass == 0) { m_currentMenu = 0; // Look up menu if (parentMenu) { foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == name) { m_currentMenu = menu; break; } } } if (!m_currentMenu) // Not found? { // Create menu m_currentMenu = new SubMenu; m_currentMenu->name = name; if (parentMenu) parentMenu->subMenus.append(m_currentMenu); else m_rootMenu = m_currentMenu; } if (directoryFile.isEmpty()) { kDebug(7021) << "Menu" << name << "does not specify a directory file."; } // Override previous directoryFile iff available QString tmp = locateDirectoryFile(directoryFile); if (! tmp.isEmpty()) m_currentMenu->directoryFile = tmp; m_currentMenu->isDeleted = isDeleted; m_currentMenu->defaultLayoutNode = defaultLayoutNode; m_currentMenu->layoutNode = layoutNode; } else { // Look up menu if (parentMenu) { foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == name) { m_currentMenu = menu; break; } } } else { m_currentMenu = m_rootMenu; } } // Process AppDir if (pass == 0) { QDomElement query; QDomNode n = docElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == "AppDir") { createAppsInfo(); QString dir = absoluteDir(e.text(), e.attribute("__BaseDir")); registerDirectory(dir); loadApplications(dir, QString()); } n = n.nextSibling(); } } loadAppsInfo(); // Update the scope wrt the list of applications if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated)) { n = docElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == "Include") { QHash items; QDomNode n2 = e.firstChild(); while( !n2.isNull() ) { QDomElement e2 = n2.toElement(); items.clear(); processCondition(e2, items); if (m_track) track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before "); includeItems(m_currentMenu->items, items); excludeItems(m_currentMenu->excludeItems, items); markUsedApplications(items); if (m_track) track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After "); n2 = n2.nextSibling(); } } else if (e.tagName() == "Exclude") { QHash items; QDomNode n2 = e.firstChild(); while( !n2.isNull() ) { QDomElement e2 = n2.toElement(); items.clear(); processCondition(e2, items); if (m_track) track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before "); excludeItems(m_currentMenu->items, items); includeItems(m_currentMenu->excludeItems, items); if (m_track) track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After "); n2 = n2.nextSibling(); } } n = n.nextSibling(); } } n = docElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == "Menu") { processMenu(e, pass); } n = n.nextSibling(); } if (pass == 2) { n = docElem.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == "Move") { QString orig; QString dest; QDomNode n2 = e.firstChild(); while( !n2.isNull() ) { QDomElement e2 = n2.toElement(); // try to convert the node to an element. if( e2.tagName() == "Old") orig = e2.text(); if( e2.tagName() == "New") dest = e2.text(); n2 = n2.nextSibling(); } kDebug(7021) << "Moving" << orig << "to" << dest; if (!orig.isEmpty() && !dest.isEmpty()) { SubMenu *menu = takeSubMenu(m_currentMenu, orig); if (menu) { insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority } } } n = n.nextSibling(); } } unloadAppsInfo(); // Update the scope wrt the list of applications while (m_directoryDirs.count() > oldDirectoryDirsCount) m_directoryDirs.pop_front(); m_currentMenu = parentMenu; } static QString parseAttribute( const QDomElement &e) { QString option; if ( e.hasAttribute( "show_empty" ) ) { QString str = e.attribute( "show_empty" ); if ( str=="true" ) option= "ME "; else if ( str=="false" ) option= "NME "; else kDebug()<<" Error in parsing show_empty attribute :"<defaultLayoutNode.isNull()) { defaultLayout = parseLayoutNode(menu->defaultLayoutNode); } if (menu->layoutNode.isNull()) { menu->layoutList = defaultLayout; } else { menu->layoutList = parseLayoutNode(menu->layoutNode); if (menu->layoutList.isEmpty()) menu->layoutList = defaultLayout; } foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus) { layoutMenu(subMenu, defaultLayout); } } void VFolderMenu::markUsedApplications(const QHash& items) { foreach(const KService::Ptr &p, items) m_usedAppsDict.insert(p->menuId()); } VFolderMenu::SubMenu * VFolderMenu::parseMenu(const QString &file) { m_appsInfo = 0; const QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu"); foreach(const QString &it, dirs) { registerDirectory(it); } loadMenu(file); delete m_rootMenu; m_rootMenu = m_currentMenu = 0; QDomElement docElem = m_doc.documentElement(); for (int pass = 0; pass <= 2; pass++) { // pass 0: load application desktop files // pass 1: the normal processing // pass 2: process to put unused files into "Lost & Found". processMenu(docElem, pass); switch (pass) { case 0: // Fill the dictCategories for each AppsInfo in m_appsInfoList, // in preparation for processMenu pass 1. buildApplicationIndex(false); break; case 1: // Fill the dictCategories for each AppsInfo in m_appsInfoList, // with only the unused apps, in preparation for processMenu pass 2. buildApplicationIndex(true /* unusedOnly */); break; case 2: { QStringList defaultLayout; defaultLayout << ":M"; // Sub-Menus defaultLayout << ":F"; // Individual entries layoutMenu(m_rootMenu, defaultLayout); break; } default: break; } } return m_rootMenu; } void VFolderMenu::setTrackId(const QString &id) { m_track = !id.isEmpty(); m_trackId = id; } #include "moc_vfolder_menu.cpp"