• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDED

vfolder_menu.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
00003  *
00004  *  This library is free software; you can redistribute it and/or
00005  *  modify it under the terms of the GNU Library General Public
00006  *  License version 2 as published by the Free Software Foundation;
00007  *
00008  *  This library is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  *  Library General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU Library General Public License
00014  *  along with this library; see the file COPYING.LIB.  If not, write to
00015  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016  *  Boston, MA 02110-1301, USA.
00017  **/
00018 
00019 #include "vfolder_menu.h"
00020 #include "kbuildservicefactory.h"
00021 
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <unistd.h>
00025 #include <dirent.h>
00026 #include <config.h>
00027 
00028 #include <kdebug.h>
00029 #include <kglobal.h>
00030 #include <kstandarddirs.h>
00031 #include <kservice.h>
00032 #include <kde_file.h>
00033 
00034 #include <QtCore/QMap>
00035 #include <QtCore/QFile>
00036 #include <QtCore/QDir>
00037 #include <QtCore/QRegExp>
00038 
00039 static void foldNode(QDomElement &docElem, QDomElement &e, QMap<QString,QDomElement> &dupeList, QString s=QString()) //krazy:exclude=passbyvalue
00040 {
00041    if (s.isEmpty())
00042       s = e.text();
00043    QMap<QString,QDomElement>::iterator it = dupeList.find(s);
00044    if (it != dupeList.end())
00045    {
00046       kDebug(7021) << e.tagName() << "and" << s << "requires combining!";
00047 
00048       docElem.removeChild(*it);
00049       dupeList.erase(it);
00050    }
00051    dupeList.insert(s, e);
00052 }
00053 
00054 static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag)
00055 {
00056    for(QStringList::ConstIterator it = list.begin();
00057        it != list.end(); ++it)
00058    {
00059       QDomElement e = docElem.ownerDocument().createElement(tag);
00060       QDomText txt = docElem.ownerDocument().createTextNode(*it);
00061       e.appendChild(txt);
00062       docElem.insertAfter(e, n);
00063    }
00064 
00065    QDomNode next = n.nextSibling();
00066    docElem.removeChild(n);
00067    n = next;
00068 //   kDebug(7021) << "Next tag = " << n.toElement().tagName();
00069 }
00070 
00071 void VFolderMenu::registerFile(const QString &file)
00072 {
00073    int i = file.lastIndexOf('/');
00074    if (i < 0)
00075       return;
00076 
00077    QString dir = file.left(i+1); // Include trailing '/'
00078    registerDirectory(dir);
00079 }
00080 
00081 void VFolderMenu::registerDirectory(const QString &directory)
00082 {
00083    m_allDirectories.append(directory);
00084 }
00085 
00086 QStringList VFolderMenu::allDirectories()
00087 {
00088    if (m_allDirectories.isEmpty())
00089      return m_allDirectories;
00090    m_allDirectories.sort();
00091 
00092    QStringList::Iterator it = m_allDirectories.begin();
00093    QString previous = *it++;
00094    for(;it != m_allDirectories.end();)
00095    {
00096      if ((*it).startsWith(previous))
00097      {
00098         it = m_allDirectories.erase(it);
00099      }
00100      else
00101      {
00102         previous = *it;
00103         ++it;
00104      }
00105    }
00106    return m_allDirectories;
00107 }
00108 
00109 static void
00110 track(const QString &menuId, const QString &menuName, const QHash<QString,KService::Ptr>& includeList, const QHash<QString,KService::Ptr>& excludeList, const QHash<QString,KService::Ptr>& itemList, const QString &comment)
00111 {
00112    if (itemList.contains(menuId))
00113       printf("%s: %s INCL %d EXCL %d\n", qPrintable(menuName), qPrintable(comment), includeList.contains(menuId) ? 1 : 0, excludeList.contains(menuId) ? 1 : 0);
00114 }
00115 
00116 void
00117 VFolderMenu::includeItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
00118 {
00119    foreach (const KService::Ptr &p, items2) {
00120        items1.insert(p->menuId(), p);
00121    }
00122 }
00123 
00124 void
00125 VFolderMenu::matchItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
00126 {
00127    foreach (const KService::Ptr &p, items1)
00128    {
00129        QString id = p->menuId();
00130        if (!items2.contains(id))
00131           items1.remove(id);
00132    }
00133 }
00134 
00135 void
00136 VFolderMenu::excludeItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
00137 {
00138    foreach (const KService::Ptr &p, items2)
00139        items1.remove(p->menuId());
00140 }
00141 
00142 VFolderMenu::SubMenu*
00143 VFolderMenu::takeSubMenu(SubMenu *parentMenu, const QString &menuName)
00144 {
00145    const int i = menuName.indexOf('/');
00146    const QString s1 = i > 0 ? menuName.left(i) : menuName;
00147    const QString s2 = menuName.mid(i+1);
00148 
00149    // Look up menu
00150    for (QList<SubMenu*>::Iterator it = parentMenu->subMenus.begin(); it != parentMenu->subMenus.end(); ++it)
00151    {
00152       SubMenu* menu = *it;
00153       if (menu->name == s1)
00154       {
00155          if (i == -1)
00156          {
00157             // Take it out
00158             parentMenu->subMenus.erase(it);
00159             return menu;
00160          }
00161          else
00162          {
00163             return takeSubMenu(menu, s2);
00164          }
00165       }
00166    }
00167    return 0; // Not found
00168 }
00169 
00170 void
00171 VFolderMenu::mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority)
00172 {
00173    if (m_track)
00174    {
00175       track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("Before MenuMerge w. %1 (incl)").arg(menu2->name));
00176       track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("Before MenuMerge w. %1 (excl)").arg(menu2->name));
00177    }
00178    if (reversePriority)
00179    {
00180       // Merge menu1 with menu2, menu1 takes precedent
00181       excludeItems(menu2->items, menu1->excludeItems);
00182       includeItems(menu1->items, menu2->items);
00183       excludeItems(menu2->excludeItems, menu1->items);
00184       includeItems(menu1->excludeItems, menu2->excludeItems);
00185    }
00186    else
00187    {
00188       // Merge menu1 with menu2, menu2 takes precedent
00189       excludeItems(menu1->items, menu2->excludeItems);
00190       includeItems(menu1->items, menu2->items);
00191       includeItems(menu1->excludeItems, menu2->excludeItems);
00192       menu1->isDeleted = menu2->isDeleted;
00193    }
00194    while (!menu2->subMenus.isEmpty())
00195    {
00196       SubMenu *subMenu = menu2->subMenus.takeFirst();
00197       insertSubMenu(menu1, subMenu->name, subMenu, reversePriority);
00198    }
00199 
00200    if (reversePriority)
00201    {
00202       // Merge menu1 with menu2, menu1 takes precedent
00203       if (menu1->directoryFile.isEmpty())
00204          menu1->directoryFile = menu2->directoryFile;
00205       if (menu1->defaultLayoutNode.isNull())
00206          menu1->defaultLayoutNode = menu2->defaultLayoutNode;
00207       if (menu1->layoutNode.isNull())
00208          menu1->layoutNode = menu2->layoutNode;
00209    }
00210    else
00211    {
00212       // Merge menu1 with menu2, menu2 takes precedent
00213       if (!menu2->directoryFile.isEmpty())
00214          menu1->directoryFile = menu2->directoryFile;
00215       if (!menu2->defaultLayoutNode.isNull())
00216          menu1->defaultLayoutNode = menu2->defaultLayoutNode;
00217       if (!menu2->layoutNode.isNull())
00218          menu1->layoutNode = menu2->layoutNode;
00219    }
00220 
00221    if (m_track)
00222    {
00223       track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("After MenuMerge w. %1 (incl)").arg(menu2->name));
00224       track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("After MenuMerge w. %1 (excl)").arg(menu2->name));
00225    }
00226 
00227    delete menu2;
00228 }
00229 
00230 void
00231 VFolderMenu::insertSubMenu(SubMenu *parentMenu, const QString &menuName, SubMenu *newMenu, bool reversePriority)
00232 {
00233    const int i = menuName.indexOf('/');
00234    const QString s1 = menuName.left(i);
00235    const QString s2 = menuName.mid(i+1);
00236 
00237    // Look up menu
00238    foreach (SubMenu *menu, parentMenu->subMenus)
00239    {
00240       if (menu->name == s1)
00241       {
00242          if (i == -1)
00243          {
00244             mergeMenu(menu, newMenu, reversePriority);
00245             return;
00246          }
00247          else
00248          {
00249             insertSubMenu(menu, s2, newMenu, reversePriority);
00250             return;
00251          }
00252       }
00253    }
00254    if (i == -1)
00255    {
00256      // Add it here
00257      newMenu->name = menuName;
00258      parentMenu->subMenus.append(newMenu);
00259    }
00260    else
00261    {
00262      SubMenu *menu = new SubMenu;
00263      menu->name = s1;
00264      parentMenu->subMenus.append(menu);
00265      insertSubMenu(menu, s2, newMenu);
00266    }
00267 }
00268 
00269 void
00270 VFolderMenu::insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService)
00271 {
00272    const int i = name.indexOf('/');
00273 
00274    if (i == -1)
00275    {
00276      // Add it here
00277      parentMenu->items.insert(newService->menuId(), newService);
00278      return;
00279    }
00280 
00281    QString s1 = name.left(i);
00282    QString s2 = name.mid(i+1);
00283 
00284    // Look up menu
00285    foreach (SubMenu *menu, parentMenu->subMenus)
00286    {
00287       if (menu->name == s1)
00288       {
00289          insertService(menu, s2, newService);
00290          return;
00291       }
00292    }
00293 
00294    SubMenu *menu = new SubMenu;
00295    menu->name = s1;
00296    parentMenu->subMenus.append(menu);
00297    insertService(menu, s2, newService);
00298 }
00299 
00300 
00301 VFolderMenu::VFolderMenu(KBuildServiceFactory* serviceFactory) :
00302     m_track(false),
00303     m_serviceFactory(serviceFactory)
00304 {
00305    m_usedAppsDict.reserve(797);
00306    m_rootMenu = 0;
00307    initDirs();
00308 }
00309 
00310 VFolderMenu::~VFolderMenu()
00311 {
00312    delete m_rootMenu;
00313    delete m_appsInfo;
00314 }
00315 
00316 #define FOR_ALL_APPLICATIONS(it) \
00317    foreach (AppsInfo *info, m_appsInfoStack) \
00318    { \
00319       QHashIterator<QString,KService::Ptr> it = info->applications; \
00320       while (it.hasNext()) \
00321       { \
00322          it.next();
00323 #define FOR_ALL_APPLICATIONS_END } }
00324 
00325 #define FOR_CATEGORY(category, it) \
00326    foreach (AppsInfo *info, m_appsInfoStack) \
00327    { \
00328       const KService::List list = info->dictCategories.value(category); \
00329       for(KService::List::ConstIterator it = list.constBegin(); \
00330              it != list.constEnd(); ++it) \
00331       {
00332 #define FOR_CATEGORY_END } }
00333 
00334 KService::Ptr
00335 VFolderMenu::findApplication(const QString &relPath)
00336 {
00337    foreach(AppsInfo *info, m_appsInfoStack)
00338    {
00339       if (info->applications.contains(relPath)) {
00340          KService::Ptr s = info->applications[relPath];
00341          if (s)
00342             return s;
00343       }
00344    }
00345    return KService::Ptr();
00346 }
00347 
00348 void
00349 VFolderMenu::addApplication(const QString &id, KService::Ptr service)
00350 {
00351    service->setMenuId(id);
00352    m_appsInfo->applications.insert(id, service);
00353    m_serviceFactory->addEntry(KSycocaEntry::Ptr::staticCast(service));
00354 }
00355 
00356 void
00357 VFolderMenu::buildApplicationIndex(bool unusedOnly)
00358 {
00359    foreach (AppsInfo *info, m_appsInfoList)
00360    {
00361       info->dictCategories.clear();
00362       QMutableHashIterator<QString,KService::Ptr> it = info->applications;
00363       while (it.hasNext())
00364       {
00365          KService::Ptr s = it.next().value();
00366          if (unusedOnly && m_usedAppsDict.contains(s->menuId()))
00367          {
00368             // Remove and skip this one
00369             it.remove();
00370             continue;
00371          }
00372 
00373          const QStringList cats = s->categories();
00374          for(QStringList::ConstIterator it2 = cats.begin();
00375              it2 != cats.end(); ++it2)
00376          {
00377             const QString &cat = *it2;
00378             info->dictCategories[cat].append(s); // find or insert entry in hash
00379          }
00380       }
00381    }
00382 }
00383 
00384 void
00385 VFolderMenu::createAppsInfo()
00386 {
00387    if (m_appsInfo) return;
00388 
00389    m_appsInfo = new AppsInfo;
00390    m_appsInfoStack.prepend(m_appsInfo);
00391    m_appsInfoList.append(m_appsInfo);
00392    m_currentMenu->apps_info = m_appsInfo;
00393 }
00394 
00395 void
00396 VFolderMenu::loadAppsInfo()
00397 {
00398    m_appsInfo = m_currentMenu->apps_info;
00399    if (!m_appsInfo)
00400       return; // No appsInfo for this menu
00401 
00402    if (m_appsInfoStack.count() && m_appsInfoStack.first() == m_appsInfo)
00403       return; // Already added (By createAppsInfo?)
00404 
00405    m_appsInfoStack.prepend(m_appsInfo); // Add
00406 }
00407 
00408 void
00409 VFolderMenu::unloadAppsInfo()
00410 {
00411    m_appsInfo = m_currentMenu->apps_info;
00412    if (!m_appsInfo)
00413       return; // No appsInfo for this menu
00414 
00415    if (m_appsInfoStack.first() != m_appsInfo)
00416    {
00417       return; // Already removed (huh?)
00418    }
00419 
00420    m_appsInfoStack.removeAll(m_appsInfo); // Remove
00421    m_appsInfo = 0;
00422 }
00423 
00424 QString
00425 VFolderMenu::absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg)
00426 {
00427    QString dir = _dir;
00428    if (QDir::isRelativePath(dir))
00429    {
00430       dir = baseDir + dir;
00431    }
00432    if (!dir.endsWith('/'))
00433       dir += '/';
00434 
00435    if (QDir::isRelativePath(dir) && !keepRelativeToCfg)
00436    {
00437       dir = KGlobal::dirs()->findResource("xdgconf-menu", dir);
00438    }
00439 
00440    dir = KGlobal::dirs()->realPath(dir);
00441 
00442    return dir;
00443 }
00444 
00445 static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir)
00446 {
00447    QDomNodeList mergeFileList = doc.elementsByTagName(tag);
00448    for(int i = 0; i < (int)mergeFileList.count(); i++)
00449    {
00450       QDomAttr attr = doc.createAttribute("__BaseDir");
00451       attr.setValue(dir);
00452       mergeFileList.item(i).toElement().setAttributeNode(attr);
00453    }
00454 }
00455 
00456 static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path)
00457 {
00458    QDomNodeList mergeFileList = doc.elementsByTagName(tag);
00459    for(int i = 0; i < (int)mergeFileList.count(); i++)
00460    {
00461       QDomAttr attr = doc.createAttribute("__BasePath");
00462       attr.setValue(path);
00463       mergeFileList.item(i).toElement().setAttributeNode(attr);
00464    }
00465 }
00466 
00467 QDomDocument
00468 VFolderMenu::loadDoc()
00469 {
00470    QDomDocument doc;
00471    if ( m_docInfo.path.isEmpty() )
00472    {
00473       return doc;
00474    }
00475    QFile file( m_docInfo.path );
00476    if ( !file.open( QIODevice::ReadOnly ) )
00477    {
00478       kWarning(7021) << "Could not open " << m_docInfo.path;
00479       return doc;
00480    }
00481    QString errorMsg;
00482    int errorRow;
00483    int errorCol;
00484    if ( !doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) {
00485       kWarning(7021) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg;
00486       file.close();
00487       return doc;
00488    }
00489    file.close();
00490 
00491    tagBaseDir(doc, "MergeFile", m_docInfo.baseDir);
00492    tagBasePath(doc, "MergeFile", m_docInfo.path);
00493    tagBaseDir(doc, "MergeDir", m_docInfo.baseDir);
00494    tagBaseDir(doc, "DirectoryDir", m_docInfo.baseDir);
00495    tagBaseDir(doc, "AppDir", m_docInfo.baseDir);
00496    tagBaseDir(doc, "LegacyDir", m_docInfo.baseDir);
00497 
00498    return doc;
00499 }
00500 
00501 
00502 void
00503 VFolderMenu::mergeFile(QDomElement &parent, const QDomNode &mergeHere)
00504 {
00505 kDebug(7021) << "VFolderMenu::mergeFile:" << m_docInfo.path;
00506    QDomDocument doc = loadDoc();
00507 
00508    QDomElement docElem = doc.documentElement();
00509    QDomNode n = docElem.firstChild();
00510    QDomNode last = mergeHere;
00511    while( !n.isNull() )
00512    {
00513       QDomElement e = n.toElement(); // try to convert the node to an element.
00514       QDomNode next = n.nextSibling();
00515 
00516       if (e.isNull())
00517       {
00518          // Skip
00519       }
00520       // The spec says we must ignore any Name nodes
00521       else if (e.tagName() != "Name")
00522       {
00523          parent.insertAfter(n, last);
00524          last = n;
00525       }
00526 
00527       docElem.removeChild(n);
00528       n = next;
00529    }
00530 }
00531 
00532 
00533 void
00534 VFolderMenu::mergeMenus(QDomElement &docElem, QString &name)
00535 {
00536    QMap<QString,QDomElement> menuNodes;
00537    QMap<QString,QDomElement> directoryNodes;
00538    QMap<QString,QDomElement> appDirNodes;
00539    QMap<QString,QDomElement> directoryDirNodes;
00540    QMap<QString,QDomElement> legacyDirNodes;
00541    QDomElement defaultLayoutNode;
00542    QDomElement layoutNode;
00543 
00544    QDomNode n = docElem.firstChild();
00545    while( !n.isNull() ) {
00546       QDomElement e = n.toElement(); // try to convert the node to an element.
00547       if( e.isNull() ) {
00548 // kDebug(7021) << "Empty node";
00549       }
00550       else if( e.tagName() == "DefaultAppDirs") {
00551          // Replace with m_defaultAppDirs
00552          replaceNode(docElem, n, m_defaultAppDirs, "AppDir");
00553          continue;
00554       }
00555       else if( e.tagName() == "DefaultDirectoryDirs") {
00556          // Replace with m_defaultDirectoryDirs
00557          replaceNode(docElem, n, m_defaultDirectoryDirs, "DirectoryDir");
00558          continue;
00559       }
00560       else if( e.tagName() == "DefaultMergeDirs") {
00561          // Replace with m_defaultMergeDirs
00562          replaceNode(docElem, n, m_defaultMergeDirs, "MergeDir");
00563          continue;
00564       }
00565       else if( e.tagName() == "AppDir") {
00566          // Filter out dupes
00567          foldNode(docElem, e, appDirNodes);
00568       }
00569       else if( e.tagName() == "DirectoryDir") {
00570          // Filter out dupes
00571          foldNode(docElem, e, directoryDirNodes);
00572       }
00573       else if( e.tagName() == "LegacyDir") {
00574          // Filter out dupes
00575          foldNode(docElem, e, legacyDirNodes);
00576       }
00577       else if( e.tagName() == "Directory") {
00578          // Filter out dupes
00579          foldNode(docElem, e, directoryNodes);
00580       }
00581       else if( e.tagName() == "Move") {
00582          // Filter out dupes
00583          QString orig;
00584          QDomNode n2 = e.firstChild();
00585          while( !n2.isNull() ) {
00586             QDomElement e2 = n2.toElement(); // try to convert the node to an element.
00587             if( e2.tagName() == "Old")
00588             {
00589                orig = e2.text();
00590                break;
00591             }
00592             n2 = n2.nextSibling();
00593          }
00594          foldNode(docElem, e, appDirNodes, orig);
00595       }
00596       else if( e.tagName() == "Menu") {
00597          QString name;
00598          mergeMenus(e, name);
00599          QMap<QString,QDomElement>::iterator it = menuNodes.find(name);
00600          if (it != menuNodes.end())
00601          {
00602            QDomElement docElem2 = *it;
00603            QDomNode n2 = docElem2.firstChild();
00604            QDomNode first = e.firstChild();
00605            while( !n2.isNull() ) {
00606              QDomElement e2 = n2.toElement(); // try to convert the node to an element.
00607              QDomNode n3 = n2.nextSibling();
00608              e.insertBefore(n2, first);
00609              docElem2.removeChild(n2);
00610              n2 = n3;
00611            }
00612            // We still have duplicated Name entries
00613            // but we don't care about that
00614 
00615            docElem.removeChild(docElem2);
00616            menuNodes.erase(it);
00617          }
00618          menuNodes.insert(name, e);
00619       }
00620       else if( e.tagName() == "MergeFile") {
00621          if ((e.attribute("type") == "parent"))
00622             pushDocInfoParent(e.attribute("__BasePath"), e.attribute("__BaseDir"));
00623          else
00624             pushDocInfo(e.text(), e.attribute("__BaseDir"));
00625 
00626          if (!m_docInfo.path.isEmpty())
00627             mergeFile(docElem, n);
00628          popDocInfo();
00629 
00630          QDomNode last = n;
00631          n = n.nextSibling();
00632          docElem.removeChild(last); // Remove the MergeFile node
00633          continue;
00634       }
00635       else if( e.tagName() == "MergeDir") {
00636          QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"), true);
00637 
00638          const QStringList dirs = KGlobal::dirs()->findDirs("xdgconf-menu", dir);
00639          for(QStringList::ConstIterator it=dirs.begin();
00640              it != dirs.end(); ++it)
00641          {
00642             registerDirectory(*it);
00643          }
00644 
00645          QStringList fileList;
00646          if (!QDir::isRelativePath(dir))
00647          {
00648             // Absolute
00649             fileList = KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu");
00650          }
00651          else
00652          {
00653             // Relative
00654             (void) KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu",
00655                                                      KStandardDirs::NoDuplicates, fileList);
00656          }
00657 
00658          for(QStringList::ConstIterator it=fileList.constBegin();
00659              it != fileList.constEnd(); ++it)
00660          {
00661             pushDocInfo(*it);
00662             mergeFile(docElem, n);
00663             popDocInfo();
00664          }
00665 
00666          QDomNode last = n;
00667          n = n.nextSibling();
00668          docElem.removeChild(last); // Remove the MergeDir node
00669 
00670          continue;
00671       }
00672       else if( e.tagName() == "Name") {
00673          name = e.text();
00674       }
00675       else if( e.tagName() == "DefaultLayout") {
00676          if (!defaultLayoutNode.isNull())
00677             docElem.removeChild(defaultLayoutNode);
00678          defaultLayoutNode = e;
00679       }
00680       else if( e.tagName() == "Layout") {
00681          if (!layoutNode.isNull())
00682             docElem.removeChild(layoutNode);
00683          layoutNode = e;
00684       }
00685       n = n.nextSibling();
00686    }
00687 }
00688 
00689 void
00690 VFolderMenu::pushDocInfo(const QString &fileName, const QString &baseDir)
00691 {
00692    m_docInfoStack.push(m_docInfo);
00693    if (!baseDir.isEmpty())
00694    {
00695       if (!QDir::isRelativePath(baseDir))
00696          m_docInfo.baseDir = KGlobal::dirs()->relativeLocation("xdgconf-menu", baseDir);
00697       else
00698          m_docInfo.baseDir = baseDir;
00699    }
00700 
00701    QString baseName = fileName;
00702    if (!QDir::isRelativePath(baseName))
00703       registerFile(baseName);
00704    else
00705       baseName = m_docInfo.baseDir + baseName;
00706 
00707    m_docInfo.path = locateMenuFile(fileName);
00708    if (m_docInfo.path.isEmpty())
00709    {
00710       m_docInfo.baseDir.clear();
00711       m_docInfo.baseName.clear();
00712       kDebug(7021) << "Menu" << fileName << "not found.";
00713       return;
00714    }
00715    int i;
00716    i = baseName.lastIndexOf('/');
00717    if (i > 0)
00718    {
00719       m_docInfo.baseDir = baseName.left(i+1);
00720       m_docInfo.baseName = baseName.mid(i+1, baseName.length() - i - 6);
00721    }
00722    else
00723    {
00724       m_docInfo.baseDir.clear();
00725       m_docInfo.baseName = baseName.left( baseName.length() - 5 );
00726    }
00727 }
00728 
00729 void
00730 VFolderMenu::pushDocInfoParent(const QString &basePath, const QString &baseDir)
00731 {
00732     m_docInfoStack.push(m_docInfo);
00733 
00734    m_docInfo.baseDir = baseDir;
00735 
00736    QString fileName = basePath.mid(basePath.lastIndexOf('/')+1);
00737    m_docInfo.baseName = fileName.left( fileName.length() - 5 );
00738    QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName);
00739 
00740    QStringList result = KGlobal::dirs()->findAllResources("xdgconf-menu", baseName);
00741 
00742    while( !result.isEmpty() && (result[0] != basePath))
00743       result.erase(result.begin());
00744 
00745    if (result.count() <= 1)
00746    {
00747       m_docInfo.path.clear(); // No parent found
00748       return;
00749    }
00750    m_docInfo.path = result[1];
00751 }
00752 
00753 void
00754 VFolderMenu::popDocInfo()
00755 {
00756    m_docInfo = m_docInfoStack.pop();
00757 }
00758 
00759 QString
00760 VFolderMenu::locateMenuFile(const QString &fileName)
00761 {
00762    if (!QDir::isRelativePath(fileName))
00763    {
00764       if (KStandardDirs::exists(fileName))
00765          return fileName;
00766       return QString();
00767    }
00768 
00769    QString result;
00770 
00771    QString xdgMenuPrefix = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX"));
00772    if (!xdgMenuPrefix.isEmpty())
00773    {
00774       QFileInfo fileInfo(fileName);
00775 
00776       QString fileNameOnly = fileInfo.fileName();
00777       if (!fileNameOnly.startsWith(xdgMenuPrefix))
00778          fileNameOnly = xdgMenuPrefix + fileNameOnly;
00779 
00780       QString baseName = QDir::cleanPath(m_docInfo.baseDir +
00781                                          fileInfo.path() + '/' + fileNameOnly);
00782       result = KStandardDirs::locate("xdgconf-menu", baseName);
00783    }
00784 
00785    if (result.isEmpty())
00786    {
00787        QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName);
00788        result = KStandardDirs::locate("xdgconf-menu", baseName);
00789    }
00790 
00791    return result;
00792 }
00793 
00794 QString
00795 VFolderMenu::locateDirectoryFile(const QString &fileName)
00796 {
00797    if (fileName.isEmpty())
00798       return QString();
00799 
00800    if (!QDir::isRelativePath(fileName))
00801    {
00802       if (KStandardDirs::exists(fileName))
00803          return fileName;
00804       return QString();
00805    }
00806 
00807    // First location in the list wins
00808    for(QStringList::ConstIterator it = m_directoryDirs.constBegin();
00809        it != m_directoryDirs.constEnd();
00810        ++it)
00811    {
00812       QString tmp = (*it)+fileName;
00813       if (KStandardDirs::exists(tmp))
00814          return tmp;
00815    }
00816 
00817    return QString();
00818 }
00819 
00820 void
00821 VFolderMenu::initDirs()
00822 {
00823    m_defaultDataDirs = KGlobal::dirs()->kfsstnd_prefixes().split(':', QString::SkipEmptyParts);
00824    const QString localDir = m_defaultDataDirs.first();
00825    m_defaultDataDirs.removeAll(localDir); // Remove local dir
00826 
00827    m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps", QString());
00828    m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs", QString());
00829    m_defaultLegacyDirs = KGlobal::dirs()->resourceDirs("apps");
00830 }
00831 
00832 void
00833 VFolderMenu::loadMenu(const QString &fileName)
00834 {
00835    m_defaultMergeDirs.clear();
00836 
00837    if (!fileName.endsWith(".menu"))
00838       return;
00839 
00840    pushDocInfo(fileName);
00841    m_defaultMergeDirs << m_docInfo.baseName+"-merged/";
00842    m_doc = loadDoc();
00843    popDocInfo();
00844 
00845    if (m_doc.isNull())
00846    {
00847       if (m_docInfo.path.isEmpty())
00848          kError(7021) << fileName << " not found in " << m_allDirectories << endl;
00849       else
00850          kWarning(7021) << "Load error (" << m_docInfo.path << ")";
00851       return;
00852    }
00853 
00854    QDomElement e = m_doc.documentElement();
00855    QString name;
00856    mergeMenus(e, name);
00857 }
00858 
00859 void
00860 VFolderMenu::processCondition(QDomElement &domElem, QHash<QString,KService::Ptr>& items)
00861 {
00862    if (domElem.tagName() == "And")
00863    {
00864       QDomNode n = domElem.firstChild();
00865       // Look for the first child element
00866       while (!n.isNull()) // loop in case of comments
00867       {
00868          QDomElement e = n.toElement();
00869          n = n.nextSibling();
00870          if ( !e.isNull() ) {
00871              processCondition(e, items);
00872              break; // we only want the first one
00873          }
00874       }
00875 
00876       QHash<QString,KService::Ptr> andItems;
00877       while( !n.isNull() ) {
00878          QDomElement e = n.toElement();
00879          if (e.tagName() == "Not")
00880          {
00881             // Special handling for "and not"
00882             QDomNode n2 = e.firstChild();
00883             while( !n2.isNull() ) {
00884                QDomElement e2 = n2.toElement();
00885                andItems.clear();
00886                processCondition(e2, andItems);
00887                excludeItems(items, andItems);
00888                n2 = n2.nextSibling();
00889             }
00890          }
00891          else
00892          {
00893             andItems.clear();
00894             processCondition(e, andItems);
00895             matchItems(items, andItems);
00896          }
00897          n = n.nextSibling();
00898       }
00899    }
00900    else if (domElem.tagName() == "Or")
00901    {
00902       QDomNode n = domElem.firstChild();
00903       // Look for the first child element
00904       while (!n.isNull()) // loop in case of comments
00905       {
00906          QDomElement e = n.toElement();
00907          n = n.nextSibling();
00908          if ( !e.isNull() ) {
00909              processCondition(e, items);
00910              break; // we only want the first one
00911          }
00912       }
00913 
00914       QHash<QString,KService::Ptr> orItems;
00915       while( !n.isNull() ) {
00916          QDomElement e = n.toElement();
00917          if ( !e.isNull() ) {
00918              orItems.clear();
00919              processCondition(e, orItems);
00920              includeItems(items, orItems);
00921          }
00922          n = n.nextSibling();
00923       }
00924    }
00925    else if (domElem.tagName() == "Not")
00926    {
00927       FOR_ALL_APPLICATIONS(it)
00928       {
00929          KService::Ptr s = it.value();
00930          items.insert(s->menuId(), s);
00931       }
00932       FOR_ALL_APPLICATIONS_END
00933 
00934       QHash<QString,KService::Ptr> notItems;
00935       QDomNode n = domElem.firstChild();
00936       while( !n.isNull() ) {
00937          QDomElement e = n.toElement();
00938          if ( !e.isNull() ) {
00939              notItems.clear();
00940              processCondition(e, notItems);
00941              excludeItems(items, notItems);
00942          }
00943          n = n.nextSibling();
00944       }
00945    }
00946    else if (domElem.tagName() == "Category")
00947    {
00948       FOR_CATEGORY(domElem.text(), it)
00949       {
00950          KService::Ptr s = *it;
00951          items.insert(s->menuId(), s);
00952       }
00953       FOR_CATEGORY_END
00954    }
00955    else if (domElem.tagName() == "All")
00956    {
00957       FOR_ALL_APPLICATIONS(it)
00958       {
00959          KService::Ptr s = it.value();
00960          items.insert(s->menuId(), s);
00961       }
00962       FOR_ALL_APPLICATIONS_END
00963    }
00964    else if (domElem.tagName() == "Filename")
00965    {
00966       QString filename = domElem.text();
00967 kDebug(7021) << "Adding file" << filename;
00968       KService::Ptr s = findApplication(filename);
00969       if (s)
00970          items.insert(filename, s);
00971    }
00972 }
00973 
00974 void
00975 VFolderMenu::loadApplications(const QString &dir, const QString &prefix)
00976 {
00977    kDebug(7021) << "Looking up applications under" << dir;
00978 
00979    // We look for a set of files.
00980    DIR *dp = opendir( QFile::encodeName(dir));
00981    if (!dp)
00982       return;
00983 
00984    KDE_struct_dirent *ep;
00985 
00986    while( ( ep = KDE_readdir( dp ) ) != 0L )
00987    {
00988       QString fn( QFile::decodeName(ep->d_name));
00989       if (fn == "." || fn == ".." || fn.at(fn.length() - 1) == '~')
00990          continue;
00991 
00992       bool isDir;
00993       bool isReg;
00994       QString pathfn = dir + fn;
00995 
00996 #ifdef HAVE_DIRENT_D_TYPE
00997       isDir = ep->d_type == DT_DIR;
00998       isReg = ep->d_type == DT_REG;
00999 
01000       if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
01001 #endif
01002       {
01003         KDE_struct_stat buff;
01004         if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
01005            continue; // Couldn't stat (e.g. no read permissions)
01006         }
01007         isDir = S_ISDIR ( buff.st_mode );
01008         isReg = S_ISREG ( buff.st_mode );
01009       }
01010       if (isDir) {
01011          loadApplications(pathfn + '/', prefix + fn + '-');
01012          continue;
01013       }
01014 
01015       if (isReg)
01016       {
01017          if (!fn.endsWith(".desktop"))
01018             continue;
01019 
01020          KService::Ptr service;
01021          emit newService(pathfn, &service); // calls KBuildSycoca::slotCreateEntry
01022          if (service)
01023             addApplication(prefix+fn, service);
01024       }
01025     }
01026     closedir( dp );
01027 }
01028 
01029 void
01030 VFolderMenu::processKDELegacyDirs()
01031 {
01032 kDebug(7021) << "processKDELegacyDirs()";
01033 
01034    QHash<QString,KService::Ptr> items;
01035    QString prefix = "kde4-";
01036 
01037    QStringList relFiles;
01038 
01039    (void) KGlobal::dirs()->findAllResources( "apps",
01040                                              QString(),
01041                                              KStandardDirs::Recursive |
01042                                              KStandardDirs::NoDuplicates,
01043                                              relFiles);
01044    for(QStringList::ConstIterator it = relFiles.constBegin();
01045        it != relFiles.constEnd(); ++it)
01046    {
01047       if (!m_forcedLegacyLoad && (*it).endsWith(".directory"))
01048       {
01049          QString name = *it;
01050          if (!name.endsWith("/.directory"))
01051             continue; // Probably ".directory", skip it.
01052 
01053          name = name.left(name.length()-11);
01054 
01055          SubMenu *newMenu = new SubMenu;
01056          newMenu->directoryFile = KStandardDirs::locate("apps", *it);
01057 
01058          insertSubMenu(m_currentMenu, name, newMenu);
01059          continue;
01060       }
01061 
01062       if ((*it).endsWith(".desktop"))
01063       {
01064          QString name = *it;
01065          KService::Ptr service;
01066          emit newService(name, &service);
01067 
01068          if (service && !m_forcedLegacyLoad)
01069          {
01070             QString id = name;
01071             // Strip path from id
01072             int i = id.lastIndexOf('/');
01073             if (i >= 0)
01074                id = id.mid(i+1);
01075 
01076             id.prepend(prefix);
01077 
01078             // TODO: add Legacy category
01079             addApplication(id, service);
01080             items.insert(service->menuId(), service);
01081             if (service->categories().isEmpty())
01082                insertService(m_currentMenu, name, service);
01083 
01084          }
01085       }
01086    }
01087    markUsedApplications(items);
01088    m_legacyLoaded = true;
01089 }
01090 
01091 void
01092 VFolderMenu::processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix)
01093 {
01094 kDebug(7021).nospace() << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")";
01095 
01096    QHash<QString,KService::Ptr> items;
01097    // We look for a set of files.
01098    DIR *dp = opendir( QFile::encodeName(dir));
01099    if (!dp)
01100       return;
01101 
01102    KDE_struct_dirent *ep;
01103 
01104    while( ( ep = KDE_readdir( dp ) ) != 0L )
01105    {
01106       QString fn( QFile::decodeName(ep->d_name));
01107       if (fn == "." || fn == ".." || fn.at(fn.length() - 1) == '~')
01108          continue;
01109 
01110       bool isDir;
01111       bool isReg;
01112       QString pathfn = dir + fn;
01113 
01114 #ifdef HAVE_DIRENT_D_TYPE
01115       isDir = ep->d_type == DT_DIR;
01116       isReg = ep->d_type == DT_REG;
01117 
01118       if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
01119 #endif
01120       {
01121     KDE_struct_stat buff;
01122         if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
01123            continue; // Couldn't stat (e.g. no read permissions)
01124         }
01125         isDir = S_ISDIR ( buff.st_mode );
01126         isReg = S_ISREG ( buff.st_mode );
01127       }
01128       if ( isDir ) {
01129          SubMenu *parentMenu = m_currentMenu;
01130 
01131          m_currentMenu = new SubMenu;
01132          m_currentMenu->name = fn;
01133          m_currentMenu->directoryFile = dir + fn + "/.directory";
01134 
01135          parentMenu->subMenus.append(m_currentMenu);
01136 
01137          processLegacyDir(pathfn + '/', relDir+fn+'/', prefix);
01138          m_currentMenu = parentMenu;
01139          continue;
01140       }
01141 
01142       if ( isReg )
01143       {
01144          if (!fn.endsWith(".desktop"))
01145             continue;
01146 
01147          KService::Ptr service;
01148          emit newService(pathfn, &service);
01149          if (service)
01150          {
01151             QString id = prefix+fn;
01152 
01153             // TODO: Add legacy category
01154             addApplication(id, service);
01155             items.insert(service->menuId(), service);
01156 
01157             if (service->categories().isEmpty())
01158                m_currentMenu->items.insert(id, service);
01159          }
01160       }
01161     }
01162     closedir( dp );
01163     markUsedApplications(items);
01164 }
01165 
01166 
01167 
01168 void
01169 VFolderMenu::processMenu(QDomElement &docElem, int pass)
01170 {
01171    SubMenu *parentMenu = m_currentMenu;
01172    int oldDirectoryDirsCount = m_directoryDirs.count();
01173 
01174    QString name;
01175    QString directoryFile;
01176    bool onlyUnallocated = false;
01177    bool isDeleted = false;
01178    bool kdeLegacyDirsDone = false;
01179    QDomElement defaultLayoutNode;
01180    QDomElement layoutNode;
01181 
01182    QDomElement query;
01183    QDomNode n = docElem.firstChild();
01184    while( !n.isNull() ) {
01185       QDomElement e = n.toElement(); // try to convert the node to an element.
01186       if (e.tagName() == "Name")
01187       {
01188          name = e.text();
01189       }
01190       else if (e.tagName() == "Directory")
01191       {
01192          directoryFile = e.text();
01193       }
01194       else if (e.tagName() == "DirectoryDir")
01195       {
01196          QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01197 
01198          m_directoryDirs.prepend(dir);
01199       }
01200       else if (e.tagName() == "OnlyUnallocated")
01201       {
01202          onlyUnallocated = true;
01203       }
01204       else if (e.tagName() == "NotOnlyUnallocated")
01205       {
01206          onlyUnallocated = false;
01207       }
01208       else if (e.tagName() == "Deleted")
01209       {
01210          isDeleted = true;
01211       }
01212       else if (e.tagName() == "NotDeleted")
01213       {
01214          isDeleted = false;
01215       }
01216       else if (e.tagName() == "DefaultLayout")
01217       {
01218          defaultLayoutNode = e;
01219       }
01220       else if (e.tagName() == "Layout")
01221       {
01222          layoutNode = e;
01223       }
01224       n = n.nextSibling();
01225    }
01226 
01227    // Setup current menu entry
01228    if (pass == 0)
01229    {
01230       m_currentMenu = 0;
01231       // Look up menu
01232       if (parentMenu)
01233       {
01234          foreach (SubMenu *menu, parentMenu->subMenus)
01235          {
01236             if (menu->name == name)
01237             {
01238                m_currentMenu = menu;
01239                break;
01240             }
01241          }
01242       }
01243 
01244       if (!m_currentMenu) // Not found?
01245       {
01246          // Create menu
01247          m_currentMenu = new SubMenu;
01248          m_currentMenu->name = name;
01249 
01250          if (parentMenu)
01251             parentMenu->subMenus.append(m_currentMenu);
01252          else
01253             m_rootMenu = m_currentMenu;
01254       }
01255       if (directoryFile.isEmpty())
01256       {
01257          kDebug(7021) << "Menu" << name << "does not specify a directory file.";
01258       }
01259 
01260       // Override previous directoryFile iff available
01261       QString tmp = locateDirectoryFile(directoryFile);
01262       if (! tmp.isEmpty())
01263          m_currentMenu->directoryFile = tmp;
01264       m_currentMenu->isDeleted = isDeleted;
01265 
01266       m_currentMenu->defaultLayoutNode = defaultLayoutNode;
01267       m_currentMenu->layoutNode = layoutNode;
01268    }
01269    else
01270    {
01271       // Look up menu
01272       if (parentMenu)
01273       {
01274          foreach (SubMenu *menu, parentMenu->subMenus)
01275          {
01276             if (menu->name == name)
01277             {
01278                m_currentMenu = menu;
01279                break;
01280             }
01281          }
01282       }
01283       else
01284       {
01285          m_currentMenu = m_rootMenu;
01286       }
01287    }
01288 
01289    // Process AppDir and LegacyDir
01290    if (pass == 0)
01291    {
01292       QDomElement query;
01293       QDomNode n = docElem.firstChild();
01294       while( !n.isNull() ) {
01295          QDomElement e = n.toElement(); // try to convert the node to an element.
01296          if (e.tagName() == "AppDir")
01297          {
01298             createAppsInfo();
01299             QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01300 
01301             registerDirectory(dir);
01302 
01303             loadApplications(dir, QString());
01304          }
01305          else if (e.tagName() == "KDELegacyDirs")
01306          {
01307             createAppsInfo();
01308             if (!kdeLegacyDirsDone)
01309             {
01310 kDebug(7021) << "Processing KDE Legacy dirs for <KDE>";
01311                SubMenu *oldMenu = m_currentMenu;
01312                m_currentMenu = new SubMenu;
01313 
01314                processKDELegacyDirs();
01315 
01316                m_legacyNodes.insert("<KDE>", m_currentMenu);
01317                m_currentMenu = oldMenu;
01318 
01319                kdeLegacyDirsDone = true;
01320             }
01321          }
01322          else if (e.tagName() == "LegacyDir")
01323          {
01324             createAppsInfo();
01325             QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01326 
01327             QString prefix = e.attributes().namedItem("prefix").toAttr().value();
01328 
01329             if (m_defaultLegacyDirs.contains(dir))
01330             {
01331                if (!kdeLegacyDirsDone)
01332                {
01333 kDebug(7021) << "Processing KDE Legacy dirs for" << dir;
01334                   SubMenu *oldMenu = m_currentMenu;
01335                   m_currentMenu = new SubMenu;
01336 
01337                   processKDELegacyDirs();
01338 
01339                   m_legacyNodes.insert("<KDE>", m_currentMenu);
01340                   m_currentMenu = oldMenu;
01341 
01342                   kdeLegacyDirsDone = true;
01343                }
01344             }
01345             else
01346             {
01347                SubMenu *oldMenu = m_currentMenu;
01348                m_currentMenu = new SubMenu;
01349 
01350                registerDirectory(dir);
01351 
01352                processLegacyDir(dir, QString(), prefix);
01353 
01354                m_legacyNodes.insert(dir, m_currentMenu);
01355                m_currentMenu = oldMenu;
01356             }
01357          }
01358          n = n.nextSibling();
01359       }
01360    }
01361 
01362    loadAppsInfo(); // Update the scope wrt the list of applications
01363 
01364    if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
01365    {
01366       n = docElem.firstChild();
01367 
01368       while( !n.isNull() ) {
01369          QDomElement e = n.toElement(); // try to convert the node to an element.
01370          if (e.tagName() == "Include")
01371          {
01372             QHash<QString,KService::Ptr> items;
01373 
01374             QDomNode n2 = e.firstChild();
01375             while( !n2.isNull() ) {
01376                QDomElement e2 = n2.toElement();
01377                items.clear();
01378                processCondition(e2, items);
01379                if (m_track)
01380                   track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Include>");
01381                includeItems(m_currentMenu->items, items);
01382                excludeItems(m_currentMenu->excludeItems, items);
01383                markUsedApplications(items);
01384 
01385                if (m_track)
01386                   track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Include>");
01387 
01388                n2 = n2.nextSibling();
01389             }
01390          }
01391 
01392          else if (e.tagName() == "Exclude")
01393          {
01394             QHash<QString,KService::Ptr> items;
01395 
01396             QDomNode n2 = e.firstChild();
01397             while( !n2.isNull() ) {
01398                QDomElement e2 = n2.toElement();
01399                items.clear();
01400                processCondition(e2, items);
01401                if (m_track)
01402                   track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Exclude>");
01403                excludeItems(m_currentMenu->items, items);
01404                includeItems(m_currentMenu->excludeItems, items);
01405                if (m_track)
01406                   track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Exclude>");
01407                n2 = n2.nextSibling();
01408             }
01409          }
01410 
01411          n = n.nextSibling();
01412       }
01413    }
01414 
01415    n = docElem.firstChild();
01416    while( !n.isNull() ) {
01417       QDomElement e = n.toElement(); // try to convert the node to an element.
01418       if (e.tagName() == "Menu")
01419       {
01420          processMenu(e, pass);
01421       }
01422 // We insert legacy dir in pass 0, this way the order in the .menu-file determines
01423 // which .directory file gets used, but the menu-entries of legacy-menus will always
01424 // have the lowest priority.
01425 //      else if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
01426       else if (pass == 0)
01427       {
01428          if (e.tagName() == "LegacyDir")
01429          {
01430             // Add legacy nodes to Menu structure
01431             QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01432             SubMenu *legacyMenu = m_legacyNodes[dir];
01433             if (legacyMenu)
01434             {
01435                mergeMenu(m_currentMenu, legacyMenu);
01436             }
01437          }
01438 
01439          else if (e.tagName() == "KDELegacyDirs")
01440          {
01441             // Add legacy nodes to Menu structure
01442             QString dir = "<KDE>";
01443             SubMenu *legacyMenu = m_legacyNodes[dir];
01444             if (legacyMenu)
01445             {
01446                mergeMenu(m_currentMenu, legacyMenu);
01447             }
01448          }
01449       }
01450       n = n.nextSibling();
01451    }
01452 
01453    if (pass == 2)
01454    {
01455       n = docElem.firstChild();
01456       while( !n.isNull() ) {
01457          QDomElement e = n.toElement(); // try to convert the node to an element.
01458          if (e.tagName() == "Move")
01459          {
01460             QString orig;
01461             QString dest;
01462             QDomNode n2 = e.firstChild();
01463             while( !n2.isNull() ) {
01464                QDomElement e2 = n2.toElement(); // try to convert the node to an element.
01465                if( e2.tagName() == "Old")
01466                   orig = e2.text();
01467                if( e2.tagName() == "New")
01468                   dest = e2.text();
01469                n2 = n2.nextSibling();
01470             }
01471             kDebug(7021) << "Moving" << orig << "to" << dest;
01472             if (!orig.isEmpty() && !dest.isEmpty())
01473             {
01474               SubMenu *menu = takeSubMenu(m_currentMenu, orig);
01475               if (menu)
01476               {
01477                 insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority
01478               }
01479             }
01480          }
01481          n = n.nextSibling();
01482       }
01483 
01484    }
01485 
01486    unloadAppsInfo(); // Update the scope wrt the list of applications
01487 
01488    while (m_directoryDirs.count() > oldDirectoryDirsCount)
01489       m_directoryDirs.pop_front();
01490 
01491    m_currentMenu = parentMenu;
01492 }
01493 
01494 
01495 
01496 static QString parseAttribute( const QDomElement &e)
01497 {
01498     QString option;
01499     if ( e.hasAttribute( "show_empty" ) )
01500     {
01501         QString str = e.attribute( "show_empty" );
01502         if ( str=="true" )
01503             option= "ME ";
01504         else if ( str=="false" )
01505             option= "NME ";
01506         else
01507             kDebug()<<" Error in parsing show_empty attribute :"<<str;
01508     }
01509     if ( e.hasAttribute( "inline" ) )
01510     {
01511         QString str = e.attribute( "inline" );
01512         if (  str=="true" )
01513             option+="I ";
01514         else if ( str=="false" )
01515             option+="NI ";
01516         else
01517             kDebug()<<" Error in parsing inlibe attribute :"<<str;
01518     }
01519     if ( e.hasAttribute( "inline_limit" ) )
01520     {
01521         bool ok;
01522         int value = e.attribute( "inline_limit" ).toInt(&ok);
01523         if ( ok )
01524             option+=QString( "IL[%1] " ).arg( value );
01525     }
01526     if ( e.hasAttribute( "inline_header" ) )
01527     {
01528         QString str = e.attribute( "inline_header" );
01529         if ( str=="true")
01530             option+="IH ";
01531         else if ( str == "false" )
01532             option+="NIH ";
01533         else
01534             kDebug()<<" Error in parsing of inline_header attribute :"<<str;
01535 
01536     }
01537     if ( e.hasAttribute( "inline_alias" ) && e.attribute( "inline_alias" )=="true")
01538     {
01539         QString str = e.attribute( "inline_alias" );
01540         if ( str=="true" )
01541             option+="IA";
01542         else if ( str=="false" )
01543             option+="NIA";
01544         else
01545             kDebug()<<" Error in parsing inline_alias attribute :"<<str;
01546     }
01547     if( !option.isEmpty())
01548     {
01549         option = option.prepend(":O");
01550     }
01551     return option;
01552 
01553 }
01554 
01555 static QStringList parseLayoutNode(const QDomElement &docElem)
01556 {
01557    QStringList layout;
01558 
01559    QString optionDefaultLayout;
01560    if( docElem.tagName()=="DefaultLayout")
01561        optionDefaultLayout =  parseAttribute( docElem);
01562    if ( !optionDefaultLayout.isEmpty() )
01563        layout.append( optionDefaultLayout );
01564 
01565    QDomNode n = docElem.firstChild();
01566    while( !n.isNull() ) {
01567       QDomElement e = n.toElement(); // try to convert the node to an element.
01568       if (e.tagName() == "Separator")
01569       {
01570          layout.append(":S");
01571       }
01572       else if (e.tagName() == "Filename")
01573       {
01574          layout.append(e.text());
01575       }
01576       else if (e.tagName() == "Menuname")
01577       {
01578          layout.append('/'+e.text());
01579          QString option = parseAttribute( e );
01580          if( !option.isEmpty())
01581              layout.append( option );
01582       }
01583       else if (e.tagName() == "Merge")
01584       {
01585          QString type = e.attributeNode("type").value();
01586          if (type == "files")
01587             layout.append(":F");
01588          else if (type == "menus")
01589             layout.append(":M");
01590          else if (type == "all")
01591             layout.append(":A");
01592       }
01593 
01594       n = n.nextSibling();
01595    }
01596    return layout;
01597 }
01598 
01599 void
01600 VFolderMenu::layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout) //krazy:exclude=passbyvalue
01601 {
01602    if (!menu->defaultLayoutNode.isNull())
01603    {
01604       defaultLayout = parseLayoutNode(menu->defaultLayoutNode);
01605    }
01606 
01607    if (menu->layoutNode.isNull())
01608    {
01609      menu->layoutList = defaultLayout;
01610    }
01611    else
01612    {
01613      menu->layoutList = parseLayoutNode(menu->layoutNode);
01614      if (menu->layoutList.isEmpty())
01615         menu->layoutList = defaultLayout;
01616    }
01617 
01618    foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus)
01619    {
01620       layoutMenu(subMenu, defaultLayout);
01621    }
01622 }
01623 
01624 void
01625 VFolderMenu::markUsedApplications(const QHash<QString,KService::Ptr>& items)
01626 {
01627    foreach(const KService::Ptr &p, items)
01628       m_usedAppsDict.insert(p->menuId());
01629 }
01630 
01631 VFolderMenu::SubMenu *
01632 VFolderMenu::parseMenu(const QString &file, bool forceLegacyLoad)
01633 {
01634    m_forcedLegacyLoad = false;
01635    m_legacyLoaded = false;
01636    m_appsInfo = 0;
01637 
01638    const QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu");
01639    for(QStringList::ConstIterator it=dirs.begin();
01640        it != dirs.end(); ++it)
01641    {
01642       registerDirectory(*it);
01643    }
01644 
01645    loadMenu(file);
01646 
01647    delete m_rootMenu;
01648    m_rootMenu = m_currentMenu = 0;
01649 
01650    QDomElement docElem = m_doc.documentElement();
01651 
01652    for (int pass = 0; pass <= 2; pass++)
01653    {
01654        // pass 0: load application desktop files
01655        // pass 1: the normal processing
01656        // pass 2: process <OnlyUnallocated> to put unused files into "Lost & Found".
01657       processMenu(docElem, pass);
01658 
01659       switch (pass) {
01660       case 0:
01661           // Fill the dictCategories for each AppsInfo in m_appsInfoList,
01662           // in preparation for processMenu pass 1.
01663           buildApplicationIndex(false);
01664           break;
01665       case 1:
01666           // Fill the dictCategories for each AppsInfo in m_appsInfoList,
01667           // with only the unused apps, in preparation for processMenu pass 2.
01668           buildApplicationIndex(true /* unusedOnly */);
01669           break;
01670       case 2:
01671       {
01672          QStringList defaultLayout;
01673          defaultLayout << ":M"; // Sub-Menus
01674          defaultLayout << ":F"; // Individual entries
01675          layoutMenu(m_rootMenu, defaultLayout);
01676          break;
01677       }
01678       default:
01679           break;
01680       }
01681    }
01682 
01683    if (!m_legacyLoaded && forceLegacyLoad)
01684    {
01685       m_forcedLegacyLoad = true;
01686       processKDELegacyDirs();
01687    }
01688 
01689    return m_rootMenu;
01690 }
01691 
01692 void
01693 VFolderMenu::setTrackId(const QString &id)
01694 {
01695    m_track = !id.isEmpty();
01696    m_trackId = id;
01697 }
01698 
01699 #include "vfolder_menu.moc"

KDED

Skip menu "KDED"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal