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