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